blob: 5bbb7fdcec7cc07b02ef9225f82f70712b4d7b0c [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
sewardj7bd6ffe2005-08-03 16:07:36 +000013 Copyright (C) 2004-2005 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
cerionedf7fc52005-11-18 20:57:41 +000049 Spot rlwimi cases which are simply left/right shifts and
sewardje14bb9f2005-07-22 09:39:02 +000050 emit Shl32/Shr32 accordingly.
51
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
cerion729edb72005-12-02 16:03:46 +000057 Floating Point
58 - Single precision stores are rounded twice - once by F64toF32,
59 and then again by the backend for storeBE( F32 ), giving a loss
60 of precision.
61
62
sewardj87e651f2005-09-09 08:31:18 +000063 LIMITATIONS:
64
65 Various, including:
66
67 - Some invalid forms of lswi and lswx are accepted when they should
68 not be.
69
cerionedf7fc52005-11-18 20:57:41 +000070 - Floating Point:
71 - All exceptions disabled in FPSCR
72 - condition codes not set in FPSCR
73 - some error in accuracy
cerion76de5cf2005-11-18 18:25:12 +000074
cerionedf7fc52005-11-18 20:57:41 +000075 - Altivec floating point:
76 - vmaddfp, vnmsubfp
77 Because we're using Java/IEEE mode (FPSCR[NJ]), rather than the
78 system default of Non-Java mode, we get some small errors
79 (lowest bit only).
80 This is because Non-Java mode brutally hacks denormalised results
81 to zero, whereas we keep maximum accuracy. However, using
82 Non-Java mode would give us more inaccuracy, as our intermediate
83 results would then be zeroed, too.
sewardjb51f0f42005-07-18 11:38:02 +000084*/
85
86
cerion5b2325f2005-12-23 00:55:09 +000087/* Translates PPC32/64 code to IR. */
cerion896a1372005-01-25 12:24:25 +000088
cerion645c9302005-01-31 10:09:59 +000089/* References
ceriona982c052005-06-28 17:23:09 +000090
91#define PPC32
cerion645c9302005-01-31 10:09:59 +000092 "PowerPC Microprocessor Family:
ceriond953ebb2005-11-29 13:27:20 +000093 The Programming Environments Manual for 32-Bit Microprocessors"
cerione9d361a2005-03-04 17:35:29 +000094 02/21/2000
95 http://www-3.ibm.com/chips/techlib/techlib.nsf/techdocs/852569B20050FF778525699600719DF2
96
ceriond953ebb2005-11-29 13:27:20 +000097#define PPC64
98 "PowerPC Microprocessor Family:
99 Programming Environments Manual for 64-Bit Microprocessors"
100 06/10/2003
101 http://www-3.ibm.com/chips/techlib/techlib.nsf/techdocs/F7E732FF811F783187256FDD004D3797
102
ceriona982c052005-06-28 17:23:09 +0000103#define AV
104 "PowerPC Microprocessor Family:
105 AltiVec(TM) Technology Programming Environments Manual"
106 07/10/2003
107 http://www-3.ibm.com/chips/techlib/techlib.nsf/techdocs/FBFA164F824370F987256D6A006F424D
cerion645c9302005-01-31 10:09:59 +0000108*/
109
cerion896a1372005-01-25 12:24:25 +0000110#include "libvex_basictypes.h"
111#include "libvex_ir.h"
112#include "libvex.h"
cerion1515db92005-01-25 17:21:23 +0000113#include "libvex_guest_ppc32.h"
ceriond953ebb2005-11-29 13:27:20 +0000114#include "libvex_guest_ppc64.h"
cerion896a1372005-01-25 12:24:25 +0000115
116#include "main/vex_util.h"
117#include "main/vex_globals.h"
sewardj9e6491a2005-07-02 19:24:10 +0000118#include "guest-generic/bb_to_IR.h"
ceriond0eae2d2005-12-23 11:43:01 +0000119#include "guest-ppc/gdefs.h"
cerion896a1372005-01-25 12:24:25 +0000120
121
122/*------------------------------------------------------------*/
123/*--- Globals ---*/
124/*------------------------------------------------------------*/
125
sewardj9e6491a2005-07-02 19:24:10 +0000126/* These are set at the start of the translation of an insn, right
cerion5b2325f2005-12-23 00:55:09 +0000127 down in disInstr_PPC, so that we don't have to pass them around
sewardj9e6491a2005-07-02 19:24:10 +0000128 endlessly. They are all constant during the translation of any
129 given insn. */
cerion896a1372005-01-25 12:24:25 +0000130
cerioned623db2005-06-20 12:42:04 +0000131/* We need to know this to do sub-register accesses correctly. */
cerioned623db2005-06-20 12:42:04 +0000132static Bool host_is_bigendian;
133
cerion896a1372005-01-25 12:24:25 +0000134/* Pointer to the guest code area. */
cerion896a1372005-01-25 12:24:25 +0000135static UChar* guest_code;
136
137/* The guest address corresponding to guest_code[0]. */
ceriond953ebb2005-11-29 13:27:20 +0000138static Addr64 guest_CIA_bbstart;
cerion896a1372005-01-25 12:24:25 +0000139
sewardj01a9e802005-02-01 20:46:00 +0000140/* The guest address for the instruction currently being
141 translated. */
ceriond953ebb2005-11-29 13:27:20 +0000142static Addr64 guest_CIA_curr_instr;
sewardj01a9e802005-02-01 20:46:00 +0000143
cerion896a1372005-01-25 12:24:25 +0000144/* The IRBB* into which we're generating code. */
145static IRBB* irbb;
146
sewardj5df65bb2005-11-29 14:47:04 +0000147/* Is our guest binary 32 or 64bit? Set at each call to
cerion5b2325f2005-12-23 00:55:09 +0000148 disInstr_PPC below. */
sewardj5df65bb2005-11-29 14:47:04 +0000149static Bool mode64 = False;
ceriond953ebb2005-11-29 13:27:20 +0000150
cerion896a1372005-01-25 12:24:25 +0000151
152/*------------------------------------------------------------*/
153/*--- Debugging output ---*/
154/*------------------------------------------------------------*/
155
156#define DIP(format, args...) \
157 if (vex_traceflags & VEX_TRACE_FE) \
158 vex_printf(format, ## args)
159
160#define DIS(buf, format, args...) \
161 if (vex_traceflags & VEX_TRACE_FE) \
162 vex_sprintf(buf, format, ## args)
163
164
cerion896a1372005-01-25 12:24:25 +0000165/*------------------------------------------------------------*/
ceriond953ebb2005-11-29 13:27:20 +0000166/*--- Offsets of various parts of the ppc32/64 guest state ---*/
cerion896a1372005-01-25 12:24:25 +0000167/*------------------------------------------------------------*/
168
cerion5b2325f2005-12-23 00:55:09 +0000169#define offsetofPPCGuestState(_x) \
170 (mode64 ? offsetof(VexGuestPPC64State, _x) : \
171 offsetof(VexGuestPPC32State, _x))
cerion91ad5362005-01-27 23:02:41 +0000172
cerion5b2325f2005-12-23 00:55:09 +0000173#define OFFB_CIA offsetofPPCGuestState(guest_CIA)
174#define OFFB_LR offsetofPPCGuestState(guest_LR)
175#define OFFB_CTR offsetofPPCGuestState(guest_CTR)
176#define OFFB_XER_SO offsetofPPCGuestState(guest_XER_SO)
177#define OFFB_XER_OV offsetofPPCGuestState(guest_XER_OV)
178#define OFFB_XER_CA offsetofPPCGuestState(guest_XER_CA)
179#define OFFB_XER_BC offsetofPPCGuestState(guest_XER_BC)
180#define OFFB_FPROUND offsetofPPCGuestState(guest_FPROUND)
181#define OFFB_VRSAVE offsetofPPCGuestState(guest_VRSAVE)
182#define OFFB_VSCR offsetofPPCGuestState(guest_VSCR)
183#define OFFB_EMWARN offsetofPPCGuestState(guest_EMWARN)
184#define OFFB_TISTART offsetofPPCGuestState(guest_TISTART)
185#define OFFB_TILEN offsetofPPCGuestState(guest_TILEN)
186#define OFFB_RESVN offsetofPPCGuestState(guest_RESVN)
cerion91ad5362005-01-27 23:02:41 +0000187
sewardj7787af42005-08-04 18:32:19 +0000188
cerion91ad5362005-01-27 23:02:41 +0000189
cerion38674602005-02-08 02:19:25 +0000190/*------------------------------------------------------------*/
sewardjb51f0f42005-07-18 11:38:02 +0000191/*--- Extract instruction fields --- */
cerion38674602005-02-08 02:19:25 +0000192/*------------------------------------------------------------*/
cerione9d361a2005-03-04 17:35:29 +0000193
cerion76de5cf2005-11-18 18:25:12 +0000194/* Extract field from insn, given idx (zero = lsb) and field length */
195#define IFIELD( insn, idx, len ) ((insn >> idx) & ((1<<len)-1))
196
sewardjb51f0f42005-07-18 11:38:02 +0000197/* Extract primary opcode, instr[31:26] */
cerion76de5cf2005-11-18 18:25:12 +0000198static UChar ifieldOPC( UInt instr ) {
199 return toUChar( IFIELD( instr, 26, 6 ) );
sewardjb51f0f42005-07-18 11:38:02 +0000200}
cerione9d361a2005-03-04 17:35:29 +0000201
cerion76de5cf2005-11-18 18:25:12 +0000202/* Extract 10-bit secondary opcode, instr[10:1] */
sewardjb51f0f42005-07-18 11:38:02 +0000203static UInt ifieldOPClo10 ( UInt instr) {
cerion76de5cf2005-11-18 18:25:12 +0000204 return IFIELD( instr, 1, 10 );
205}
206
207/* Extract 9-bit secondary opcode, instr[9:1] */
208static UInt ifieldOPClo9 ( UInt instr) {
209 return IFIELD( instr, 1, 9 );
210}
211
212/* Extract 5-bit secondary opcode, instr[5:1] */
213static UInt ifieldOPClo5 ( UInt instr) {
214 return IFIELD( instr, 1, 5 );
sewardjb51f0f42005-07-18 11:38:02 +0000215}
cerione9d361a2005-03-04 17:35:29 +0000216
sewardjb51f0f42005-07-18 11:38:02 +0000217/* Extract RD (destination register) field, instr[25:21] */
cerion76de5cf2005-11-18 18:25:12 +0000218static UChar ifieldRegDS( UInt instr ) {
219 return toUChar( IFIELD( instr, 21, 5 ) );
sewardjb51f0f42005-07-18 11:38:02 +0000220}
cerion094d1392005-06-20 13:45:57 +0000221
cerion76de5cf2005-11-18 18:25:12 +0000222/* Extract RA (1st source register) field, instr[20:16] */
223static UChar ifieldRegA ( UInt instr ) {
224 return toUChar( IFIELD( instr, 16, 5 ) );
sewardjb51f0f42005-07-18 11:38:02 +0000225}
226
cerion76de5cf2005-11-18 18:25:12 +0000227/* Extract RB (2nd source register) field, instr[15:11] */
228static UChar ifieldRegB ( UInt instr ) {
229 return toUChar( IFIELD( instr, 11, 5 ) );
sewardjb51f0f42005-07-18 11:38:02 +0000230}
231
cerion76de5cf2005-11-18 18:25:12 +0000232/* Extract RC (3rd source register) field, instr[10:6] */
233static UChar ifieldRegC ( UInt instr ) {
234 return toUChar( IFIELD( instr, 6, 5 ) );
sewardjb51f0f42005-07-18 11:38:02 +0000235}
236
cerion76de5cf2005-11-18 18:25:12 +0000237/* Extract 2nd lowest bit, instr[1] */
238static UChar ifieldBIT10 ( UInt instr ) {
239 return toUChar( IFIELD( instr, 10, 1 ) );
240}
241
242/* Extract 2nd lowest bit, instr[1] */
243static UChar ifieldBIT1 ( UInt instr ) {
244 return toUChar( IFIELD( instr, 1, 1 ) );
245}
246
247/* Extract lowest bit, instr[0] */
248static UChar ifieldBIT0 ( UInt instr ) {
249 return toUChar( instr & 0x1 );
250}
251
252/* Extract unsigned bottom half, instr[15:0] */
253static UInt ifieldUIMM16 ( UInt instr ) {
254 return instr & 0xFFFF;
255}
256
ceriond953ebb2005-11-29 13:27:20 +0000257/* Extract unsigned bottom 26 bits, instr[25:0] */
258static UInt ifieldUIMM26 ( UInt instr ) {
259 return instr & 0x3FFFFFF;
cerion76de5cf2005-11-18 18:25:12 +0000260}
261
sewardjb51f0f42005-07-18 11:38:02 +0000262
cerionedf7fc52005-11-18 20:57:41 +0000263/*------------------------------------------------------------*/
ceriond953ebb2005-11-29 13:27:20 +0000264/*--- Guest-state identifiers ---*/
cerionedf7fc52005-11-18 20:57:41 +0000265/*------------------------------------------------------------*/
sewardje14bb9f2005-07-22 09:39:02 +0000266
cerione9d361a2005-03-04 17:35:29 +0000267typedef enum {
ceriond953ebb2005-11-29 13:27:20 +0000268 PPC_GST_CIA, // Current Instruction Address
269 PPC_GST_LR, // Link Register
270 PPC_GST_CTR, // Count Register
271 PPC_GST_XER, // Overflow, carry flags, byte count
272 PPC_GST_CR, // Condition Register
273 PPC_GST_FPSCR, // Floating Point Status/Control Register
274 PPC_GST_VRSAVE, // Vector Save/Restore Register
275 PPC_GST_VSCR, // Vector Status and Control Register
276 PPC_GST_EMWARN, // Emulation warnings
277 PPC_GST_TISTART,// For icbi: start of area to invalidate
278 PPC_GST_TILEN, // For icbi: length of area to invalidate
279 PPC_GST_RESVN, // For lwarx/stwcx.
280 PPC_GST_MAX
281} PPC_GST;
cerione9d361a2005-03-04 17:35:29 +0000282
cerionedf7fc52005-11-18 20:57:41 +0000283#define MASK_FPSCR_RN 0x3
284#define MASK_VSCR_VALID 0x00010001
sewardje14bb9f2005-07-22 09:39:02 +0000285
cerionedf7fc52005-11-18 20:57:41 +0000286
287/*------------------------------------------------------------*/
288/*--- FP Helpers ---*/
289/*------------------------------------------------------------*/
290
sewardj2ead5222005-11-23 03:53:45 +0000291/* Produce the 32-bit pattern corresponding to the supplied
292 float. */
293static UInt float_to_bits ( Float f )
294{
295 union { UInt i; Float f; } u;
296 vassert(4 == sizeof(UInt));
297 vassert(4 == sizeof(Float));
298 vassert(4 == sizeof(u));
299 u.f = f;
300 return u.i;
301}
302
cerion38674602005-02-08 02:19:25 +0000303
cerion38674602005-02-08 02:19:25 +0000304/*------------------------------------------------------------*/
305/*--- Misc Helpers ---*/
306/*------------------------------------------------------------*/
307
cerionf0de28c2005-12-13 20:21:11 +0000308/* Generate mask with 1's from 'begin' through 'end',
309 wrapping if begin > end.
310 begin->end works from right to left, 0=lsb
311*/
ceriond953ebb2005-11-29 13:27:20 +0000312static UInt MASK32( UInt begin, UInt end )
cerion38674602005-02-08 02:19:25 +0000313{
ceriond953ebb2005-11-29 13:27:20 +0000314 vassert(begin < 32);
315 vassert(end < 32);
cerionb85e8bb2005-02-16 08:54:33 +0000316 UInt m1 = ((UInt)(-1)) << begin;
317 UInt m2 = ((UInt)(-1)) << (end + 1);
318 UInt mask = m1 ^ m2;
319 if (begin > end) mask = ~mask; // wrap mask
320 return mask;
cerion38674602005-02-08 02:19:25 +0000321}
322
cerion5b2325f2005-12-23 00:55:09 +0000323/* ditto for 64bit mask */
ceriond953ebb2005-11-29 13:27:20 +0000324static ULong MASK64( UInt begin, UInt end )
325{
326 vassert(begin < 64);
327 vassert(end < 64);
328 ULong m1 = ((ULong)(-1)) << begin;
329 ULong m2 = ((ULong)(-1)) << (end + 1);
330 ULong mask = m1 ^ m2;
331 if (begin > end) mask = ~mask; // wrap mask
332 return mask;
333}
334
cerionf0de28c2005-12-13 20:21:11 +0000335static Addr64 nextInsnAddr( void )
336{
337 return guest_CIA_curr_instr + 4;
338}
ceriond953ebb2005-11-29 13:27:20 +0000339
cerion896a1372005-01-25 12:24:25 +0000340
cerion896a1372005-01-25 12:24:25 +0000341/*------------------------------------------------------------*/
342/*--- Helper bits and pieces for deconstructing the ---*/
ceriond953ebb2005-11-29 13:27:20 +0000343/*--- ppc32/64 insn stream. ---*/
cerion896a1372005-01-25 12:24:25 +0000344/*------------------------------------------------------------*/
345
346/* Add a statement to the list held by "irbb". */
347static void stmt ( IRStmt* st )
348{
349 addStmtToIRBB( irbb, st );
350}
351
cerion896a1372005-01-25 12:24:25 +0000352/* Generate a new temporary of the given type. */
353static IRTemp newTemp ( IRType ty )
354{
sewardj496a58d2005-03-20 18:44:44 +0000355 vassert(isPlausibleIRType(ty));
cerion896a1372005-01-25 12:24:25 +0000356 return newIRTemp( irbb->tyenv, ty );
357}
cerion896a1372005-01-25 12:24:25 +0000358
cerion32aad402005-09-10 12:02:24 +0000359/* Various simple conversions */
360
361static UChar extend_s_5to8 ( UChar x )
362{
363 return toUChar((((Int)x) << 27) >> 27);
364}
365
cerion92d9d872005-09-15 21:58:50 +0000366static UInt extend_s_8to32( UChar x )
367{
368 return (UInt)((((Int)x) << 24) >> 24);
369}
cerion91ad5362005-01-27 23:02:41 +0000370
cerion896a1372005-01-25 12:24:25 +0000371static UInt extend_s_16to32 ( UInt x )
372{
373 return (UInt)((((Int)x) << 16) >> 16);
374}
cerion896a1372005-01-25 12:24:25 +0000375
ceriond953ebb2005-11-29 13:27:20 +0000376static ULong extend_s_16to64 ( UInt x )
377{
378 return (ULong)((((Long)x) << 48) >> 48);
379}
380
381static ULong extend_s_26to64 ( UInt x )
382{
383 return (ULong)((((Long)x) << 38) >> 38);
384}
385
386static ULong extend_s_32to64 ( UInt x )
387{
388 return (ULong)((((Long)x) << 32) >> 32);
389}
390
sewardj684aa952005-01-30 12:52:14 +0000391/* Do a big-endian load of a 32-bit word, regardless of the endianness
392 of the underlying host. */
cerioncf004462005-01-31 15:24:55 +0000393static UInt getUIntBigendianly ( UChar* p )
sewardj684aa952005-01-30 12:52:14 +0000394{
cerioncf004462005-01-31 15:24:55 +0000395 UInt w = 0;
sewardj684aa952005-01-30 12:52:14 +0000396 w = (w << 8) | p[0];
397 w = (w << 8) | p[1];
398 w = (w << 8) | p[2];
399 w = (w << 8) | p[3];
400 return w;
401}
402
cerion896a1372005-01-25 12:24:25 +0000403
404/*------------------------------------------------------------*/
405/*--- Helpers for constructing IR. ---*/
406/*------------------------------------------------------------*/
407
cerion896a1372005-01-25 12:24:25 +0000408static void assign ( IRTemp dst, IRExpr* e )
409{
410 stmt( IRStmt_Tmp(dst, e) );
411}
412
cerionae694622005-01-28 17:52:47 +0000413static void storeBE ( IRExpr* addr, IRExpr* data )
cerion896a1372005-01-25 12:24:25 +0000414{
ceriond953ebb2005-11-29 13:27:20 +0000415 vassert(typeOfIRExpr(irbb->tyenv, addr) == Ity_I32 ||
416 typeOfIRExpr(irbb->tyenv, addr) == Ity_I64);
sewardjaf1ceca2005-06-30 23:31:27 +0000417 stmt( IRStmt_Store(Iend_BE,addr,data) );
cerion896a1372005-01-25 12:24:25 +0000418}
419
420static IRExpr* unop ( IROp op, IRExpr* a )
421{
422 return IRExpr_Unop(op, a);
423}
424
425static IRExpr* binop ( IROp op, IRExpr* a1, IRExpr* a2 )
426{
427 return IRExpr_Binop(op, a1, a2);
428}
429
430static IRExpr* mkexpr ( IRTemp tmp )
431{
432 return IRExpr_Tmp(tmp);
433}
434
sewardj7f080782005-07-27 00:22:15 +0000435//uu static IRExpr* mkU1 ( UInt i )
436//uu {
437//uu vassert(i < 2);
438//uu return IRExpr_Const(IRConst_U1( toBool(i) ));
439//uu }
cerion45552a92005-02-03 18:20:22 +0000440
sewardj684c0372005-02-07 02:33:58 +0000441static IRExpr* mkU8 ( UChar i )
cerion896a1372005-01-25 12:24:25 +0000442{
cerion896a1372005-01-25 12:24:25 +0000443 return IRExpr_Const(IRConst_U8(i));
444}
cerion896a1372005-01-25 12:24:25 +0000445
cerion92d9d872005-09-15 21:58:50 +0000446static IRExpr* mkU16 ( UInt i )
447{
448 return IRExpr_Const(IRConst_U16(i));
449}
450
cerion896a1372005-01-25 12:24:25 +0000451static IRExpr* mkU32 ( UInt i )
452{
453 return IRExpr_Const(IRConst_U32(i));
454}
455
cerion4a49b032005-11-08 16:23:07 +0000456static IRExpr* mkU64 ( ULong i )
457{
458 return IRExpr_Const(IRConst_U64(i));
459}
460
cerionae694622005-01-28 17:52:47 +0000461static IRExpr* loadBE ( IRType ty, IRExpr* data )
cerion896a1372005-01-25 12:24:25 +0000462{
sewardjaf1ceca2005-06-30 23:31:27 +0000463 return IRExpr_Load(Iend_BE,ty,data);
cerion896a1372005-01-25 12:24:25 +0000464}
cerion896a1372005-01-25 12:24:25 +0000465
sewardj20ef5472005-07-21 14:48:31 +0000466static IRExpr* mkOR1 ( IRExpr* arg1, IRExpr* arg2 )
467{
468 vassert(typeOfIRExpr(irbb->tyenv, arg1) == Ity_I1);
469 vassert(typeOfIRExpr(irbb->tyenv, arg2) == Ity_I1);
cerion5b2325f2005-12-23 00:55:09 +0000470 return unop(Iop_32to1, binop(Iop_Or32, unop(Iop_1Uto32, arg1),
471 unop(Iop_1Uto32, arg2)));
sewardj20ef5472005-07-21 14:48:31 +0000472}
473
474static IRExpr* mkAND1 ( IRExpr* arg1, IRExpr* arg2 )
475{
476 vassert(typeOfIRExpr(irbb->tyenv, arg1) == Ity_I1);
477 vassert(typeOfIRExpr(irbb->tyenv, arg2) == Ity_I1);
cerion5b2325f2005-12-23 00:55:09 +0000478 return unop(Iop_32to1, binop(Iop_And32, unop(Iop_1Uto32, arg1),
479 unop(Iop_1Uto32, arg2)));
sewardj20ef5472005-07-21 14:48:31 +0000480}
sewardjb51f0f42005-07-18 11:38:02 +0000481
cerion4a49b032005-11-08 16:23:07 +0000482/* expand V128_8Ux16 to 2x V128_16Ux8's */
cerion5b2325f2005-12-23 00:55:09 +0000483static void expand8Ux16( IRExpr* vIn,
484 /*OUTs*/ IRTemp* vEvn, IRTemp* vOdd )
cerion4a49b032005-11-08 16:23:07 +0000485{
486 IRTemp ones8x16 = newTemp(Ity_V128);
487
488 vassert(typeOfIRExpr(irbb->tyenv, vIn) == Ity_V128);
489 vassert(vEvn && *vEvn == IRTemp_INVALID);
490 vassert(vOdd && *vOdd == IRTemp_INVALID);
491 *vEvn = newTemp(Ity_V128);
492 *vOdd = newTemp(Ity_V128);
493
494 assign( ones8x16, unop(Iop_Dup8x16, mkU8(0x1)) );
cerion24d06f12005-11-09 21:34:20 +0000495 assign( *vOdd, binop(Iop_MullEven8Ux16, mkexpr(ones8x16), vIn) );
496 assign( *vEvn, binop(Iop_MullEven8Ux16, mkexpr(ones8x16),
497 binop(Iop_ShrV128, vIn, mkU8(8))) );
cerion4a49b032005-11-08 16:23:07 +0000498}
499
500/* expand V128_8Sx16 to 2x V128_16Sx8's */
cerion5b2325f2005-12-23 00:55:09 +0000501static void expand8Sx16( IRExpr* vIn,
502 /*OUTs*/ IRTemp* vEvn, IRTemp* vOdd )
cerion4a49b032005-11-08 16:23:07 +0000503{
504 IRTemp ones8x16 = newTemp(Ity_V128);
505
506 vassert(typeOfIRExpr(irbb->tyenv, vIn) == Ity_V128);
507 vassert(vEvn && *vEvn == IRTemp_INVALID);
508 vassert(vOdd && *vOdd == IRTemp_INVALID);
509 *vEvn = newTemp(Ity_V128);
510 *vOdd = newTemp(Ity_V128);
511
512 assign( ones8x16, unop(Iop_Dup8x16, mkU8(0x1)) );
cerion24d06f12005-11-09 21:34:20 +0000513 assign( *vOdd, binop(Iop_MullEven8Sx16, mkexpr(ones8x16), vIn) );
514 assign( *vEvn, binop(Iop_MullEven8Sx16, mkexpr(ones8x16),
515 binop(Iop_ShrV128, vIn, mkU8(8))) );
cerion4a49b032005-11-08 16:23:07 +0000516}
517
518/* expand V128_16Uto8 to 2x V128_32Ux4's */
cerion5b2325f2005-12-23 00:55:09 +0000519static void expand16Ux8( IRExpr* vIn,
520 /*OUTs*/ IRTemp* vEvn, IRTemp* vOdd )
cerion4a49b032005-11-08 16:23:07 +0000521{
522 IRTemp ones16x8 = newTemp(Ity_V128);
523
524 vassert(typeOfIRExpr(irbb->tyenv, vIn) == Ity_V128);
525 vassert(vEvn && *vEvn == IRTemp_INVALID);
526 vassert(vOdd && *vOdd == IRTemp_INVALID);
527 *vEvn = newTemp(Ity_V128);
528 *vOdd = newTemp(Ity_V128);
529
530 assign( ones16x8, unop(Iop_Dup16x8, mkU16(0x1)) );
cerion24d06f12005-11-09 21:34:20 +0000531 assign( *vOdd, binop(Iop_MullEven16Ux8, mkexpr(ones16x8), vIn) );
532 assign( *vEvn, binop(Iop_MullEven16Ux8, mkexpr(ones16x8),
533 binop(Iop_ShrV128, vIn, mkU8(16))) );
cerion4a49b032005-11-08 16:23:07 +0000534}
535
536/* expand V128_16Sto8 to 2x V128_32Sx4's */
cerion5b2325f2005-12-23 00:55:09 +0000537static void expand16Sx8( IRExpr* vIn,
538 /*OUTs*/ IRTemp* vEvn, IRTemp* vOdd )
cerion4a49b032005-11-08 16:23:07 +0000539{
540 IRTemp ones16x8 = 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( ones16x8, unop(Iop_Dup16x8, mkU16(0x1)) );
cerion24d06f12005-11-09 21:34:20 +0000549 assign( *vOdd, binop(Iop_MullEven16Sx8, mkexpr(ones16x8), vIn) );
550 assign( *vEvn, binop(Iop_MullEven16Sx8, mkexpr(ones16x8),
551 binop(Iop_ShrV128, vIn, mkU8(16))) );
cerion4a49b032005-11-08 16:23:07 +0000552}
553
554/* break V128 to 4xI32's, then sign-extend to I64's */
555static void breakV128to4x64S( IRExpr* t128,
556 /*OUTs*/
557 IRTemp* t3, IRTemp* t2,
558 IRTemp* t1, IRTemp* t0 )
559{
560 IRTemp hi64 = newTemp(Ity_I64);
561 IRTemp lo64 = newTemp(Ity_I64);
562
563 vassert(typeOfIRExpr(irbb->tyenv, t128) == Ity_V128);
564 vassert(t0 && *t0 == IRTemp_INVALID);
565 vassert(t1 && *t1 == IRTemp_INVALID);
566 vassert(t2 && *t2 == IRTemp_INVALID);
567 vassert(t3 && *t3 == IRTemp_INVALID);
568 *t0 = newTemp(Ity_I64);
569 *t1 = newTemp(Ity_I64);
570 *t2 = newTemp(Ity_I64);
571 *t3 = newTemp(Ity_I64);
572
573 assign( hi64, unop(Iop_V128HIto64, t128) );
574 assign( lo64, unop(Iop_V128to64, t128) );
575 assign( *t3, unop(Iop_32Sto64, unop(Iop_64HIto32, mkexpr(hi64))) );
576 assign( *t2, unop(Iop_32Sto64, unop(Iop_64to32, mkexpr(hi64))) );
577 assign( *t1, unop(Iop_32Sto64, unop(Iop_64HIto32, mkexpr(lo64))) );
578 assign( *t0, unop(Iop_32Sto64, unop(Iop_64to32, mkexpr(lo64))) );
579}
580
581/* break V128 to 4xI32's, then zero-extend to I64's */
582static void breakV128to4x64U ( IRExpr* t128,
583 /*OUTs*/
584 IRTemp* t3, IRTemp* t2,
585 IRTemp* t1, IRTemp* t0 )
586{
587 IRTemp hi64 = newTemp(Ity_I64);
588 IRTemp lo64 = newTemp(Ity_I64);
589
590 vassert(typeOfIRExpr(irbb->tyenv, t128) == Ity_V128);
591 vassert(t0 && *t0 == IRTemp_INVALID);
592 vassert(t1 && *t1 == IRTemp_INVALID);
593 vassert(t2 && *t2 == IRTemp_INVALID);
594 vassert(t3 && *t3 == IRTemp_INVALID);
595 *t0 = newTemp(Ity_I64);
596 *t1 = newTemp(Ity_I64);
597 *t2 = newTemp(Ity_I64);
598 *t3 = newTemp(Ity_I64);
599
600 assign( hi64, unop(Iop_V128HIto64, t128) );
601 assign( lo64, unop(Iop_V128to64, t128) );
602 assign( *t3, unop(Iop_32Uto64, unop(Iop_64HIto32, mkexpr(hi64))) );
603 assign( *t2, unop(Iop_32Uto64, unop(Iop_64to32, mkexpr(hi64))) );
604 assign( *t1, unop(Iop_32Uto64, unop(Iop_64HIto32, mkexpr(lo64))) );
605 assign( *t0, unop(Iop_32Uto64, unop(Iop_64to32, mkexpr(lo64))) );
606}
607
608/* Signed saturating narrow 64S to 32 */
609static IRExpr* mkQNarrow64Sto32 ( IRExpr* t64 )
610{
611 IRTemp hi32 = newTemp(Ity_I32);
612 IRTemp lo32 = newTemp(Ity_I32);
613
614 vassert(typeOfIRExpr(irbb->tyenv, t64) == Ity_I64);
615
616 assign( hi32, unop(Iop_64HIto32, t64));
617 assign( lo32, unop(Iop_64to32, t64));
618
619 return IRExpr_Mux0X(
620 /* if (hi32 == (lo32 >>s 31)) */
621 unop(Iop_1Uto8,
622 binop(Iop_CmpEQ32, mkexpr(hi32),
623 binop( Iop_Sar32, mkexpr(lo32), mkU8(31)))),
624 /* else: sign dep saturate: 1->0x80000000, 0->0x7FFFFFFF */
625 binop(Iop_Add32, mkU32(0x7FFFFFFF),
626 binop(Iop_Shr32, mkexpr(hi32), mkU8(31))),
627 /* then: within signed-32 range: lo half good enough */
628 mkexpr(lo32) );
629}
630
631/* Unsigned saturating narrow 64S to 32 */
632static IRExpr* mkQNarrow64Uto32 ( IRExpr* t64 )
633{
634 IRTemp hi32 = newTemp(Ity_I32);
635 IRTemp lo32 = newTemp(Ity_I32);
636
637 vassert(typeOfIRExpr(irbb->tyenv, t64) == Ity_I64);
638
639 assign( hi32, unop(Iop_64HIto32, t64));
640 assign( lo32, unop(Iop_64to32, t64));
641
642 return IRExpr_Mux0X(
643 /* if (top 32 bits of t64 are 0) */
644 unop(Iop_1Uto8, binop(Iop_CmpEQ32, mkexpr(hi32), mkU32(0))),
645 /* else: positive saturate -> 0xFFFFFFFF */
646 mkU32(0xFFFFFFFF),
647 /* then: within unsigned-32 range: lo half good enough */
648 mkexpr(lo32) );
649}
650
651/* Signed saturate narrow 64->32, combining to V128 */
652static IRExpr* mkV128from4x64S ( IRExpr* t3, IRExpr* t2,
653 IRExpr* t1, IRExpr* t0 )
654{
655 vassert(typeOfIRExpr(irbb->tyenv, t3) == Ity_I64);
656 vassert(typeOfIRExpr(irbb->tyenv, t2) == Ity_I64);
657 vassert(typeOfIRExpr(irbb->tyenv, t1) == Ity_I64);
658 vassert(typeOfIRExpr(irbb->tyenv, t0) == Ity_I64);
659 return binop(Iop_64HLtoV128,
660 binop(Iop_32HLto64,
661 mkQNarrow64Sto32( t3 ),
662 mkQNarrow64Sto32( t2 )),
663 binop(Iop_32HLto64,
664 mkQNarrow64Sto32( t1 ),
665 mkQNarrow64Sto32( t0 )));
666}
667
668/* Unsigned saturate narrow 64->32, combining to V128 */
669static IRExpr* mkV128from4x64U ( IRExpr* t3, IRExpr* t2,
670 IRExpr* t1, IRExpr* t0 )
671{
672 vassert(typeOfIRExpr(irbb->tyenv, t3) == Ity_I64);
673 vassert(typeOfIRExpr(irbb->tyenv, t2) == Ity_I64);
674 vassert(typeOfIRExpr(irbb->tyenv, t1) == Ity_I64);
675 vassert(typeOfIRExpr(irbb->tyenv, t0) == Ity_I64);
676 return binop(Iop_64HLtoV128,
677 binop(Iop_32HLto64,
678 mkQNarrow64Uto32( t3 ),
679 mkQNarrow64Uto32( t2 )),
680 binop(Iop_32HLto64,
681 mkQNarrow64Uto32( t1 ),
682 mkQNarrow64Uto32( t0 )));
683}
684
cerion24d06f12005-11-09 21:34:20 +0000685/* Simulate irops Iop_MullOdd*, since we don't have them */
686#define MK_Iop_MullOdd8Ux16( expr_vA, expr_vB ) \
687 binop(Iop_MullEven8Ux16, \
688 binop(Iop_ShrV128, expr_vA, mkU8(8)), \
689 binop(Iop_ShrV128, expr_vB, mkU8(8)))
690
691#define MK_Iop_MullOdd8Sx16( expr_vA, expr_vB ) \
692 binop(Iop_MullEven8Sx16, \
693 binop(Iop_ShrV128, expr_vA, mkU8(8)), \
694 binop(Iop_ShrV128, expr_vB, mkU8(8)))
695
696#define MK_Iop_MullOdd16Ux8( expr_vA, expr_vB ) \
697 binop(Iop_MullEven16Ux8, \
698 binop(Iop_ShrV128, expr_vA, mkU8(16)), \
699 binop(Iop_ShrV128, expr_vB, mkU8(16)))
700
701#define MK_Iop_MullOdd16Sx8( expr_vA, expr_vB ) \
702 binop(Iop_MullEven16Sx8, \
703 binop(Iop_ShrV128, expr_vA, mkU8(16)), \
704 binop(Iop_ShrV128, expr_vB, mkU8(16)))
705
cerion59b2c312005-12-17 11:28:53 +0000706static IRExpr* /* :: Ity_I64 */ mk64lo32Sto64 ( IRExpr* src )
ceriond953ebb2005-11-29 13:27:20 +0000707{
708 vassert(typeOfIRExpr(irbb->tyenv, src) == Ity_I64);
709 return unop(Iop_32Sto64, unop(Iop_64to32, src));
710}
711
cerion59b2c312005-12-17 11:28:53 +0000712static IRExpr* /* :: Ity_I64 */ mk64lo32Uto64 ( IRExpr* src )
cerionbb01b7c2005-12-16 13:40:18 +0000713{
714 vassert(typeOfIRExpr(irbb->tyenv, src) == Ity_I64);
715 return unop(Iop_32Uto64, unop(Iop_64to32, src));
716}
717
cerion2831b002005-11-30 19:55:22 +0000718static IROp mkSzOp ( IRType ty, IROp op8 )
ceriond953ebb2005-11-29 13:27:20 +0000719{
720 Int adj;
721 vassert(ty == Ity_I8 || ty == Ity_I16 ||
722 ty == Ity_I32 || ty == Ity_I64);
723 vassert(op8 == Iop_Add8 || op8 == Iop_Sub8 || op8 == Iop_Mul8 ||
724 op8 == Iop_Or8 || op8 == Iop_And8 || op8 == Iop_Xor8 ||
725 op8 == Iop_Shl8 || op8 == Iop_Shr8 || op8 == Iop_Sar8 ||
726 op8 == Iop_CmpEQ8 || op8 == Iop_CmpNE8 ||
727 op8 == Iop_Not8 || op8 == Iop_Neg8 );
728 adj = ty==Ity_I8 ? 0 : (ty==Ity_I16 ? 1 : (ty==Ity_I32 ? 2 : 3));
729 return adj + op8;
730}
731
cerion5b2325f2005-12-23 00:55:09 +0000732/* Make sure we get valid 32 and 64bit addresses */
cerion2831b002005-11-30 19:55:22 +0000733static Addr64 mkSzAddr ( IRType ty, Addr64 addr )
ceriond953ebb2005-11-29 13:27:20 +0000734{
735 vassert(ty == Ity_I32 || ty == Ity_I64);
736 return ( ty == Ity_I64 ?
737 (Addr64)addr :
738 (Addr64)extend_s_32to64( toUInt(addr) ) );
739}
740
741/* sz, ULong -> IRExpr */
cerion2831b002005-11-30 19:55:22 +0000742static IRExpr* mkSzImm ( IRType ty, ULong imm64 )
ceriond953ebb2005-11-29 13:27:20 +0000743{
744 vassert(ty == Ity_I32 || ty == Ity_I64);
745 return ty == Ity_I64 ? mkU64(imm64) : mkU32((UInt)imm64);
746}
747
748/* sz, ULong -> IRConst */
cerion2831b002005-11-30 19:55:22 +0000749static IRConst* mkSzConst ( IRType ty, ULong imm64 )
ceriond953ebb2005-11-29 13:27:20 +0000750{
751 vassert(ty == Ity_I32 || ty == Ity_I64);
752 return ( ty == Ity_I64 ?
753 IRConst_U64(imm64) :
754 IRConst_U32((UInt)imm64) );
755}
756
757/* Sign extend imm16 -> IRExpr* */
cerion2831b002005-11-30 19:55:22 +0000758static IRExpr* mkSzExtendS16 ( IRType ty, UInt imm16 )
ceriond953ebb2005-11-29 13:27:20 +0000759{
760 vassert(ty == Ity_I32 || ty == Ity_I64);
761 return ( ty == Ity_I64 ?
762 mkU64(extend_s_16to64(imm16)) :
763 mkU32(extend_s_16to32(imm16)) );
764}
765
766/* Sign extend imm32 -> IRExpr* */
cerion2831b002005-11-30 19:55:22 +0000767static IRExpr* mkSzExtendS32 ( IRType ty, UInt imm32 )
ceriond953ebb2005-11-29 13:27:20 +0000768{
769 vassert(ty == Ity_I32 || ty == Ity_I64);
770 return ( ty == Ity_I64 ?
771 mkU64(extend_s_32to64(imm32)) :
772 mkU32(imm32) );
773}
774
775/* IR narrows I32/I64 -> I8/I16/I32 */
cerion2831b002005-11-30 19:55:22 +0000776static IRExpr* mkSzNarrow8 ( IRType ty, IRExpr* src )
ceriond953ebb2005-11-29 13:27:20 +0000777{
778 vassert(ty == Ity_I32 || ty == Ity_I64);
779 return ty == Ity_I64 ? unop(Iop_64to8, src) : unop(Iop_32to8, src);
780}
781
cerion2831b002005-11-30 19:55:22 +0000782static IRExpr* mkSzNarrow16 ( IRType ty, IRExpr* src )
ceriond953ebb2005-11-29 13:27:20 +0000783{
784 vassert(ty == Ity_I32 || ty == Ity_I64);
785 return ty == Ity_I64 ? unop(Iop_64to16, src) : unop(Iop_32to16, src);
786}
787
cerion2831b002005-11-30 19:55:22 +0000788static IRExpr* mkSzNarrow32 ( IRType ty, IRExpr* src )
ceriond953ebb2005-11-29 13:27:20 +0000789{
790 vassert(ty == Ity_I32 || ty == Ity_I64);
791 return ty == Ity_I64 ? unop(Iop_64to32, src) : src;
792}
793
794/* Signed/Unsigned IR widens I8/I16/I32 -> I32/I64 */
cerion2831b002005-11-30 19:55:22 +0000795static IRExpr* mkSzWiden8 ( IRType ty, IRExpr* src, Bool sined )
ceriond953ebb2005-11-29 13:27:20 +0000796{
797 vassert(ty == Ity_I32 || ty == Ity_I64);
798 IROp op;
799 if (sined) op = (ty==Ity_I32) ? Iop_8Sto32 : Iop_8Sto64;
800 else op = (ty==Ity_I32) ? Iop_8Uto32 : Iop_8Uto64;
801 return unop(op, src);
802}
803
cerion2831b002005-11-30 19:55:22 +0000804static IRExpr* mkSzWiden16 ( IRType ty, IRExpr* src, Bool sined )
ceriond953ebb2005-11-29 13:27:20 +0000805{
806 vassert(ty == Ity_I32 || ty == Ity_I64);
807 IROp op;
808 if (sined) op = (ty==Ity_I32) ? Iop_16Sto32 : Iop_16Sto64;
809 else op = (ty==Ity_I32) ? Iop_16Uto32 : Iop_16Uto64;
810 return unop(op, src);
811}
812
cerion2831b002005-11-30 19:55:22 +0000813static IRExpr* mkSzWiden32 ( IRType ty, IRExpr* src, Bool sined )
ceriond953ebb2005-11-29 13:27:20 +0000814{
815 vassert(ty == Ity_I32 || ty == Ity_I64);
816 if (ty == Ity_I32)
817 return src;
818 return (sined) ? unop(Iop_32Sto64, src) : unop(Iop_32Uto64, src);
819}
cerion24d06f12005-11-09 21:34:20 +0000820
cerion4a49b032005-11-08 16:23:07 +0000821
sewardjb51f0f42005-07-18 11:38:02 +0000822static Int integerGuestRegOffset ( UInt archreg )
cerion45b70ff2005-01-31 17:03:25 +0000823{
sewardjb51f0f42005-07-18 11:38:02 +0000824 vassert(archreg < 32);
825
826 // jrs: probably not necessary; only matters if we reference sub-parts
cerion5b2325f2005-12-23 00:55:09 +0000827 // of the ppc registers, but that isn't the case
sewardjb51f0f42005-07-18 11:38:02 +0000828 // later: this might affect Altivec though?
829 vassert(host_is_bigendian);
830
cerion5b2325f2005-12-23 00:55:09 +0000831 switch (archreg) {
832 case 0: return offsetofPPCGuestState(guest_GPR0);
833 case 1: return offsetofPPCGuestState(guest_GPR1);
834 case 2: return offsetofPPCGuestState(guest_GPR2);
835 case 3: return offsetofPPCGuestState(guest_GPR3);
836 case 4: return offsetofPPCGuestState(guest_GPR4);
837 case 5: return offsetofPPCGuestState(guest_GPR5);
838 case 6: return offsetofPPCGuestState(guest_GPR6);
839 case 7: return offsetofPPCGuestState(guest_GPR7);
840 case 8: return offsetofPPCGuestState(guest_GPR8);
841 case 9: return offsetofPPCGuestState(guest_GPR9);
842 case 10: return offsetofPPCGuestState(guest_GPR10);
843 case 11: return offsetofPPCGuestState(guest_GPR11);
844 case 12: return offsetofPPCGuestState(guest_GPR12);
845 case 13: return offsetofPPCGuestState(guest_GPR13);
846 case 14: return offsetofPPCGuestState(guest_GPR14);
847 case 15: return offsetofPPCGuestState(guest_GPR15);
848 case 16: return offsetofPPCGuestState(guest_GPR16);
849 case 17: return offsetofPPCGuestState(guest_GPR17);
850 case 18: return offsetofPPCGuestState(guest_GPR18);
851 case 19: return offsetofPPCGuestState(guest_GPR19);
852 case 20: return offsetofPPCGuestState(guest_GPR20);
853 case 21: return offsetofPPCGuestState(guest_GPR21);
854 case 22: return offsetofPPCGuestState(guest_GPR22);
855 case 23: return offsetofPPCGuestState(guest_GPR23);
856 case 24: return offsetofPPCGuestState(guest_GPR24);
857 case 25: return offsetofPPCGuestState(guest_GPR25);
858 case 26: return offsetofPPCGuestState(guest_GPR26);
859 case 27: return offsetofPPCGuestState(guest_GPR27);
860 case 28: return offsetofPPCGuestState(guest_GPR28);
861 case 29: return offsetofPPCGuestState(guest_GPR29);
862 case 30: return offsetofPPCGuestState(guest_GPR30);
863 case 31: return offsetofPPCGuestState(guest_GPR31);
864 default: break;
sewardjb51f0f42005-07-18 11:38:02 +0000865 }
cerion5b2325f2005-12-23 00:55:09 +0000866 vpanic("integerGuestRegOffset(ppc,be)"); /*notreached*/
sewardjb51f0f42005-07-18 11:38:02 +0000867}
868
869static IRExpr* getIReg ( UInt archreg )
870{
ceriond953ebb2005-11-29 13:27:20 +0000871 IRType ty = mode64 ? Ity_I64 : Ity_I32;
sewardjb51f0f42005-07-18 11:38:02 +0000872 vassert(archreg < 32);
ceriond953ebb2005-11-29 13:27:20 +0000873 return IRExpr_Get( integerGuestRegOffset(archreg), ty );
sewardjb51f0f42005-07-18 11:38:02 +0000874}
875
876/* Ditto, but write to a reg instead. */
877static void putIReg ( UInt archreg, IRExpr* e )
878{
ceriond953ebb2005-11-29 13:27:20 +0000879 IRType ty = mode64 ? Ity_I64 : Ity_I32;
sewardjb51f0f42005-07-18 11:38:02 +0000880 vassert(archreg < 32);
ceriond953ebb2005-11-29 13:27:20 +0000881 vassert(typeOfIRExpr(irbb->tyenv, e) == ty );
sewardjb51f0f42005-07-18 11:38:02 +0000882 stmt( IRStmt_Put(integerGuestRegOffset(archreg), e) );
883}
884
885
886static Int floatGuestRegOffset ( UInt archreg )
887{
888 vassert(archreg < 32);
889
cerion5b2325f2005-12-23 00:55:09 +0000890 switch (archreg) {
891 case 0: return offsetofPPCGuestState(guest_FPR0);
892 case 1: return offsetofPPCGuestState(guest_FPR1);
893 case 2: return offsetofPPCGuestState(guest_FPR2);
894 case 3: return offsetofPPCGuestState(guest_FPR3);
895 case 4: return offsetofPPCGuestState(guest_FPR4);
896 case 5: return offsetofPPCGuestState(guest_FPR5);
897 case 6: return offsetofPPCGuestState(guest_FPR6);
898 case 7: return offsetofPPCGuestState(guest_FPR7);
899 case 8: return offsetofPPCGuestState(guest_FPR8);
900 case 9: return offsetofPPCGuestState(guest_FPR9);
901 case 10: return offsetofPPCGuestState(guest_FPR10);
902 case 11: return offsetofPPCGuestState(guest_FPR11);
903 case 12: return offsetofPPCGuestState(guest_FPR12);
904 case 13: return offsetofPPCGuestState(guest_FPR13);
905 case 14: return offsetofPPCGuestState(guest_FPR14);
906 case 15: return offsetofPPCGuestState(guest_FPR15);
907 case 16: return offsetofPPCGuestState(guest_FPR16);
908 case 17: return offsetofPPCGuestState(guest_FPR17);
909 case 18: return offsetofPPCGuestState(guest_FPR18);
910 case 19: return offsetofPPCGuestState(guest_FPR19);
911 case 20: return offsetofPPCGuestState(guest_FPR20);
912 case 21: return offsetofPPCGuestState(guest_FPR21);
913 case 22: return offsetofPPCGuestState(guest_FPR22);
914 case 23: return offsetofPPCGuestState(guest_FPR23);
915 case 24: return offsetofPPCGuestState(guest_FPR24);
916 case 25: return offsetofPPCGuestState(guest_FPR25);
917 case 26: return offsetofPPCGuestState(guest_FPR26);
918 case 27: return offsetofPPCGuestState(guest_FPR27);
919 case 28: return offsetofPPCGuestState(guest_FPR28);
920 case 29: return offsetofPPCGuestState(guest_FPR29);
921 case 30: return offsetofPPCGuestState(guest_FPR30);
922 case 31: return offsetofPPCGuestState(guest_FPR31);
923 default: break;
sewardjb51f0f42005-07-18 11:38:02 +0000924 }
cerion5b2325f2005-12-23 00:55:09 +0000925 vpanic("floatGuestRegOffset(ppc)"); /*notreached*/
sewardjb51f0f42005-07-18 11:38:02 +0000926}
927
928static IRExpr* getFReg ( UInt archreg )
929{
930 vassert(archreg < 32);
931 return IRExpr_Get( floatGuestRegOffset(archreg), Ity_F64 );
932}
933
934/* Ditto, but write to a reg instead. */
935static void putFReg ( UInt archreg, IRExpr* e )
936{
937 vassert(archreg < 32);
938 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_F64);
939 stmt( IRStmt_Put(floatGuestRegOffset(archreg), e) );
940}
941
942
943static Int vectorGuestRegOffset ( UInt archreg )
944{
945 vassert(archreg < 32);
946
cerion5b2325f2005-12-23 00:55:09 +0000947 switch (archreg) {
948 case 0: return offsetofPPCGuestState(guest_VR0);
949 case 1: return offsetofPPCGuestState(guest_VR1);
950 case 2: return offsetofPPCGuestState(guest_VR2);
951 case 3: return offsetofPPCGuestState(guest_VR3);
952 case 4: return offsetofPPCGuestState(guest_VR4);
953 case 5: return offsetofPPCGuestState(guest_VR5);
954 case 6: return offsetofPPCGuestState(guest_VR6);
955 case 7: return offsetofPPCGuestState(guest_VR7);
956 case 8: return offsetofPPCGuestState(guest_VR8);
957 case 9: return offsetofPPCGuestState(guest_VR9);
958 case 10: return offsetofPPCGuestState(guest_VR10);
959 case 11: return offsetofPPCGuestState(guest_VR11);
960 case 12: return offsetofPPCGuestState(guest_VR12);
961 case 13: return offsetofPPCGuestState(guest_VR13);
962 case 14: return offsetofPPCGuestState(guest_VR14);
963 case 15: return offsetofPPCGuestState(guest_VR15);
964 case 16: return offsetofPPCGuestState(guest_VR16);
965 case 17: return offsetofPPCGuestState(guest_VR17);
966 case 18: return offsetofPPCGuestState(guest_VR18);
967 case 19: return offsetofPPCGuestState(guest_VR19);
968 case 20: return offsetofPPCGuestState(guest_VR20);
969 case 21: return offsetofPPCGuestState(guest_VR21);
970 case 22: return offsetofPPCGuestState(guest_VR22);
971 case 23: return offsetofPPCGuestState(guest_VR23);
972 case 24: return offsetofPPCGuestState(guest_VR24);
973 case 25: return offsetofPPCGuestState(guest_VR25);
974 case 26: return offsetofPPCGuestState(guest_VR26);
975 case 27: return offsetofPPCGuestState(guest_VR27);
976 case 28: return offsetofPPCGuestState(guest_VR28);
977 case 29: return offsetofPPCGuestState(guest_VR29);
978 case 30: return offsetofPPCGuestState(guest_VR30);
979 case 31: return offsetofPPCGuestState(guest_VR31);
980 default: break;
sewardjb51f0f42005-07-18 11:38:02 +0000981 }
cerion5b2325f2005-12-23 00:55:09 +0000982 vpanic("vextorGuestRegOffset(ppc)"); /*notreached*/
sewardjb51f0f42005-07-18 11:38:02 +0000983}
984
985static IRExpr* getVReg ( UInt archreg )
986{
987 vassert(archreg < 32);
988 return IRExpr_Get( vectorGuestRegOffset(archreg), Ity_V128 );
989}
990
991/* Ditto, but write to a reg instead. */
992static void putVReg ( UInt archreg, IRExpr* e )
993{
994 vassert(archreg < 32);
995 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_V128);
996 stmt( IRStmt_Put(vectorGuestRegOffset(archreg), e) );
997}
998
999static Int guestCR321offset ( UInt cr )
1000{
cerion5b2325f2005-12-23 00:55:09 +00001001 switch (cr) {
1002 case 0: return offsetofPPCGuestState(guest_CR0_321 );
1003 case 1: return offsetofPPCGuestState(guest_CR1_321 );
1004 case 2: return offsetofPPCGuestState(guest_CR2_321 );
1005 case 3: return offsetofPPCGuestState(guest_CR3_321 );
1006 case 4: return offsetofPPCGuestState(guest_CR4_321 );
1007 case 5: return offsetofPPCGuestState(guest_CR5_321 );
1008 case 6: return offsetofPPCGuestState(guest_CR6_321 );
1009 case 7: return offsetofPPCGuestState(guest_CR7_321 );
1010 default: vpanic("guestCR321offset(ppc)");
sewardjb51f0f42005-07-18 11:38:02 +00001011 }
1012}
1013
1014static Int guestCR0offset ( UInt cr )
1015{
cerion5b2325f2005-12-23 00:55:09 +00001016 switch (cr) {
1017 case 0: return offsetofPPCGuestState(guest_CR0_0 );
1018 case 1: return offsetofPPCGuestState(guest_CR1_0 );
1019 case 2: return offsetofPPCGuestState(guest_CR2_0 );
1020 case 3: return offsetofPPCGuestState(guest_CR3_0 );
1021 case 4: return offsetofPPCGuestState(guest_CR4_0 );
1022 case 5: return offsetofPPCGuestState(guest_CR5_0 );
1023 case 6: return offsetofPPCGuestState(guest_CR6_0 );
1024 case 7: return offsetofPPCGuestState(guest_CR7_0 );
1025 default: vpanic("guestCR3offset(ppc)");
sewardjb51f0f42005-07-18 11:38:02 +00001026 }
sewardjb51f0f42005-07-18 11:38:02 +00001027}
1028
cerion07b07a92005-12-22 14:32:35 +00001029// ROTL(src32/64, rot_amt5/6)
ceriond953ebb2005-11-29 13:27:20 +00001030static IRExpr* /* :: Ity_I32/64 */ ROTL ( IRExpr* src,
1031 IRExpr* rot_amt )
sewardjb51f0f42005-07-18 11:38:02 +00001032{
ceriond953ebb2005-11-29 13:27:20 +00001033 IRExpr *mask, *rot;
cerionf0de28c2005-12-13 20:21:11 +00001034 vassert(typeOfIRExpr(irbb->tyenv,rot_amt) == Ity_I8);
sewardjb51f0f42005-07-18 11:38:02 +00001035
cerionf0de28c2005-12-13 20:21:11 +00001036 if (typeOfIRExpr(irbb->tyenv,src) == Ity_I64) {
ceriond953ebb2005-11-29 13:27:20 +00001037 // rot = (src << rot_amt) | (src >> (64-rot_amt))
1038 mask = binop(Iop_And8, rot_amt, mkU8(63));
1039 rot = binop(Iop_Or64,
1040 binop(Iop_Shl64, src, mask),
1041 binop(Iop_Shr64, src, binop(Iop_Sub8, mkU8(64), mask)));
1042 } else {
ceriond953ebb2005-11-29 13:27:20 +00001043 // rot = (src << rot_amt) | (src >> (32-rot_amt))
cerionf0de28c2005-12-13 20:21:11 +00001044 mask = binop(Iop_And8, rot_amt, mkU8(31));
ceriond953ebb2005-11-29 13:27:20 +00001045 rot = binop(Iop_Or32,
1046 binop(Iop_Shl32, src, mask),
1047 binop(Iop_Shr32, src, binop(Iop_Sub8, mkU8(32), mask)));
cerion2831b002005-11-30 19:55:22 +00001048 }
sewardjc9659532005-07-21 21:33:57 +00001049 /* Note: the MuxOX is not merely an optimisation; it's needed
cerionf0de28c2005-12-13 20:21:11 +00001050 because otherwise the Shr is a shift by the word size when
ceriond953ebb2005-11-29 13:27:20 +00001051 mask denotes zero. For rotates by immediates, a lot of
sewardjc9659532005-07-21 21:33:57 +00001052 this junk gets folded out. */
ceriond953ebb2005-11-29 13:27:20 +00001053 return IRExpr_Mux0X( mask, /* zero rotate */ src,
1054 /* non-zero rotate */ rot );
cerion45b70ff2005-01-31 17:03:25 +00001055}
cerion896a1372005-01-25 12:24:25 +00001056
ceriond953ebb2005-11-29 13:27:20 +00001057#if 0
1058/* ROTL32_64(src64, rot_amt5)
1059 Weirdo 32bit rotl on ppc64:
1060 rot32 = ROTL(src_lo32,y);
1061 return (rot32|rot32);
1062*/
1063static IRExpr* /* :: Ity_I64 */ ROTL32_64 ( IRExpr* src64,
1064 IRExpr* rot_amt )
sewardj87e651f2005-09-09 08:31:18 +00001065{
ceriond953ebb2005-11-29 13:27:20 +00001066 IRExpr *mask, *rot32;
1067 vassert(mode64); // used only in 64bit mode
1068 vassert(typeOfIRExpr(irbb->tyenv,src64) == Ity_I64);
1069 vassert(typeOfIRExpr(irbb->tyenv,rot_amt) == Ity_I8);
1070
1071 mask = binop(Iop_And8, rot_amt, mkU8(31));
1072 rot32 = ROTL( unop(Iop_64to32, src64), rot_amt );
1073
1074 return binop(Iop_Or64,
1075 binop(Iop_Shl64, unop(Iop_32Uto64, rot32), mkU8(32)),
cerion2831b002005-11-30 19:55:22 +00001076 unop(Iop_32Uto64, rot32));
ceriond953ebb2005-11-29 13:27:20 +00001077}
1078#endif
1079
1080
1081/* Standard effective address calc: (rA + rB) */
1082static IRExpr* ea_rA_idxd ( UInt rA, UInt rB )
1083{
1084 IRType ty = mode64 ? Ity_I64 : Ity_I32;
1085 vassert(rA < 32);
1086 vassert(rB < 32);
cerion2831b002005-11-30 19:55:22 +00001087 return binop(mkSzOp(ty, Iop_Add8), getIReg(rA), getIReg(rB));
sewardj87e651f2005-09-09 08:31:18 +00001088}
1089
ceriond953ebb2005-11-29 13:27:20 +00001090/* Standard effective address calc: (rA + simm) */
1091static IRExpr* ea_rA_simm ( UInt rA, UInt simm16 )
sewardj87e651f2005-09-09 08:31:18 +00001092{
ceriond953ebb2005-11-29 13:27:20 +00001093 IRType ty = mode64 ? Ity_I64 : Ity_I32;
1094 vassert(rA < 32);
cerion2831b002005-11-30 19:55:22 +00001095 return binop(mkSzOp(ty, Iop_Add8), getIReg(rA),
1096 mkSzExtendS16(ty, simm16));
ceriond953ebb2005-11-29 13:27:20 +00001097}
1098
1099/* Standard effective address calc: (rA|0) */
1100static IRExpr* ea_rAor0 ( UInt rA )
1101{
1102 IRType ty = mode64 ? Ity_I64 : Ity_I32;
1103 vassert(rA < 32);
sewardj87e651f2005-09-09 08:31:18 +00001104 if (rA == 0) {
cerion2831b002005-11-30 19:55:22 +00001105 return mkSzImm(ty, 0);
sewardj87e651f2005-09-09 08:31:18 +00001106 } else {
1107 return getIReg(rA);
1108 }
1109}
1110
ceriond953ebb2005-11-29 13:27:20 +00001111/* Standard effective address calc: (rA|0) + rB */
1112static IRExpr* ea_rAor0_idxd ( UInt rA, UInt rB )
1113{
1114 vassert(rA < 32);
1115 vassert(rB < 32);
1116 return (rA == 0) ? getIReg(rB) : ea_rA_idxd( rA, rB );
1117}
1118
1119/* Standard effective address calc: (rA|0) + simm16 */
1120static IRExpr* ea_rAor0_simm ( UInt rA, UInt simm16 )
1121{
1122 IRType ty = mode64 ? Ity_I64 : Ity_I32;
1123 vassert(rA < 32);
1124 if (rA == 0) {
cerion2831b002005-11-30 19:55:22 +00001125 return mkSzExtendS16(ty, simm16);
ceriond953ebb2005-11-29 13:27:20 +00001126 } else {
1127 return ea_rA_simm( rA, simm16 );
1128 }
1129}
1130
1131
1132/* Align effective address */
1133static IRExpr* addr_align( IRExpr* addr, UChar align )
1134{
1135 IRType ty = mode64 ? Ity_I64 : Ity_I32;
1136 Long mask;
1137 switch (align) {
cerion2831b002005-11-30 19:55:22 +00001138 case 1: return addr; // byte aligned
ceriond953ebb2005-11-29 13:27:20 +00001139 case 2: mask = ((Long)-1) << 1; break; // half-word aligned
1140 case 4: mask = ((Long)-1) << 2; break; // word aligned
1141 case 16: mask = ((Long)-1) << 4; break; // quad-word aligned
1142 default:
1143 vex_printf("addr_align: align = %u\n", align);
cerion5b2325f2005-12-23 00:55:09 +00001144 vpanic("addr_align(ppc)");
ceriond953ebb2005-11-29 13:27:20 +00001145 }
1146
1147 vassert(typeOfIRExpr(irbb->tyenv,addr) == ty);
cerionfb197c42005-12-24 12:32:10 +00001148 return binop( mkSzOp(ty, Iop_And8), addr, mkSzImm(ty, mask) );
ceriond953ebb2005-11-29 13:27:20 +00001149}
1150
cerion896a1372005-01-25 12:24:25 +00001151
cerion896a1372005-01-25 12:24:25 +00001152/*------------------------------------------------------------*/
sewardjb51f0f42005-07-18 11:38:02 +00001153/*--- Helpers for condition codes. ---*/
cerion896a1372005-01-25 12:24:25 +00001154/*------------------------------------------------------------*/
1155
sewardjb51f0f42005-07-18 11:38:02 +00001156/* Condition register layout.
cerion896a1372005-01-25 12:24:25 +00001157
sewardjb51f0f42005-07-18 11:38:02 +00001158 In the hardware, CR is laid out like this. The leftmost end is the
1159 most significant bit in the register; however the IBM documentation
1160 numbers the bits backwards for some reason.
1161
1162 CR0 CR1 .......... CR6 CR7
1163 0 .. 3 ....................... 28 .. 31 (IBM bit numbering)
1164 31 28 3 0 (normal bit numbering)
1165
cerionedf7fc52005-11-18 20:57:41 +00001166 Each CR field is 4 bits: [<,>,==,SO]
sewardjb51f0f42005-07-18 11:38:02 +00001167
cerionedf7fc52005-11-18 20:57:41 +00001168 Hence in IBM's notation, BI=0 is CR7[SO], BI=1 is CR7[==], etc.
sewardjb51f0f42005-07-18 11:38:02 +00001169
1170 Indexing from BI to guest state:
1171
1172 let n = BI / 4
1173 off = BI % 4
1174 this references CR n:
1175
cerionedf7fc52005-11-18 20:57:41 +00001176 off==0 -> guest_CRn_321 >> 3
1177 off==1 -> guest_CRn_321 >> 2
1178 off==2 -> guest_CRn_321 >> 1
sewardjb51f0f42005-07-18 11:38:02 +00001179 off==3 -> guest_CRn_SO
sewardjb51f0f42005-07-18 11:38:02 +00001180
1181 Bear in mind the only significant bit in guest_CRn_SO is bit 0
cerionedf7fc52005-11-18 20:57:41 +00001182 (normal notation) and in guest_CRn_321 the significant bits are
sewardjb51f0f42005-07-18 11:38:02 +00001183 3, 2 and 1 (normal notation).
1184*/
cerionedf7fc52005-11-18 20:57:41 +00001185
1186static void putCR321 ( UInt cr, IRExpr* e )
1187{
1188 vassert(cr < 8);
1189 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_I8);
1190 stmt( IRStmt_Put(guestCR321offset(cr), e) );
1191}
1192
1193static void putCR0 ( UInt cr, IRExpr* e )
1194{
1195 vassert(cr < 8);
1196 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_I8);
1197 stmt( IRStmt_Put(guestCR0offset(cr), e) );
1198}
1199
1200static IRExpr* /* :: Ity_I8 */ getCR0 ( UInt cr )
1201{
1202 vassert(cr < 8);
1203 return IRExpr_Get(guestCR0offset(cr), Ity_I8);
1204}
1205
1206static IRExpr* /* :: Ity_I8 */ getCR321 ( UInt cr )
1207{
1208 vassert(cr < 8);
1209 return IRExpr_Get(guestCR321offset(cr), Ity_I8);
1210}
1211
sewardjb51f0f42005-07-18 11:38:02 +00001212/* Fetch the specified CR bit (as per IBM/hardware notation) and
1213 return it at the bottom of an I32; the top 31 bits are guaranteed
1214 to be zero. */
1215static IRExpr* /* :: Ity_I32 */ getCRbit ( UInt bi )
cerion896a1372005-01-25 12:24:25 +00001216{
sewardjb51f0f42005-07-18 11:38:02 +00001217 UInt n = bi / 4;
1218 UInt off = bi % 4;
1219 vassert(bi < 32);
1220 if (off == 3) {
1221 /* Fetch the SO bit for this CR field */
1222 /* Note: And32 is redundant paranoia iff guest state only has 0
1223 or 1 in that slot. */
1224 return binop(Iop_And32, unop(Iop_8Uto32, getCR0(n)), mkU32(1));
1225 } else {
1226 /* Fetch the <, > or == bit for this CR field */
1227 return binop( Iop_And32,
1228 binop( Iop_Shr32,
1229 unop(Iop_8Uto32, getCR321(n)),
sewardjc7cd2142005-09-09 22:31:49 +00001230 mkU8(toUChar(3-off)) ),
sewardjb51f0f42005-07-18 11:38:02 +00001231 mkU32(1) );
1232 }
cerion91ad5362005-01-27 23:02:41 +00001233}
1234
sewardjb51f0f42005-07-18 11:38:02 +00001235/* Dually, write the least significant bit of BIT to the specified CR
1236 bit. Indexing as per getCRbit. */
1237static void putCRbit ( UInt bi, IRExpr* bit )
1238{
sewardj197bd172005-10-12 11:34:33 +00001239 UInt n, off;
sewardjb51f0f42005-07-18 11:38:02 +00001240 IRExpr* safe;
1241 vassert(typeOfIRExpr(irbb->tyenv,bit) == Ity_I32);
1242 safe = binop(Iop_And32, bit, mkU32(1));
sewardj197bd172005-10-12 11:34:33 +00001243 n = bi / 4;
1244 off = bi % 4;
sewardjb51f0f42005-07-18 11:38:02 +00001245 vassert(bi < 32);
1246 if (off == 3) {
1247 /* This is the SO bit for this CR field */
1248 putCR0(n, unop(Iop_32to8, safe));
1249 } else {
1250 off = 3 - off;
1251 vassert(off == 1 || off == 2 || off == 3);
1252 putCR321(
1253 n,
1254 unop( Iop_32to8,
1255 binop( Iop_Or32,
1256 /* old value with field masked out */
1257 binop(Iop_And32, unop(Iop_8Uto32, getCR321(n)),
1258 mkU32(~(1 << off))),
1259 /* new value in the right place */
sewardjc7cd2142005-09-09 22:31:49 +00001260 binop(Iop_Shl32, safe, mkU8(toUChar(off)))
sewardjb51f0f42005-07-18 11:38:02 +00001261 )
1262 )
1263 );
1264 }
1265}
1266
sewardjb51f0f42005-07-18 11:38:02 +00001267/* Fetch the specified CR bit (as per IBM/hardware notation) and
1268 return it somewhere in an I32; it does not matter where, but
1269 whichever bit it is, all other bits are guaranteed to be zero. In
1270 other words, the I32-typed expression will be zero if the bit is
1271 zero and nonzero if the bit is 1. Write into *where the index
1272 of where the bit will be. */
1273
cerion5b2325f2005-12-23 00:55:09 +00001274static
1275IRExpr* /* :: Ity_I32 */ getCRbit_anywhere ( UInt bi, Int* where )
sewardjb51f0f42005-07-18 11:38:02 +00001276{
1277 UInt n = bi / 4;
1278 UInt off = bi % 4;
1279 vassert(bi < 32);
1280 if (off == 3) {
1281 /* Fetch the SO bit for this CR field */
1282 /* Note: And32 is redundant paranoia iff guest state only has 0
1283 or 1 in that slot. */
1284 *where = 0;
1285 return binop(Iop_And32, unop(Iop_8Uto32, getCR0(n)), mkU32(1));
1286 } else {
1287 /* Fetch the <, > or == bit for this CR field */
1288 *where = 3-off;
1289 return binop( Iop_And32,
1290 unop(Iop_8Uto32, getCR321(n)),
1291 mkU32(1 << (3-off)) );
1292 }
1293}
1294
sewardjb51f0f42005-07-18 11:38:02 +00001295/* Set the CR0 flags following an arithmetic operation.
1296 (Condition Register CR0 Field Definition, PPC32 p60)
cerion896a1372005-01-25 12:24:25 +00001297*/
cerionedf7fc52005-11-18 20:57:41 +00001298static IRExpr* getXER_SO ( void );
sewardj20ef5472005-07-21 14:48:31 +00001299static void set_CR0 ( IRExpr* result )
cerion896a1372005-01-25 12:24:25 +00001300{
ceriond953ebb2005-11-29 13:27:20 +00001301 vassert(typeOfIRExpr(irbb->tyenv,result) == Ity_I32 ||
1302 typeOfIRExpr(irbb->tyenv,result) == Ity_I64);
1303 if (mode64) {
ceriond953ebb2005-11-29 13:27:20 +00001304 putCR321( 0, unop(Iop_64to8,
cerion2831b002005-11-30 19:55:22 +00001305 binop(Iop_CmpORD64S, result, mkU64(0))) );
ceriond953ebb2005-11-29 13:27:20 +00001306 } else {
1307 putCR321( 0, unop(Iop_32to8,
1308 binop(Iop_CmpORD32S, result, mkU32(0))) );
1309 }
sewardjb51f0f42005-07-18 11:38:02 +00001310 putCR0( 0, getXER_SO() );
cerion896a1372005-01-25 12:24:25 +00001311}
cerion896a1372005-01-25 12:24:25 +00001312
sewardj20ef5472005-07-21 14:48:31 +00001313
cerionedf7fc52005-11-18 20:57:41 +00001314/* Set the CR6 flags following an AltiVec compare operation. */
1315static void set_AV_CR6 ( IRExpr* result, Bool test_all_ones )
1316{
1317 /* CR6[0:3] = {all_ones, 0, all_zeros, 0}
1318 all_ones = (v[0] && v[1] && v[2] && v[3])
1319 all_zeros = ~(v[0] || v[1] || v[2] || v[3])
1320 */
1321 IRTemp v0 = newTemp(Ity_V128);
1322 IRTemp v1 = newTemp(Ity_V128);
1323 IRTemp v2 = newTemp(Ity_V128);
1324 IRTemp v3 = newTemp(Ity_V128);
1325 IRTemp rOnes = newTemp(Ity_I8);
1326 IRTemp rZeros = newTemp(Ity_I8);
1327
1328 vassert(typeOfIRExpr(irbb->tyenv,result) == Ity_V128);
1329
1330 assign( v0, result );
1331 assign( v1, binop(Iop_ShrV128, result, mkU8(32)) );
1332 assign( v2, binop(Iop_ShrV128, result, mkU8(64)) );
1333 assign( v3, binop(Iop_ShrV128, result, mkU8(96)) );
1334
1335 assign( rZeros, unop(Iop_1Uto8,
1336 binop(Iop_CmpEQ32, mkU32(0xFFFFFFFF),
1337 unop(Iop_Not32,
1338 unop(Iop_V128to32,
1339 binop(Iop_OrV128,
1340 binop(Iop_OrV128, mkexpr(v0), mkexpr(v1)),
1341 binop(Iop_OrV128, mkexpr(v2), mkexpr(v3))))
1342 ))) );
1343
1344 if (test_all_ones) {
1345 assign( rOnes, unop(Iop_1Uto8,
1346 binop(Iop_CmpEQ32, mkU32(0xFFFFFFFF),
1347 unop(Iop_V128to32,
1348 binop(Iop_AndV128,
1349 binop(Iop_AndV128, mkexpr(v0), mkexpr(v1)),
cerion5b2325f2005-12-23 00:55:09 +00001350 binop(Iop_AndV128, mkexpr(v2), mkexpr(v3)))
1351 ))) );
cerionedf7fc52005-11-18 20:57:41 +00001352 putCR321( 6, binop(Iop_Or8,
1353 binop(Iop_Shl8, mkexpr(rOnes), mkU8(3)),
1354 binop(Iop_Shl8, mkexpr(rZeros), mkU8(1))) );
1355 } else {
1356 putCR321( 6, binop(Iop_Shl8, mkexpr(rZeros), mkU8(1)) );
1357 }
1358 putCR0( 6, mkU8(0) );
1359}
1360
1361
1362
1363/*------------------------------------------------------------*/
1364/*--- Helpers for XER flags. ---*/
1365/*------------------------------------------------------------*/
1366
1367static void putXER_SO ( IRExpr* e )
1368{
1369 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_I8);
cerionbb01b7c2005-12-16 13:40:18 +00001370 IRExpr* so = binop(Iop_And8, e, mkU8(1));
cerion5b2325f2005-12-23 00:55:09 +00001371 stmt( IRStmt_Put( OFFB_XER_SO, so ) );
cerionedf7fc52005-11-18 20:57:41 +00001372}
1373
1374static void putXER_OV ( IRExpr* e )
1375{
1376 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_I8);
cerionbb01b7c2005-12-16 13:40:18 +00001377 IRExpr* ov = binop(Iop_And8, e, mkU8(1));
cerion5b2325f2005-12-23 00:55:09 +00001378 stmt( IRStmt_Put( OFFB_XER_OV, ov ) );
cerionedf7fc52005-11-18 20:57:41 +00001379}
1380
1381static void putXER_CA ( IRExpr* e )
1382{
1383 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_I8);
cerionbb01b7c2005-12-16 13:40:18 +00001384 IRExpr* ca = binop(Iop_And8, e, mkU8(1));
cerion5b2325f2005-12-23 00:55:09 +00001385 stmt( IRStmt_Put( OFFB_XER_CA, ca ) );
cerionedf7fc52005-11-18 20:57:41 +00001386}
1387
1388static void putXER_BC ( IRExpr* e )
1389{
1390 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_I8);
cerionbb01b7c2005-12-16 13:40:18 +00001391 IRExpr* bc = binop(Iop_And8, e, mkU8(0x7F));
cerion5b2325f2005-12-23 00:55:09 +00001392 stmt( IRStmt_Put( OFFB_XER_BC, bc ) );
cerionedf7fc52005-11-18 20:57:41 +00001393}
1394
1395static IRExpr* /* :: Ity_I8 */ getXER_SO ( void )
1396{
cerion5b2325f2005-12-23 00:55:09 +00001397 return IRExpr_Get( OFFB_XER_SO, Ity_I8 );
cerionedf7fc52005-11-18 20:57:41 +00001398}
1399
1400static IRExpr* /* :: Ity_I32 */ getXER_SO32 ( void )
1401{
1402 return binop( Iop_And32, unop(Iop_8Uto32, getXER_SO()), mkU32(1) );
1403}
1404
1405static IRExpr* /* :: Ity_I8 */ getXER_OV ( void )
1406{
cerion5b2325f2005-12-23 00:55:09 +00001407 return IRExpr_Get( OFFB_XER_OV, Ity_I8 );
cerionedf7fc52005-11-18 20:57:41 +00001408}
1409
1410static IRExpr* /* :: Ity_I32 */ getXER_OV32 ( void )
1411{
1412 return binop( Iop_And32, unop(Iop_8Uto32, getXER_OV()), mkU32(1) );
1413}
1414
1415static IRExpr* /* :: Ity_I32 */ getXER_CA32 ( void )
1416{
cerion5b2325f2005-12-23 00:55:09 +00001417 IRExpr* ca = IRExpr_Get( OFFB_XER_CA, Ity_I8 );
ceriond953ebb2005-11-29 13:27:20 +00001418 return binop( Iop_And32, unop(Iop_8Uto32, ca ), mkU32(1) );
cerionedf7fc52005-11-18 20:57:41 +00001419}
1420
1421static IRExpr* /* :: Ity_I8 */ getXER_BC ( void )
1422{
cerion5b2325f2005-12-23 00:55:09 +00001423 return IRExpr_Get( OFFB_XER_BC, Ity_I8 );
cerionedf7fc52005-11-18 20:57:41 +00001424}
1425
1426static IRExpr* /* :: Ity_I32 */ getXER_BC32 ( void )
1427{
cerion5b2325f2005-12-23 00:55:09 +00001428 IRExpr* bc = IRExpr_Get( OFFB_XER_BC, Ity_I8 );
ceriond953ebb2005-11-29 13:27:20 +00001429 return binop( Iop_And32, unop(Iop_8Uto32, bc), mkU32(0x7F) );
cerionedf7fc52005-11-18 20:57:41 +00001430}
1431
1432
sewardj20ef5472005-07-21 14:48:31 +00001433/* RES is the result of doing OP on ARGL and ARGR. Set %XER.OV and
1434 %XER.SO accordingly. */
1435
ceriond953ebb2005-11-29 13:27:20 +00001436static void set_XER_OV_32( UInt op, IRExpr* res,
1437 IRExpr* argL, IRExpr* argR )
sewardj20ef5472005-07-21 14:48:31 +00001438{
1439 IRTemp t64;
1440 IRExpr* xer_ov;
cerion5b2325f2005-12-23 00:55:09 +00001441 vassert(op < PPCG_FLAG_OP_NUMBER);
ceriond953ebb2005-11-29 13:27:20 +00001442 vassert(typeOfIRExpr(irbb->tyenv,res) == Ity_I32);
sewardj20ef5472005-07-21 14:48:31 +00001443 vassert(typeOfIRExpr(irbb->tyenv,argL) == Ity_I32);
1444 vassert(typeOfIRExpr(irbb->tyenv,argR) == Ity_I32);
1445
1446# define INT32_MIN 0x80000000
1447
1448# define XOR2(_aa,_bb) \
1449 binop(Iop_Xor32,(_aa),(_bb))
1450
1451# define XOR3(_cc,_dd,_ee) \
1452 binop(Iop_Xor32,binop(Iop_Xor32,(_cc),(_dd)),(_ee))
1453
1454# define AND3(_ff,_gg,_hh) \
1455 binop(Iop_And32,binop(Iop_And32,(_ff),(_gg)),(_hh))
1456
1457#define NOT(_jj) \
1458 unop(Iop_Not32, (_jj))
1459
1460 switch (op) {
cerion5b2325f2005-12-23 00:55:09 +00001461 case /* 0 */ PPCG_FLAG_OP_ADD:
1462 case /* 1 */ PPCG_FLAG_OP_ADDE:
ceriond953ebb2005-11-29 13:27:20 +00001463 /* (argL^argR^-1) & (argL^res) & (1<<31) ?1:0 */
1464 // i.e. ((both_same_sign) & (sign_changed) & (sign_mask))
1465 xer_ov
1466 = AND3( XOR3(argL,argR,mkU32(-1)),
1467 XOR2(argL,res),
1468 mkU32(INT32_MIN) );
1469 /* xer_ov can only be 0 or 1<<31 */
1470 xer_ov
1471 = binop(Iop_Shr32, xer_ov, mkU8(31) );
1472 break;
1473
cerion5b2325f2005-12-23 00:55:09 +00001474 case /* 2 */ PPCG_FLAG_OP_DIVW:
ceriond953ebb2005-11-29 13:27:20 +00001475 /* (argL == INT32_MIN && argR == -1) || argR == 0 */
1476 xer_ov
1477 = mkOR1(
1478 mkAND1(
1479 binop(Iop_CmpEQ32, argL, mkU32(INT32_MIN)),
1480 binop(Iop_CmpEQ32, argR, mkU32(-1))
1481 ),
1482 binop(Iop_CmpEQ32, argR, mkU32(0) )
1483 );
1484 xer_ov
1485 = unop(Iop_1Uto32, xer_ov);
1486 break;
1487
cerion5b2325f2005-12-23 00:55:09 +00001488 case /* 3 */ PPCG_FLAG_OP_DIVWU:
ceriond953ebb2005-11-29 13:27:20 +00001489 /* argR == 0 */
1490 xer_ov
1491 = unop(Iop_1Uto32, binop(Iop_CmpEQ32, argR, mkU32(0)));
1492 break;
1493
cerion5b2325f2005-12-23 00:55:09 +00001494 case /* 4 */ PPCG_FLAG_OP_MULLW:
ceriond953ebb2005-11-29 13:27:20 +00001495 /* OV true if result can't be represented in 32 bits
1496 i.e sHi != sign extension of sLo */
1497 t64 = newTemp(Ity_I64);
1498 assign( t64, binop(Iop_MullS32, argL, argR) );
1499 xer_ov
1500 = binop( Iop_CmpNE32,
1501 unop(Iop_64HIto32, mkexpr(t64)),
1502 binop( Iop_Sar32,
1503 unop(Iop_64to32, mkexpr(t64)),
1504 mkU8(31))
1505 );
1506 xer_ov
1507 = unop(Iop_1Uto32, xer_ov);
1508 break;
1509
cerion5b2325f2005-12-23 00:55:09 +00001510 case /* 5 */ PPCG_FLAG_OP_NEG:
ceriond953ebb2005-11-29 13:27:20 +00001511 /* argL == INT32_MIN */
1512 xer_ov
1513 = unop( Iop_1Uto32,
1514 binop(Iop_CmpEQ32, argL, mkU32(INT32_MIN)) );
1515 break;
1516
cerion5b2325f2005-12-23 00:55:09 +00001517 case /* 6 */ PPCG_FLAG_OP_SUBF:
1518 case /* 7 */ PPCG_FLAG_OP_SUBFC:
1519 case /* 8 */ PPCG_FLAG_OP_SUBFE:
ceriond953ebb2005-11-29 13:27:20 +00001520 /* ((~argL)^argR^-1) & ((~argL)^res) & (1<<31) ?1:0; */
1521 xer_ov
1522 = AND3( XOR3(NOT(argL),argR,mkU32(-1)),
1523 XOR2(NOT(argL),res),
1524 mkU32(INT32_MIN) );
1525 /* xer_ov can only be 0 or 1<<31 */
1526 xer_ov
1527 = binop(Iop_Shr32, xer_ov, mkU8(31) );
1528 break;
1529
1530 default:
1531 vex_printf("set_XER_OV: op = %u\n", op);
cerion5b2325f2005-12-23 00:55:09 +00001532 vpanic("set_XER_OV(ppc)");
sewardj20ef5472005-07-21 14:48:31 +00001533 }
ceriond953ebb2005-11-29 13:27:20 +00001534
sewardj20ef5472005-07-21 14:48:31 +00001535 /* xer_ov MUST denote either 0 or 1, no other value allowed */
ceriond953ebb2005-11-29 13:27:20 +00001536 putXER_OV( unop(Iop_32to8, xer_ov) );
sewardj20ef5472005-07-21 14:48:31 +00001537
1538 /* Update the summary overflow */
cerionedf7fc52005-11-18 20:57:41 +00001539 putXER_SO( binop(Iop_Or8, getXER_SO(), getXER_OV()) );
sewardj20ef5472005-07-21 14:48:31 +00001540
1541# undef INT32_MIN
1542# undef AND3
1543# undef XOR3
1544# undef XOR2
1545# undef NOT
1546}
1547
ceriond953ebb2005-11-29 13:27:20 +00001548static void set_XER_OV_64( UInt op, IRExpr* res,
1549 IRExpr* argL, IRExpr* argR )
1550{
1551 IRExpr* xer_ov;
cerion5b2325f2005-12-23 00:55:09 +00001552 vassert(op < PPCG_FLAG_OP_NUMBER);
ceriond953ebb2005-11-29 13:27:20 +00001553 vassert(typeOfIRExpr(irbb->tyenv,res) == Ity_I64);
1554 vassert(typeOfIRExpr(irbb->tyenv,argL) == Ity_I64);
1555 vassert(typeOfIRExpr(irbb->tyenv,argR) == Ity_I64);
1556
cerion2831b002005-11-30 19:55:22 +00001557# define INT64_MIN 0x8000000000000000ULL
ceriond953ebb2005-11-29 13:27:20 +00001558
1559# define XOR2(_aa,_bb) \
1560 binop(Iop_Xor64,(_aa),(_bb))
1561
1562# define XOR3(_cc,_dd,_ee) \
1563 binop(Iop_Xor64,binop(Iop_Xor64,(_cc),(_dd)),(_ee))
1564
1565# define AND3(_ff,_gg,_hh) \
1566 binop(Iop_And64,binop(Iop_And64,(_ff),(_gg)),(_hh))
1567
1568#define NOT(_jj) \
1569 unop(Iop_Not64, (_jj))
1570
1571 switch (op) {
cerion5b2325f2005-12-23 00:55:09 +00001572 case /* 0 */ PPCG_FLAG_OP_ADD:
1573 case /* 1 */ PPCG_FLAG_OP_ADDE:
ceriond953ebb2005-11-29 13:27:20 +00001574 /* (argL^argR^-1) & (argL^res) & (1<<63) ? 1:0 */
1575 // i.e. ((both_same_sign) & (sign_changed) & (sign_mask))
1576 xer_ov
1577 = AND3( XOR3(argL,argR,mkU64(-1)),
1578 XOR2(argL,res),
1579 mkU64(INT64_MIN) );
1580 /* xer_ov can only be 0 or 1<<63 */
1581 xer_ov
1582 = unop(Iop_64to1, binop(Iop_Shr64, xer_ov, mkU8(63)));
1583 break;
1584
cerion5b2325f2005-12-23 00:55:09 +00001585 case /* 2 */ PPCG_FLAG_OP_DIVW:
ceriond953ebb2005-11-29 13:27:20 +00001586 /* (argL == INT64_MIN && argR == -1) || argR == 0 */
1587 xer_ov
1588 = mkOR1(
1589 mkAND1(
1590 binop(Iop_CmpEQ64, argL, mkU64(INT64_MIN)),
1591 binop(Iop_CmpEQ64, argR, mkU64(-1))
1592 ),
1593 binop(Iop_CmpEQ64, argR, mkU64(0) )
1594 );
1595 break;
1596
cerion5b2325f2005-12-23 00:55:09 +00001597 case /* 3 */ PPCG_FLAG_OP_DIVWU:
ceriond953ebb2005-11-29 13:27:20 +00001598 /* argR == 0 */
1599 xer_ov
1600 = binop(Iop_CmpEQ64, argR, mkU64(0));
1601 break;
1602
cerion5b2325f2005-12-23 00:55:09 +00001603 case /* 4 */ PPCG_FLAG_OP_MULLW: {
ceriond953ebb2005-11-29 13:27:20 +00001604 /* OV true if result can't be represented in 64 bits
1605 i.e sHi != sign extension of sLo */
ceriond953ebb2005-11-29 13:27:20 +00001606 xer_ov
cerionbb01b7c2005-12-16 13:40:18 +00001607 = binop( Iop_CmpNE32,
1608 unop(Iop_64HIto32, res),
1609 binop( Iop_Sar32,
1610 unop(Iop_64to32, res),
1611 mkU8(31))
1612 );
ceriond953ebb2005-11-29 13:27:20 +00001613 break;
1614 }
1615
cerion5b2325f2005-12-23 00:55:09 +00001616 case /* 5 */ PPCG_FLAG_OP_NEG:
ceriond953ebb2005-11-29 13:27:20 +00001617 /* argL == INT64_MIN */
1618 xer_ov
1619 = binop(Iop_CmpEQ64, argL, mkU64(INT64_MIN));
1620 break;
1621
cerion5b2325f2005-12-23 00:55:09 +00001622 case /* 6 */ PPCG_FLAG_OP_SUBF:
1623 case /* 7 */ PPCG_FLAG_OP_SUBFC:
1624 case /* 8 */ PPCG_FLAG_OP_SUBFE:
ceriond953ebb2005-11-29 13:27:20 +00001625 /* ((~argL)^argR^-1) & ((~argL)^res) & (1<<63) ?1:0; */
1626 xer_ov
1627 = AND3( XOR3(NOT(argL),argR,mkU64(-1)),
1628 XOR2(NOT(argL),res),
1629 mkU64(INT64_MIN) );
1630 /* xer_ov can only be 0 or 1<<63 */
1631 xer_ov
1632 = unop(Iop_64to1, binop(Iop_Shr64, xer_ov, mkU8(63)));
1633 break;
1634
1635 default:
1636 vex_printf("set_XER_OV: op = %u\n", op);
cerion5b2325f2005-12-23 00:55:09 +00001637 vpanic("set_XER_OV(ppc64)");
ceriond953ebb2005-11-29 13:27:20 +00001638 }
1639
1640 /* xer_ov MUST denote either 0 or 1, no other value allowed */
1641 putXER_OV( unop(Iop_1Uto8, xer_ov) );
1642
1643 /* Update the summary overflow */
1644 putXER_SO( binop(Iop_Or8, getXER_SO(), getXER_OV()) );
1645
1646# undef INT64_MIN
1647# undef AND3
1648# undef XOR3
1649# undef XOR2
1650# undef NOT
1651}
1652
1653static void set_XER_OV ( IRType ty, UInt op, IRExpr* res,
1654 IRExpr* argL, IRExpr* argR )
1655{
1656 if (ty == Ity_I32)
1657 set_XER_OV_32( op, res, argL, argR );
1658 else
1659 set_XER_OV_64( op, res, argL, argR );
1660}
1661
1662
1663
sewardjb51f0f42005-07-18 11:38:02 +00001664/* RES is the result of doing OP on ARGL and ARGR with the old %XER.CA
1665 value being OLDCA. Set %XER.CA accordingly. */
cerione9d361a2005-03-04 17:35:29 +00001666
cerion2831b002005-11-30 19:55:22 +00001667static void set_XER_CA_32 ( UInt op, IRExpr* res,
1668 IRExpr* argL, IRExpr* argR, IRExpr* oldca )
cerion38674602005-02-08 02:19:25 +00001669{
sewardj9a036bf2005-03-14 18:19:08 +00001670 IRExpr* xer_ca;
cerion5b2325f2005-12-23 00:55:09 +00001671 vassert(op < PPCG_FLAG_OP_NUMBER);
sewardjb51f0f42005-07-18 11:38:02 +00001672 vassert(typeOfIRExpr(irbb->tyenv,res) == Ity_I32);
1673 vassert(typeOfIRExpr(irbb->tyenv,argL) == Ity_I32);
1674 vassert(typeOfIRExpr(irbb->tyenv,argR) == Ity_I32);
1675 vassert(typeOfIRExpr(irbb->tyenv,oldca) == Ity_I32);
cerion70e24122005-03-16 00:27:37 +00001676
sewardj20ef5472005-07-21 14:48:31 +00001677 /* Incoming oldca is assumed to hold the values 0 or 1 only. This
1678 seems reasonable given that it's always generated by
cerionedf7fc52005-11-18 20:57:41 +00001679 getXER_CA32(), which masks it accordingly. In any case it being
cerion75949202005-12-24 13:14:11 +00001680 0 or 1 is an invariant of the ppc guest state representation;
sewardj20ef5472005-07-21 14:48:31 +00001681 if it has any other value, that invariant has been violated. */
cerione9d361a2005-03-04 17:35:29 +00001682
sewardj20ef5472005-07-21 14:48:31 +00001683 switch (op) {
cerion5b2325f2005-12-23 00:55:09 +00001684 case /* 0 */ PPCG_FLAG_OP_ADD:
ceriond953ebb2005-11-29 13:27:20 +00001685 /* res <u argL */
1686 xer_ca
1687 = unop(Iop_1Uto32, binop(Iop_CmpLT32U, res, argL));
1688 break;
1689
cerion5b2325f2005-12-23 00:55:09 +00001690 case /* 1 */ PPCG_FLAG_OP_ADDE:
ceriond953ebb2005-11-29 13:27:20 +00001691 /* res <u argL || (old_ca==1 && res==argL) */
1692 xer_ca
1693 = mkOR1(
1694 binop(Iop_CmpLT32U, res, argL),
1695 mkAND1(
1696 binop(Iop_CmpEQ32, oldca, mkU32(1)),
1697 binop(Iop_CmpEQ32, res, argL)
1698 )
1699 );
1700 xer_ca
1701 = unop(Iop_1Uto32, xer_ca);
1702 break;
1703
cerion5b2325f2005-12-23 00:55:09 +00001704 case /* 8 */ PPCG_FLAG_OP_SUBFE:
ceriond953ebb2005-11-29 13:27:20 +00001705 /* res <u argR || (old_ca==1 && res==argR) */
1706 xer_ca
1707 = mkOR1(
1708 binop(Iop_CmpLT32U, res, argR),
1709 mkAND1(
1710 binop(Iop_CmpEQ32, oldca, mkU32(1)),
1711 binop(Iop_CmpEQ32, res, argR)
1712 )
1713 );
1714 xer_ca
1715 = unop(Iop_1Uto32, xer_ca);
1716 break;
1717
cerion5b2325f2005-12-23 00:55:09 +00001718 case /* 7 */ PPCG_FLAG_OP_SUBFC:
1719 case /* 9 */ PPCG_FLAG_OP_SUBFI:
ceriond953ebb2005-11-29 13:27:20 +00001720 /* res <=u argR */
1721 xer_ca
1722 = unop(Iop_1Uto32, binop(Iop_CmpLE32U, res, argR));
1723 break;
1724
cerion5b2325f2005-12-23 00:55:09 +00001725 case /* 10 */ PPCG_FLAG_OP_SRAW:
ceriond953ebb2005-11-29 13:27:20 +00001726 /* The shift amount is guaranteed to be in 0 .. 63 inclusive.
1727 If it is <= 31, behave like SRAWI; else XER.CA is the sign
1728 bit of argL. */
1729 /* This term valid for shift amount < 32 only */
1730 xer_ca
1731 = binop(
1732 Iop_And32,
1733 binop(Iop_Sar32, argL, mkU8(31)),
1734 binop( Iop_And32,
1735 argL,
1736 binop( Iop_Sub32,
cerion5b2325f2005-12-23 00:55:09 +00001737 binop(Iop_Shl32, mkU32(1),
1738 unop(Iop_32to8,argR)),
ceriond953ebb2005-11-29 13:27:20 +00001739 mkU32(1) )
1740 )
sewardj20ef5472005-07-21 14:48:31 +00001741 );
ceriond953ebb2005-11-29 13:27:20 +00001742 xer_ca
1743 = IRExpr_Mux0X(
1744 /* shift amt > 31 ? */
1745 unop(Iop_1Uto8, binop(Iop_CmpLT32U, mkU32(31), argR)),
1746 /* no -- be like srawi */
1747 unop(Iop_1Uto32, binop(Iop_CmpNE32, xer_ca, mkU32(0))),
1748 /* yes -- get sign bit of argL */
1749 binop(Iop_Shr32, argL, mkU8(31))
1750 );
1751 break;
sewardj20ef5472005-07-21 14:48:31 +00001752
cerion5b2325f2005-12-23 00:55:09 +00001753 case /* 11 */ PPCG_FLAG_OP_SRAWI:
ceriond953ebb2005-11-29 13:27:20 +00001754 /* xer_ca is 1 iff src was negative and bits_shifted_out !=
1755 0. Since the shift amount is known to be in the range
1756 0 .. 31 inclusive the following seems viable:
1757 xer.ca == 1 iff the following is nonzero:
1758 (argL >>s 31) -- either all 0s or all 1s
1759 & (argL & (1<<argR)-1) -- the stuff shifted out */
1760 xer_ca
1761 = binop(
1762 Iop_And32,
1763 binop(Iop_Sar32, argL, mkU8(31)),
1764 binop( Iop_And32,
1765 argL,
1766 binop( Iop_Sub32,
cerion5b2325f2005-12-23 00:55:09 +00001767 binop(Iop_Shl32, mkU32(1),
1768 unop(Iop_32to8,argR)),
ceriond953ebb2005-11-29 13:27:20 +00001769 mkU32(1) )
1770 )
sewardj20ef5472005-07-21 14:48:31 +00001771 );
ceriond953ebb2005-11-29 13:27:20 +00001772 xer_ca
1773 = unop(Iop_1Uto32, binop(Iop_CmpNE32, xer_ca, mkU32(0)));
1774 break;
1775
1776 default:
1777 vex_printf("set_XER_CA: op = %u\n", op);
cerion5b2325f2005-12-23 00:55:09 +00001778 vpanic("set_XER_CA(ppc)");
sewardj20ef5472005-07-21 14:48:31 +00001779 }
1780
1781 /* xer_ca MUST denote either 0 or 1, no other value allowed */
cerionedf7fc52005-11-18 20:57:41 +00001782 putXER_CA( unop(Iop_32to8, xer_ca) );
sewardjb51f0f42005-07-18 11:38:02 +00001783}
1784
cerion2831b002005-11-30 19:55:22 +00001785static void set_XER_CA_64 ( UInt op, IRExpr* res,
1786 IRExpr* argL, IRExpr* argR, IRExpr* oldca )
sewardje14bb9f2005-07-22 09:39:02 +00001787{
ceriond953ebb2005-11-29 13:27:20 +00001788 IRExpr* xer_ca;
cerion5b2325f2005-12-23 00:55:09 +00001789 vassert(op < PPCG_FLAG_OP_NUMBER);
ceriond953ebb2005-11-29 13:27:20 +00001790 vassert(typeOfIRExpr(irbb->tyenv,res) == Ity_I64);
1791 vassert(typeOfIRExpr(irbb->tyenv,argL) == Ity_I64);
1792 vassert(typeOfIRExpr(irbb->tyenv,argR) == Ity_I64);
1793 vassert(typeOfIRExpr(irbb->tyenv,oldca) == Ity_I64);
sewardje14bb9f2005-07-22 09:39:02 +00001794
ceriond953ebb2005-11-29 13:27:20 +00001795 /* Incoming oldca is assumed to hold the values 0 or 1 only. This
1796 seems reasonable given that it's always generated by
1797 getXER_CA32(), which masks it accordingly. In any case it being
cerion75949202005-12-24 13:14:11 +00001798 0 or 1 is an invariant of the ppc guest state representation;
ceriond953ebb2005-11-29 13:27:20 +00001799 if it has any other value, that invariant has been violated. */
1800
1801 switch (op) {
cerion5b2325f2005-12-23 00:55:09 +00001802 case /* 0 */ PPCG_FLAG_OP_ADD:
ceriond953ebb2005-11-29 13:27:20 +00001803 /* res <u argL */
1804 xer_ca
cerionf0de28c2005-12-13 20:21:11 +00001805 = unop(Iop_1Uto32, binop(Iop_CmpLT64U, res, argL));
ceriond953ebb2005-11-29 13:27:20 +00001806 break;
sewardje14bb9f2005-07-22 09:39:02 +00001807
cerion5b2325f2005-12-23 00:55:09 +00001808 case /* 1 */ PPCG_FLAG_OP_ADDE:
ceriond953ebb2005-11-29 13:27:20 +00001809 /* res <u argL || (old_ca==1 && res==argL) */
1810 xer_ca
1811 = mkOR1(
1812 binop(Iop_CmpLT64U, res, argL),
1813 mkAND1(
cerionf0de28c2005-12-13 20:21:11 +00001814 binop(Iop_CmpEQ64, oldca, mkU64(1)),
ceriond953ebb2005-11-29 13:27:20 +00001815 binop(Iop_CmpEQ64, res, argL)
1816 )
1817 );
cerionf0de28c2005-12-13 20:21:11 +00001818 xer_ca
1819 = unop(Iop_1Uto32, xer_ca);
cerionedf7fc52005-11-18 20:57:41 +00001820 break;
ceriond953ebb2005-11-29 13:27:20 +00001821
cerion5b2325f2005-12-23 00:55:09 +00001822 case /* 8 */ PPCG_FLAG_OP_SUBFE:
ceriond953ebb2005-11-29 13:27:20 +00001823 /* res <u argR || (old_ca==1 && res==argR) */
1824 xer_ca
1825 = mkOR1(
1826 binop(Iop_CmpLT64U, res, argR),
1827 mkAND1(
1828 binop(Iop_CmpEQ64, oldca, mkU64(1)),
1829 binop(Iop_CmpEQ64, res, argR)
1830 )
1831 );
cerionf0de28c2005-12-13 20:21:11 +00001832 xer_ca
1833 = unop(Iop_1Uto32, xer_ca);
ceriond953ebb2005-11-29 13:27:20 +00001834 break;
1835
cerion5b2325f2005-12-23 00:55:09 +00001836 case /* 7 */ PPCG_FLAG_OP_SUBFC:
1837 case /* 9 */ PPCG_FLAG_OP_SUBFI:
ceriond953ebb2005-11-29 13:27:20 +00001838 /* res <=u argR */
1839 xer_ca
cerionf0de28c2005-12-13 20:21:11 +00001840 = unop(Iop_1Uto32, binop(Iop_CmpLE64U, res, argR));
ceriond953ebb2005-11-29 13:27:20 +00001841 break;
1842
1843
cerion5b2325f2005-12-23 00:55:09 +00001844 case /* 10 */ PPCG_FLAG_OP_SRAW:
cerionf0de28c2005-12-13 20:21:11 +00001845 /* The shift amount is guaranteed to be in 0 .. 31 inclusive.
ceriond953ebb2005-11-29 13:27:20 +00001846 If it is <= 31, behave like SRAWI; else XER.CA is the sign
1847 bit of argL. */
cerionf0de28c2005-12-13 20:21:11 +00001848 /* This term valid for shift amount < 31 only */
1849
ceriond953ebb2005-11-29 13:27:20 +00001850 xer_ca
1851 = binop(
cerionf0de28c2005-12-13 20:21:11 +00001852 Iop_And64,
1853 binop(Iop_Sar64, argL, mkU8(31)),
1854 binop( Iop_And64,
ceriond953ebb2005-11-29 13:27:20 +00001855 argL,
cerionf0de28c2005-12-13 20:21:11 +00001856 binop( Iop_Sub64,
cerion5b2325f2005-12-23 00:55:09 +00001857 binop(Iop_Shl64, mkU64(1),
1858 unop(Iop_64to8,argR)),
cerionf0de28c2005-12-13 20:21:11 +00001859 mkU64(1) )
ceriond953ebb2005-11-29 13:27:20 +00001860 )
1861 );
1862 xer_ca
1863 = IRExpr_Mux0X(
1864 /* shift amt > 31 ? */
cerionf0de28c2005-12-13 20:21:11 +00001865 unop(Iop_1Uto8, binop(Iop_CmpLT64U, mkU64(31), argR)),
ceriond953ebb2005-11-29 13:27:20 +00001866 /* no -- be like srawi */
cerionf0de28c2005-12-13 20:21:11 +00001867 unop(Iop_1Uto32, binop(Iop_CmpNE64, xer_ca, mkU64(0))),
ceriond953ebb2005-11-29 13:27:20 +00001868 /* yes -- get sign bit of argL */
cerionf0de28c2005-12-13 20:21:11 +00001869 unop(Iop_64to32, binop(Iop_Shr64, argL, mkU8(63)))
ceriond953ebb2005-11-29 13:27:20 +00001870 );
1871 break;
1872
cerion5b2325f2005-12-23 00:55:09 +00001873 case /* 11 */ PPCG_FLAG_OP_SRAWI:
cerionf0de28c2005-12-13 20:21:11 +00001874 /* xer_ca is 1 iff src was negative and bits_shifted_out != 0.
1875 Since the shift amount is known to be in the range 0 .. 31
1876 inclusive the following seems viable:
ceriond953ebb2005-11-29 13:27:20 +00001877 xer.ca == 1 iff the following is nonzero:
1878 (argL >>s 31) -- either all 0s or all 1s
1879 & (argL & (1<<argR)-1) -- the stuff shifted out */
cerionf0de28c2005-12-13 20:21:11 +00001880
ceriond953ebb2005-11-29 13:27:20 +00001881 xer_ca
1882 = binop(
cerionf0de28c2005-12-13 20:21:11 +00001883 Iop_And64,
1884 binop(Iop_Sar64, argL, mkU8(31)),
1885 binop( Iop_And64,
ceriond953ebb2005-11-29 13:27:20 +00001886 argL,
cerionf0de28c2005-12-13 20:21:11 +00001887 binop( Iop_Sub64,
cerion5b2325f2005-12-23 00:55:09 +00001888 binop(Iop_Shl64, mkU64(1),
1889 unop(Iop_64to8,argR)),
cerionf0de28c2005-12-13 20:21:11 +00001890 mkU64(1) )
ceriond953ebb2005-11-29 13:27:20 +00001891 )
1892 );
1893 xer_ca
cerionf0de28c2005-12-13 20:21:11 +00001894 = unop(Iop_1Uto32, binop(Iop_CmpNE64, xer_ca, mkU64(0)));
ceriond953ebb2005-11-29 13:27:20 +00001895 break;
ceriond953ebb2005-11-29 13:27:20 +00001896
cerionf0de28c2005-12-13 20:21:11 +00001897
cerion5b2325f2005-12-23 00:55:09 +00001898 case /* 12 */ PPCG_FLAG_OP_SRAD:
cerionf0de28c2005-12-13 20:21:11 +00001899 /* The shift amount is guaranteed to be in 0 .. 63 inclusive.
1900 If it is <= 63, behave like SRADI; else XER.CA is the sign
1901 bit of argL. */
1902 /* This term valid for shift amount < 63 only */
1903
1904 xer_ca
1905 = binop(
1906 Iop_And64,
1907 binop(Iop_Sar64, argL, mkU8(63)),
1908 binop( Iop_And64,
1909 argL,
1910 binop( Iop_Sub64,
cerion5b2325f2005-12-23 00:55:09 +00001911 binop(Iop_Shl64, mkU64(1),
1912 unop(Iop_64to8,argR)),
cerionf0de28c2005-12-13 20:21:11 +00001913 mkU64(1) )
1914 )
1915 );
1916 xer_ca
1917 = IRExpr_Mux0X(
1918 /* shift amt > 63 ? */
1919 unop(Iop_1Uto8, binop(Iop_CmpLT64U, mkU64(63), argR)),
cerion07b07a92005-12-22 14:32:35 +00001920 /* no -- be like sradi */
1921 unop(Iop_1Uto32, binop(Iop_CmpNE64, xer_ca, mkU64(0))),
cerionf0de28c2005-12-13 20:21:11 +00001922 /* yes -- get sign bit of argL */
1923 unop(Iop_64to32, binop(Iop_Shr64, argL, mkU8(63)))
1924 );
1925 break;
1926
1927
cerion5b2325f2005-12-23 00:55:09 +00001928 case /* 13 */ PPCG_FLAG_OP_SRADI:
cerionf0de28c2005-12-13 20:21:11 +00001929 /* xer_ca is 1 iff src was negative and bits_shifted_out != 0.
1930 Since the shift amount is known to be in the range 0 .. 63
1931 inclusive, the following seems viable:
1932 xer.ca == 1 iff the following is nonzero:
1933 (argL >>s 63) -- either all 0s or all 1s
1934 & (argL & (1<<argR)-1) -- the stuff shifted out */
1935
1936 xer_ca
1937 = binop(
1938 Iop_And64,
1939 binop(Iop_Sar64, argL, mkU8(63)),
1940 binop( Iop_And64,
1941 argL,
1942 binop( Iop_Sub64,
cerion5b2325f2005-12-23 00:55:09 +00001943 binop(Iop_Shl64, mkU64(1),
1944 unop(Iop_64to8,argR)),
cerionf0de28c2005-12-13 20:21:11 +00001945 mkU64(1) )
1946 )
1947 );
1948 xer_ca
1949 = unop(Iop_1Uto32, binop(Iop_CmpNE64, xer_ca, mkU64(0)));
1950 break;
1951
ceriond953ebb2005-11-29 13:27:20 +00001952 default:
1953 vex_printf("set_XER_CA: op = %u\n", op);
cerionf0de28c2005-12-13 20:21:11 +00001954 vpanic("set_XER_CA(ppc64)");
sewardje14bb9f2005-07-22 09:39:02 +00001955 }
1956
ceriond953ebb2005-11-29 13:27:20 +00001957 /* xer_ca MUST denote either 0 or 1, no other value allowed */
cerionf0de28c2005-12-13 20:21:11 +00001958 putXER_CA( unop(Iop_32to8, xer_ca) );
sewardje14bb9f2005-07-22 09:39:02 +00001959}
1960
cerion2831b002005-11-30 19:55:22 +00001961static void set_XER_CA ( IRType ty, UInt op, IRExpr* res,
1962 IRExpr* argL, IRExpr* argR, IRExpr* oldca )
cerionedf7fc52005-11-18 20:57:41 +00001963{
cerion2831b002005-11-30 19:55:22 +00001964 if (ty == Ity_I32)
ceriond953ebb2005-11-29 13:27:20 +00001965 set_XER_CA_32( op, res, argL, argR, oldca );
1966 else
1967 set_XER_CA_64( op, res, argL, argR, oldca );
cerionedf7fc52005-11-18 20:57:41 +00001968}
1969
ceriond953ebb2005-11-29 13:27:20 +00001970
1971
1972/*------------------------------------------------------------*/
1973/*--- Read/write to guest-state --- */
1974/*------------------------------------------------------------*/
1975
cerionf0de28c2005-12-13 20:21:11 +00001976static IRExpr* /* :: Ity_I32/64 */ getGST ( PPC_GST reg )
cerionedf7fc52005-11-18 20:57:41 +00001977{
ceriond953ebb2005-11-29 13:27:20 +00001978 IRType ty = mode64 ? Ity_I64 : Ity_I32;
cerionedf7fc52005-11-18 20:57:41 +00001979 switch (reg) {
ceriond953ebb2005-11-29 13:27:20 +00001980 case PPC_GST_LR:
cerion5b2325f2005-12-23 00:55:09 +00001981 return IRExpr_Get( OFFB_LR, ty );
ceriond953ebb2005-11-29 13:27:20 +00001982
1983 case PPC_GST_CTR:
cerion5b2325f2005-12-23 00:55:09 +00001984 return IRExpr_Get( OFFB_CTR, ty );
ceriond953ebb2005-11-29 13:27:20 +00001985
1986 case PPC_GST_VRSAVE:
cerion5b2325f2005-12-23 00:55:09 +00001987 return IRExpr_Get( OFFB_VRSAVE, Ity_I32 );
ceriond953ebb2005-11-29 13:27:20 +00001988
1989 case PPC_GST_VSCR:
cerion5b2325f2005-12-23 00:55:09 +00001990 return binop(Iop_And32, IRExpr_Get( OFFB_VSCR,Ity_I32 ),
1991 mkU32(MASK_VSCR_VALID));
ceriond953ebb2005-11-29 13:27:20 +00001992
1993 case PPC_GST_CR: {
cerionedf7fc52005-11-18 20:57:41 +00001994 /* Synthesise the entire CR into a single word. Expensive. */
1995# define FIELD(_n) \
1996 binop(Iop_Shl32, \
1997 unop(Iop_8Uto32, \
1998 binop(Iop_Or8, \
1999 binop(Iop_And8, getCR321(_n), mkU8(7<<1)), \
2000 binop(Iop_And8, getCR0(_n), mkU8(1)) \
2001 ) \
2002 ), \
2003 mkU8(4 * (7-(_n))) \
2004 )
2005 return binop(Iop_Or32,
2006 binop(Iop_Or32,
2007 binop(Iop_Or32, FIELD(0), FIELD(1)),
2008 binop(Iop_Or32, FIELD(2), FIELD(3))
2009 ),
2010 binop(Iop_Or32,
2011 binop(Iop_Or32, FIELD(4), FIELD(5)),
2012 binop(Iop_Or32, FIELD(6), FIELD(7))
2013 )
2014 );
2015# undef FIELD
2016 }
ceriond953ebb2005-11-29 13:27:20 +00002017
2018 case PPC_GST_XER:
cerionedf7fc52005-11-18 20:57:41 +00002019 return binop(Iop_Or32,
2020 binop(Iop_Or32,
2021 binop( Iop_Shl32, getXER_SO32(), mkU8(31)),
2022 binop( Iop_Shl32, getXER_OV32(), mkU8(30))),
2023 binop(Iop_Or32,
2024 binop( Iop_Shl32, getXER_CA32(), mkU8(29)),
2025 getXER_BC32()));
ceriond953ebb2005-11-29 13:27:20 +00002026
2027 case PPC_GST_RESVN:
cerion5b2325f2005-12-23 00:55:09 +00002028 return IRExpr_Get( OFFB_RESVN, ty);
ceriond953ebb2005-11-29 13:27:20 +00002029
cerionedf7fc52005-11-18 20:57:41 +00002030 default:
cerion5b2325f2005-12-23 00:55:09 +00002031 vex_printf("getGST(ppc): reg = %u", reg);
2032 vpanic("getGST(ppc)");
ceriond953ebb2005-11-29 13:27:20 +00002033 }
2034}
2035
2036/* Get a masked word from the given reg */
2037static IRExpr* /* ::Ity_I32 */ getGST_masked ( PPC_GST reg, UInt mask )
2038{
2039 IRTemp val = newTemp(Ity_I32);
2040 vassert( reg < PPC_GST_MAX );
2041
2042 switch (reg) {
2043
2044 case PPC_GST_FPSCR: {
2045 vassert((mask & 0x3) == 0x3 || (mask & 0x3) == 0x0);
2046 vassert((mask & 0xF000) == 0xF000 || (mask & 0xF000) == 0x0);
2047 /* all masks now refer to valid fields */
2048
cerion5b2325f2005-12-23 00:55:09 +00002049 /* Vex-generated code expects the FPSCR to be set as follows:
ceriond953ebb2005-11-29 13:27:20 +00002050 all exceptions masked, round-to-nearest.
2051 This corresponds to a FPSCR value of 0x0. */
2052
2053 /* We're only keeping track of the rounding mode,
2054 so if the mask isn't asking for this, just return 0x0 */
2055 if (mask & 0x3) {
cerion5b2325f2005-12-23 00:55:09 +00002056 assign( val, IRExpr_Get( OFFB_FPROUND, Ity_I32 ) );
ceriond953ebb2005-11-29 13:27:20 +00002057 } else {
2058 assign( val, mkU32(0x0) );
2059 }
2060 break;
2061 }
2062
2063 default:
cerion5b2325f2005-12-23 00:55:09 +00002064 vex_printf("getGST_masked(ppc): reg = %u", reg);
2065 vpanic("getGST_masked(ppc)");
ceriond953ebb2005-11-29 13:27:20 +00002066 }
2067
2068 if (mask != 0xFFFFFFFF) {
2069 return binop(Iop_And32, mkexpr(val), mkU32(mask));
2070 } else {
2071 return mkexpr(val);
2072 }
2073}
2074
2075/* Fetch the specified REG[FLD] nibble (as per IBM/hardware notation)
2076 and return it at the bottom of an I32; the top 27 bits are
2077 guaranteed to be zero. */
2078static IRExpr* /* ::Ity_I32 */ getGST_field ( PPC_GST reg, UInt fld )
2079{
2080 UInt shft, mask;
2081
2082 vassert( fld < 8 );
2083 vassert( reg < PPC_GST_MAX );
2084
2085 shft = 4*(7-fld);
2086 mask = 0xF<<shft;
2087
2088 switch (reg) {
2089 case PPC_GST_XER:
2090 vassert(fld ==7);
2091 return binop(Iop_Or32,
2092 binop(Iop_Or32,
2093 binop(Iop_Shl32, getXER_SO32(), mkU8(3)),
2094 binop(Iop_Shl32, getXER_OV32(), mkU8(2))),
2095 binop( Iop_Shl32, getXER_CA32(), mkU8(1)));
2096 break;
2097
2098 default:
2099 if (shft == 0)
2100 return getGST_masked( reg, mask );
2101 else
2102 return binop(Iop_Shr32,
2103 getGST_masked( reg, mask ),
2104 mkU8(toUChar( shft )));
2105 }
2106}
2107
2108static void putGST ( PPC_GST reg, IRExpr* src )
2109{
cerion5b2325f2005-12-23 00:55:09 +00002110 IRType ty = mode64 ? Ity_I64 : Ity_I32;
2111 IRType ty_src = typeOfIRExpr(irbb->tyenv,src );
ceriond953ebb2005-11-29 13:27:20 +00002112 vassert( reg < PPC_GST_MAX );
2113 switch (reg) {
2114 case PPC_GST_CIA:
cerion5b2325f2005-12-23 00:55:09 +00002115 vassert( ty_src == ty );
2116 stmt( IRStmt_Put( OFFB_CIA, src ) );
ceriond953ebb2005-11-29 13:27:20 +00002117 break;
2118 case PPC_GST_LR:
cerion5b2325f2005-12-23 00:55:09 +00002119 vassert( ty_src == ty );
2120 stmt( IRStmt_Put( OFFB_LR, src ) );
ceriond953ebb2005-11-29 13:27:20 +00002121 break;
2122 case PPC_GST_CTR:
cerion5b2325f2005-12-23 00:55:09 +00002123 vassert( ty_src == ty );
2124 stmt( IRStmt_Put( OFFB_CTR, src ) );
ceriond953ebb2005-11-29 13:27:20 +00002125 break;
2126 case PPC_GST_VRSAVE:
cerion5b2325f2005-12-23 00:55:09 +00002127 vassert( ty_src == Ity_I32 );
2128 stmt( IRStmt_Put( OFFB_VRSAVE,src));
ceriond953ebb2005-11-29 13:27:20 +00002129 break;
2130 case PPC_GST_VSCR:
cerion5b2325f2005-12-23 00:55:09 +00002131 vassert( ty_src == Ity_I32 );
2132 stmt( IRStmt_Put( OFFB_VSCR,
ceriond953ebb2005-11-29 13:27:20 +00002133 binop(Iop_And32, src,
2134 mkU32(MASK_VSCR_VALID)) ) );
2135 break;
2136 case PPC_GST_XER:
cerion5b2325f2005-12-23 00:55:09 +00002137 vassert( ty_src == Ity_I32 );
ceriond953ebb2005-11-29 13:27:20 +00002138 putXER_SO( unop(Iop_32to8, binop(Iop_Shr32, src, mkU8(31))) );
2139 putXER_OV( unop(Iop_32to8, binop(Iop_Shr32, src, mkU8(30))) );
2140 putXER_CA( unop(Iop_32to8, binop(Iop_Shr32, src, mkU8(29))) );
2141 putXER_BC( unop(Iop_32to8, src) );
2142 break;
2143
2144 case PPC_GST_EMWARN:
cerion5b2325f2005-12-23 00:55:09 +00002145 vassert( ty_src == Ity_I32 );
2146 stmt( IRStmt_Put( OFFB_EMWARN,src) );
ceriond953ebb2005-11-29 13:27:20 +00002147 break;
2148
2149 case PPC_GST_TISTART:
cerion5b2325f2005-12-23 00:55:09 +00002150 vassert( ty_src == ty );
2151 stmt( IRStmt_Put( OFFB_TISTART, src) );
ceriond953ebb2005-11-29 13:27:20 +00002152 break;
2153
2154 case PPC_GST_TILEN:
cerion5b2325f2005-12-23 00:55:09 +00002155 vassert( ty_src == ty );
2156 stmt( IRStmt_Put( OFFB_TILEN, src) );
ceriond953ebb2005-11-29 13:27:20 +00002157 break;
2158
2159 case PPC_GST_RESVN:
cerion5b2325f2005-12-23 00:55:09 +00002160 vassert( ty_src == ty );
2161 stmt( IRStmt_Put( OFFB_RESVN, src) );
ceriond953ebb2005-11-29 13:27:20 +00002162 break;
2163
2164 default:
cerion5b2325f2005-12-23 00:55:09 +00002165 vex_printf("putGST(ppc): reg = %u", reg);
2166 vpanic("putGST(ppc)");
cerionedf7fc52005-11-18 20:57:41 +00002167 }
2168}
sewardje14bb9f2005-07-22 09:39:02 +00002169
2170/* Write masked src to the given reg */
ceriond953ebb2005-11-29 13:27:20 +00002171static void putGST_masked ( PPC_GST reg, IRExpr* src, UInt mask )
sewardje14bb9f2005-07-22 09:39:02 +00002172{
ceriond953ebb2005-11-29 13:27:20 +00002173 IRType ty = mode64 ? Ity_I64 : Ity_I32;
2174 vassert( reg < PPC_GST_MAX );
sewardje14bb9f2005-07-22 09:39:02 +00002175 vassert( typeOfIRExpr(irbb->tyenv,src ) == Ity_I32 );
2176
2177 switch (reg) {
ceriond953ebb2005-11-29 13:27:20 +00002178 case PPC_GST_FPSCR: {
sewardje14bb9f2005-07-22 09:39:02 +00002179 vassert((mask & 0x3) == 0x3 || (mask & 0x3) == 0x0);
2180 vassert((mask & 0xF000) == 0xF000 || (mask & 0xF000) == 0x0);
2181 /* all masks now refer to valid fields */
2182
2183 /* Allow writes to Rounding Mode */
2184 if (mask & 0x3) {
cerion5b2325f2005-12-23 00:55:09 +00002185 stmt( IRStmt_Put( OFFB_FPROUND,
sewardje14bb9f2005-07-22 09:39:02 +00002186 binop(Iop_And32, src, mkU32(0x3)) ));
2187 }
2188
cerionedf7fc52005-11-18 20:57:41 +00002189 /* Give EmWarn for attempted writes to:
sewardje14bb9f2005-07-22 09:39:02 +00002190 - Exception Controls
2191 - Non-IEEE Mode
2192 */
2193 if (mask & 0xFC) { // Exception Control, Non-IEE mode
2194 VexEmWarn ew = EmWarn_PPC32exns;
2195
2196 /* If any of the src::exception_control bits are actually set,
2197 side-exit to the next insn, reporting the warning,
2198 so that Valgrind's dispatcher sees the warning. */
ceriond953ebb2005-11-29 13:27:20 +00002199 putGST( PPC_GST_EMWARN, mkU32(ew) );
sewardje14bb9f2005-07-22 09:39:02 +00002200 stmt(
2201 IRStmt_Exit(
2202 binop(Iop_CmpNE32, mkU32(ew), mkU32(EmWarn_NONE)),
2203 Ijk_EmWarn,
cerion2831b002005-11-30 19:55:22 +00002204 mkSzConst( ty, nextInsnAddr()) ));
sewardje14bb9f2005-07-22 09:39:02 +00002205 }
2206
cerionedf7fc52005-11-18 20:57:41 +00002207 /* Ignore all other writes */
sewardje14bb9f2005-07-22 09:39:02 +00002208 break;
cerionedf7fc52005-11-18 20:57:41 +00002209 }
sewardje14bb9f2005-07-22 09:39:02 +00002210
2211 default:
cerion5b2325f2005-12-23 00:55:09 +00002212 vex_printf("putGST_masked(ppc): reg = %u", reg);
2213 vpanic("putGST_masked(ppc)");
sewardje14bb9f2005-07-22 09:39:02 +00002214 }
2215}
2216
cerionedf7fc52005-11-18 20:57:41 +00002217/* Write the least significant nibble of src to the specified
2218 REG[FLD] (as per IBM/hardware notation). */
ceriond953ebb2005-11-29 13:27:20 +00002219static void putGST_field ( PPC_GST reg, IRExpr* src, UInt fld )
cerionedf7fc52005-11-18 20:57:41 +00002220{
sewardj41a7b702005-11-18 22:18:23 +00002221 UInt shft, mask;
2222
cerionedf7fc52005-11-18 20:57:41 +00002223 vassert( typeOfIRExpr(irbb->tyenv,src ) == Ity_I32 );
2224 vassert( fld < 8 );
ceriond953ebb2005-11-29 13:27:20 +00002225 vassert( reg < PPC_GST_MAX );
cerionedf7fc52005-11-18 20:57:41 +00002226
sewardj41a7b702005-11-18 22:18:23 +00002227 shft = 4*(7-fld);
2228 mask = 0xF<<shft;
cerionedf7fc52005-11-18 20:57:41 +00002229
2230 switch (reg) {
ceriond953ebb2005-11-29 13:27:20 +00002231 case PPC_GST_CR:
cerionedf7fc52005-11-18 20:57:41 +00002232 putCR0 (fld, binop(Iop_And8, mkU8(1 ), unop(Iop_32to8, src)));
2233 putCR321(fld, binop(Iop_And8, mkU8(7<<1), unop(Iop_32to8, src)));
2234 break;
2235
2236 default:
2237 if (shft == 0) {
ceriond953ebb2005-11-29 13:27:20 +00002238 putGST_masked( reg, src, mask );
cerionedf7fc52005-11-18 20:57:41 +00002239 } else {
ceriond953ebb2005-11-29 13:27:20 +00002240 putGST_masked( reg,
cerionedf7fc52005-11-18 20:57:41 +00002241 binop(Iop_Shl32, src, mkU8(toUChar(shft))),
2242 mask );
2243 }
2244 }
2245}
cerion62bec572005-02-01 21:29:39 +00002246
cerion76222262005-02-05 13:45:57 +00002247
2248
cerione9d361a2005-03-04 17:35:29 +00002249/*------------------------------------------------------------*/
cerion3d870a32005-03-18 12:23:33 +00002250/*--- Integer Instruction Translation --- */
cerione9d361a2005-03-04 17:35:29 +00002251/*------------------------------------------------------------*/
cerion896a1372005-01-25 12:24:25 +00002252
cerion91ad5362005-01-27 23:02:41 +00002253/*
2254 Integer Arithmetic Instructions
2255*/
cerion645c9302005-01-31 10:09:59 +00002256static Bool dis_int_arith ( UInt theInstr )
cerion91ad5362005-01-27 23:02:41 +00002257{
cerion76de5cf2005-11-18 18:25:12 +00002258 /* D-Form, XO-Form */
2259 UChar opc1 = ifieldOPC(theInstr);
2260 UChar rD_addr = ifieldRegDS(theInstr);
2261 UChar rA_addr = ifieldRegA(theInstr);
ceriond953ebb2005-11-29 13:27:20 +00002262 UInt uimm16 = ifieldUIMM16(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00002263 UChar rB_addr = ifieldRegB(theInstr);
2264 UChar flag_OE = ifieldBIT10(theInstr);
2265 UInt opc2 = ifieldOPClo9(theInstr);
2266 UChar flag_rC = ifieldBIT0(theInstr);
2267
ceriond953ebb2005-11-29 13:27:20 +00002268 Long simm16 = extend_s_16to64(uimm16);
2269 IRType ty = mode64 ? Ity_I64 : Ity_I32;
2270 IRTemp rA = newTemp(ty);
2271 IRTemp rB = newTemp(ty);
2272 IRTemp rD = newTemp(ty);
cerion70e24122005-03-16 00:27:37 +00002273
cerionb85e8bb2005-02-16 08:54:33 +00002274 Bool do_rc = False;
cerion91ad5362005-01-27 23:02:41 +00002275
cerion76de5cf2005-11-18 18:25:12 +00002276 assign( rA, getIReg(rA_addr) );
2277 assign( rB, getIReg(rB_addr) ); // XO-Form: rD, rA, rB
sewardjb51f0f42005-07-18 11:38:02 +00002278
cerionb85e8bb2005-02-16 08:54:33 +00002279 switch (opc1) {
cerionb85e8bb2005-02-16 08:54:33 +00002280 /* D-Form */
cerione9d361a2005-03-04 17:35:29 +00002281 case 0x0C: // addic (Add Immediate Carrying, PPC32 p351
ceriond953ebb2005-11-29 13:27:20 +00002282 DIP("addic r%u,r%u,%d\n", rD_addr, rA_addr, (Int)simm16);
cerion2831b002005-11-30 19:55:22 +00002283 assign( rD, binop( mkSzOp(ty, Iop_Add8), mkexpr(rA),
2284 mkSzExtendS16(ty, uimm16) ) );
cerion5b2325f2005-12-23 00:55:09 +00002285 set_XER_CA( ty, PPCG_FLAG_OP_ADD,
cerion2831b002005-11-30 19:55:22 +00002286 mkexpr(rD), mkexpr(rA), mkSzExtendS16(ty, uimm16),
2287 mkSzImm(ty, 0)/*old xer.ca, which is ignored*/ );
cerion4561acb2005-02-21 14:07:48 +00002288 break;
sewardjb51f0f42005-07-18 11:38:02 +00002289
cerione9d361a2005-03-04 17:35:29 +00002290 case 0x0D: // addic. (Add Immediate Carrying and Record, PPC32 p352)
ceriond953ebb2005-11-29 13:27:20 +00002291 DIP("addic. r%u,r%u,%d\n", rD_addr, rA_addr, (Int)simm16);
cerion2831b002005-11-30 19:55:22 +00002292 assign( rD, binop( mkSzOp(ty, Iop_Add8), mkexpr(rA),
2293 mkSzExtendS16(ty, uimm16) ) );
cerion5b2325f2005-12-23 00:55:09 +00002294 set_XER_CA( ty, PPCG_FLAG_OP_ADD,
cerion2831b002005-11-30 19:55:22 +00002295 mkexpr(rD), mkexpr(rA), mkSzExtendS16(ty, uimm16),
2296 mkSzImm(ty, 0)/*old xer.ca, which is ignored*/ );
cerion70e24122005-03-16 00:27:37 +00002297 do_rc = True; // Always record to CR
cerion76de5cf2005-11-18 18:25:12 +00002298 flag_rC = 1;
cerion4561acb2005-02-21 14:07:48 +00002299 break;
2300
cerione9d361a2005-03-04 17:35:29 +00002301 case 0x0E: // addi (Add Immediate, PPC32 p350)
cerionb85e8bb2005-02-16 08:54:33 +00002302 // li rD,val == addi rD,0,val
2303 // la disp(rA) == addi rD,rA,disp
cerion76de5cf2005-11-18 18:25:12 +00002304 if ( rA_addr == 0 ) {
ceriond953ebb2005-11-29 13:27:20 +00002305 DIP("li r%u,%d\n", rD_addr, (Int)simm16);
cerion2831b002005-11-30 19:55:22 +00002306 assign( rD, mkSzExtendS16(ty, uimm16) );
cerionb85e8bb2005-02-16 08:54:33 +00002307 } else {
ceriond953ebb2005-11-29 13:27:20 +00002308 DIP("addi r%u,r%u,%d\n", rD_addr, rA_addr, (Int)simm16);
cerion2831b002005-11-30 19:55:22 +00002309 assign( rD, binop( mkSzOp(ty, Iop_Add8), mkexpr(rA),
2310 mkSzExtendS16(ty, uimm16) ) );
cerionb85e8bb2005-02-16 08:54:33 +00002311 }
2312 break;
cerion91ad5362005-01-27 23:02:41 +00002313
cerione9d361a2005-03-04 17:35:29 +00002314 case 0x0F: // addis (Add Immediate Shifted, PPC32 p353)
cerionb85e8bb2005-02-16 08:54:33 +00002315 // lis rD,val == addis rD,0,val
cerion76de5cf2005-11-18 18:25:12 +00002316 if ( rA_addr == 0 ) {
ceriond953ebb2005-11-29 13:27:20 +00002317 DIP("lis r%u,%d\n", rD_addr, (Int)simm16);
cerion2831b002005-11-30 19:55:22 +00002318 assign( rD, mkSzExtendS32(ty, uimm16 << 16) );
cerionb85e8bb2005-02-16 08:54:33 +00002319 } else {
ceriond953ebb2005-11-29 13:27:20 +00002320 DIP("addis r%u,r%u,0x%x\n", rD_addr, rA_addr, (Int)simm16);
cerion2831b002005-11-30 19:55:22 +00002321 assign( rD, binop( mkSzOp(ty, Iop_Add8), mkexpr(rA),
2322 mkSzExtendS32(ty, uimm16 << 16) ) );
cerionb85e8bb2005-02-16 08:54:33 +00002323 }
2324 break;
cerion91ad5362005-01-27 23:02:41 +00002325
cerione9d361a2005-03-04 17:35:29 +00002326 case 0x07: // mulli (Multiply Low Immediate, PPC32 p490)
ceriond953ebb2005-11-29 13:27:20 +00002327 DIP("mulli r%u,r%u,%d\n", rD_addr, rA_addr, (Int)simm16);
2328 if (mode64)
2329 assign( rD, unop(Iop_128to64,
2330 binop(Iop_MullS64, mkexpr(rA),
cerion2831b002005-11-30 19:55:22 +00002331 mkSzExtendS16(ty, uimm16))) );
ceriond953ebb2005-11-29 13:27:20 +00002332 else
2333 assign( rD, unop(Iop_64to32,
2334 binop(Iop_MullS32, mkexpr(rA),
cerion2831b002005-11-30 19:55:22 +00002335 mkSzExtendS16(ty, uimm16))) );
cerionb85e8bb2005-02-16 08:54:33 +00002336 break;
cerion38674602005-02-08 02:19:25 +00002337
cerione9d361a2005-03-04 17:35:29 +00002338 case 0x08: // subfic (Subtract from Immediate Carrying, PPC32 p540)
ceriond953ebb2005-11-29 13:27:20 +00002339 DIP("subfic r%u,r%u,%d\n", rD_addr, rA_addr, (Int)simm16);
cerion76de5cf2005-11-18 18:25:12 +00002340 // rD = simm16 - rA
cerion2831b002005-11-30 19:55:22 +00002341 assign( rD, binop( mkSzOp(ty, Iop_Sub8),
2342 mkSzExtendS16(ty, uimm16),
ceriond953ebb2005-11-29 13:27:20 +00002343 mkexpr(rA)) );
cerion5b2325f2005-12-23 00:55:09 +00002344 set_XER_CA( ty, PPCG_FLAG_OP_SUBFI,
cerion2831b002005-11-30 19:55:22 +00002345 mkexpr(rD), mkexpr(rA), mkSzExtendS16(ty, uimm16),
2346 mkSzImm(ty, 0)/*old xer.ca, which is ignored*/ );
cerionb85e8bb2005-02-16 08:54:33 +00002347 break;
cerion38674602005-02-08 02:19:25 +00002348
cerionb85e8bb2005-02-16 08:54:33 +00002349 /* XO-Form */
2350 case 0x1F:
cerionb85e8bb2005-02-16 08:54:33 +00002351 do_rc = True; // All below record to CR
2352
2353 switch (opc2) {
cerione9d361a2005-03-04 17:35:29 +00002354 case 0x10A: // add (Add, PPC32 p347)
ceriond953ebb2005-11-29 13:27:20 +00002355 DIP("add%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002356 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002357 rD_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00002358 assign( rD, binop( mkSzOp(ty, Iop_Add8),
ceriond953ebb2005-11-29 13:27:20 +00002359 mkexpr(rA), mkexpr(rB) ) );
cerion70e24122005-03-16 00:27:37 +00002360 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002361 set_XER_OV( ty, PPCG_FLAG_OP_ADD,
cerion76de5cf2005-11-18 18:25:12 +00002362 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00002363 }
cerionb85e8bb2005-02-16 08:54:33 +00002364 break;
cerion91ad5362005-01-27 23:02:41 +00002365
cerione9d361a2005-03-04 17:35:29 +00002366 case 0x00A: // addc (Add Carrying, PPC32 p348)
ceriond953ebb2005-11-29 13:27:20 +00002367 DIP("addc%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002368 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002369 rD_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00002370 assign( rD, binop( mkSzOp(ty, Iop_Add8),
ceriond953ebb2005-11-29 13:27:20 +00002371 mkexpr(rA), mkexpr(rB)) );
cerion5b2325f2005-12-23 00:55:09 +00002372 set_XER_CA( ty, PPCG_FLAG_OP_ADD,
cerion76de5cf2005-11-18 18:25:12 +00002373 mkexpr(rD), mkexpr(rA), mkexpr(rB),
cerion2831b002005-11-30 19:55:22 +00002374 mkSzImm(ty, 0)/*old xer.ca, which is ignored*/ );
cerion70e24122005-03-16 00:27:37 +00002375 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002376 set_XER_OV( ty, PPCG_FLAG_OP_ADD,
cerion76de5cf2005-11-18 18:25:12 +00002377 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00002378 }
cerionb85e8bb2005-02-16 08:54:33 +00002379 break;
2380
sewardjb51f0f42005-07-18 11:38:02 +00002381 case 0x08A: { // adde (Add Extended, PPC32 p349)
cerion2831b002005-11-30 19:55:22 +00002382 IRTemp old_xer_ca = newTemp(ty);
ceriond953ebb2005-11-29 13:27:20 +00002383 DIP("adde%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002384 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002385 rD_addr, rA_addr, rB_addr);
cerionb85e8bb2005-02-16 08:54:33 +00002386 // rD = rA + rB + XER[CA]
cerion2831b002005-11-30 19:55:22 +00002387 assign( old_xer_ca, mkSzWiden32(ty, getXER_CA32(), False) );
2388 assign( rD, binop( mkSzOp(ty, Iop_Add8), mkexpr(rA),
2389 binop( mkSzOp(ty, Iop_Add8),
2390 mkexpr(rB), mkexpr(old_xer_ca))) );
cerion5b2325f2005-12-23 00:55:09 +00002391 set_XER_CA( ty, PPCG_FLAG_OP_ADDE,
cerion76de5cf2005-11-18 18:25:12 +00002392 mkexpr(rD), mkexpr(rA), mkexpr(rB),
cerion2831b002005-11-30 19:55:22 +00002393 mkexpr(old_xer_ca) );
cerion70e24122005-03-16 00:27:37 +00002394 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002395 set_XER_OV( ty, PPCG_FLAG_OP_ADDE,
cerion76de5cf2005-11-18 18:25:12 +00002396 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00002397 }
cerionb85e8bb2005-02-16 08:54:33 +00002398 break;
sewardjb51f0f42005-07-18 11:38:02 +00002399 }
2400
cerion5b2325f2005-12-23 00:55:09 +00002401 case 0x0EA: { // addme (Add to Minus One Extended, PPC32 p354)
cerion2831b002005-11-30 19:55:22 +00002402 IRTemp old_xer_ca = newTemp(ty);
2403 IRExpr *min_one;
cerion76de5cf2005-11-18 18:25:12 +00002404 if (rB_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002405 vex_printf("dis_int_arith(ppc)(addme,rB_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002406 return False;
2407 }
ceriond953ebb2005-11-29 13:27:20 +00002408 DIP("addme%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002409 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002410 rD_addr, rA_addr, rB_addr);
cerion70e24122005-03-16 00:27:37 +00002411 // rD = rA + (-1) + XER[CA]
2412 // => Just another form of adde
cerion2831b002005-11-30 19:55:22 +00002413 assign( old_xer_ca, mkSzWiden32(ty, getXER_CA32(), False) );
2414 min_one = mkSzImm(ty, (Long)-1);
2415 assign( rD, binop( mkSzOp(ty, Iop_Add8), mkexpr(rA),
2416 binop( mkSzOp(ty, Iop_Add8),
2417 min_one, mkexpr(old_xer_ca)) ));
cerion5b2325f2005-12-23 00:55:09 +00002418 set_XER_CA( ty, PPCG_FLAG_OP_ADDE,
ceriond953ebb2005-11-29 13:27:20 +00002419 mkexpr(rD), mkexpr(rA), min_one,
cerion2831b002005-11-30 19:55:22 +00002420 mkexpr(old_xer_ca) );
cerion70e24122005-03-16 00:27:37 +00002421 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002422 set_XER_OV( ty, PPCG_FLAG_OP_ADDE,
ceriond953ebb2005-11-29 13:27:20 +00002423 mkexpr(rD), mkexpr(rA), min_one );
cerion70e24122005-03-16 00:27:37 +00002424 }
cerionb85e8bb2005-02-16 08:54:33 +00002425 break;
sewardjb51f0f42005-07-18 11:38:02 +00002426 }
2427
2428 case 0x0CA: { // addze (Add to Zero Extended, PPC32 p355)
cerion2831b002005-11-30 19:55:22 +00002429 IRTemp old_xer_ca = newTemp(ty);
cerion76de5cf2005-11-18 18:25:12 +00002430 if (rB_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002431 vex_printf("dis_int_arith(ppc)(addze,rB_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002432 return False;
2433 }
ceriond953ebb2005-11-29 13:27:20 +00002434 DIP("addze%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002435 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002436 rD_addr, rA_addr, rB_addr);
cerion70e24122005-03-16 00:27:37 +00002437 // rD = rA + (0) + XER[CA]
2438 // => Just another form of adde
cerion2831b002005-11-30 19:55:22 +00002439 assign( old_xer_ca, mkSzWiden32(ty, getXER_CA32(), False) );
2440 assign( rD, binop( mkSzOp(ty, Iop_Add8),
2441 mkexpr(rA), mkexpr(old_xer_ca)) );
cerion5b2325f2005-12-23 00:55:09 +00002442 set_XER_CA( ty, PPCG_FLAG_OP_ADDE,
cerion2831b002005-11-30 19:55:22 +00002443 mkexpr(rD), mkexpr(rA), mkSzImm(ty, 0),
2444 mkexpr(old_xer_ca) );
cerion70e24122005-03-16 00:27:37 +00002445 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002446 set_XER_OV( ty, PPCG_FLAG_OP_ADDE,
cerion2831b002005-11-30 19:55:22 +00002447 mkexpr(rD), mkexpr(rA), mkSzImm(ty, 0) );
cerion70e24122005-03-16 00:27:37 +00002448 }
cerionb85e8bb2005-02-16 08:54:33 +00002449 break;
sewardjb51f0f42005-07-18 11:38:02 +00002450 }
cerion91ad5362005-01-27 23:02:41 +00002451
cerione9d361a2005-03-04 17:35:29 +00002452 case 0x1EB: // divw (Divide Word, PPC32 p388)
ceriond953ebb2005-11-29 13:27:20 +00002453 DIP("divw%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002454 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002455 rD_addr, rA_addr, rB_addr);
ceriond953ebb2005-11-29 13:27:20 +00002456 if (mode64) {
cerion2831b002005-11-30 19:55:22 +00002457 /* Note:
2458 XER settings are mode independent, and reflect the
2459 overflow of the low-order 32bit result
2460 CR0[LT|GT|EQ] are undefined if flag_rC && mode64
2461 */
cerionbb01b7c2005-12-16 13:40:18 +00002462 /* rD[hi32] are undefined: setting them to sign of lo32
2463 - makes set_CR0 happy */
2464 IRExpr* dividend = mk64lo32Sto64( mkexpr(rA) );
2465 IRExpr* divisor = mk64lo32Sto64( mkexpr(rB) );
cerion5b2325f2005-12-23 00:55:09 +00002466 assign( rD, mk64lo32Uto64( binop(Iop_DivS64, dividend,
2467 divisor) ) );
ceriond953ebb2005-11-29 13:27:20 +00002468 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002469 set_XER_OV( ty, PPCG_FLAG_OP_DIVW,
cerionf0de28c2005-12-13 20:21:11 +00002470 mkexpr(rD), dividend, divisor );
ceriond953ebb2005-11-29 13:27:20 +00002471 }
2472 } else {
2473 assign( rD, binop(Iop_DivS32, mkexpr(rA), mkexpr(rB)) );
2474 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002475 set_XER_OV( ty, PPCG_FLAG_OP_DIVW,
ceriond953ebb2005-11-29 13:27:20 +00002476 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
2477 }
cerion70e24122005-03-16 00:27:37 +00002478 }
cerionb85e8bb2005-02-16 08:54:33 +00002479 /* Note:
2480 if (0x8000_0000 / -1) or (x / 0)
cerion76de5cf2005-11-18 18:25:12 +00002481 => rD=undef, if(flag_rC) CR7=undef, if(flag_OE) XER_OV=1
cerionb85e8bb2005-02-16 08:54:33 +00002482 => But _no_ exception raised. */
2483 break;
cerion91ad5362005-01-27 23:02:41 +00002484
cerione9d361a2005-03-04 17:35:29 +00002485 case 0x1CB: // divwu (Divide Word Unsigned, PPC32 p389)
ceriond953ebb2005-11-29 13:27:20 +00002486 DIP("divwu%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002487 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002488 rD_addr, rA_addr, rB_addr);
ceriond953ebb2005-11-29 13:27:20 +00002489 if (mode64) {
cerion2831b002005-11-30 19:55:22 +00002490 /* Note:
2491 XER settings are mode independent, and reflect the
2492 overflow of the low-order 32bit result
2493 CR0[LT|GT|EQ] are undefined if flag_rC && mode64
2494 */
cerionbb01b7c2005-12-16 13:40:18 +00002495 IRExpr* dividend = mk64lo32Uto64( mkexpr(rA) );
2496 IRExpr* divisor = mk64lo32Uto64( mkexpr(rB) );
cerion5b2325f2005-12-23 00:55:09 +00002497 assign( rD, mk64lo32Uto64( binop(Iop_DivU64, dividend,
2498 divisor) ) );
ceriond953ebb2005-11-29 13:27:20 +00002499 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002500 set_XER_OV( ty, PPCG_FLAG_OP_DIVWU,
cerionf0de28c2005-12-13 20:21:11 +00002501 mkexpr(rD), dividend, divisor );
ceriond953ebb2005-11-29 13:27:20 +00002502 }
cerion2831b002005-11-30 19:55:22 +00002503 } else {
ceriond953ebb2005-11-29 13:27:20 +00002504 assign( rD, binop(Iop_DivU32, mkexpr(rA), mkexpr(rB)) );
2505 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002506 set_XER_OV( ty, PPCG_FLAG_OP_DIVWU,
ceriond953ebb2005-11-29 13:27:20 +00002507 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
2508 }
cerion70e24122005-03-16 00:27:37 +00002509 }
cerionb85e8bb2005-02-16 08:54:33 +00002510 /* Note: ditto comment divw, for (x / 0) */
2511 break;
cerion91ad5362005-01-27 23:02:41 +00002512
cerione9d361a2005-03-04 17:35:29 +00002513 case 0x04B: // mulhw (Multiply High Word, PPC32 p488)
cerionb85e8bb2005-02-16 08:54:33 +00002514 if (flag_OE != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002515 vex_printf("dis_int_arith(ppc)(mulhw,flag_OE)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002516 return False;
2517 }
cerion5b2325f2005-12-23 00:55:09 +00002518 DIP("mulhw%s r%u,r%u,r%u\n", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002519 rD_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00002520 if (mode64) {
cerionbb01b7c2005-12-16 13:40:18 +00002521 /* rD[hi32] are undefined: setting them to sign of lo32
2522 - makes set_CR0 happy */
2523 assign( rD, binop(Iop_Sar64,
2524 binop(Iop_Mul64,
2525 mk64lo32Sto64( mkexpr(rA) ),
2526 mk64lo32Sto64( mkexpr(rB) )),
2527 mkU8(32)) );
cerion2831b002005-11-30 19:55:22 +00002528 } else {
ceriond953ebb2005-11-29 13:27:20 +00002529 assign( rD, unop(Iop_64HIto32,
2530 binop(Iop_MullS32,
2531 mkexpr(rA), mkexpr(rB))) );
cerion2831b002005-11-30 19:55:22 +00002532 }
cerionb85e8bb2005-02-16 08:54:33 +00002533 break;
cerionc19d5e12005-02-01 15:56:25 +00002534
cerion5b2325f2005-12-23 00:55:09 +00002535 case 0x00B: // mulhwu (Multiply High Word Unsigned, PPC32 p489)
cerionb85e8bb2005-02-16 08:54:33 +00002536 if (flag_OE != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002537 vex_printf("dis_int_arith(ppc)(mulhwu,flag_OE)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002538 return False;
2539 }
cerion5b2325f2005-12-23 00:55:09 +00002540 DIP("mulhwu%s r%u,r%u,r%u\n", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002541 rD_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00002542 if (mode64) {
cerionbb01b7c2005-12-16 13:40:18 +00002543 /* rD[hi32] are undefined: setting them to sign of lo32
2544 - makes set_CR0 happy */
2545 assign( rD, binop(Iop_Sar64,
2546 binop(Iop_Mul64,
2547 mk64lo32Uto64( mkexpr(rA) ),
2548 mk64lo32Uto64( mkexpr(rB) ) ),
2549 mkU8(32)) );
cerion2831b002005-11-30 19:55:22 +00002550 } else {
ceriond953ebb2005-11-29 13:27:20 +00002551 assign( rD, unop(Iop_64HIto32,
2552 binop(Iop_MullU32,
2553 mkexpr(rA), mkexpr(rB))) );
cerion2831b002005-11-30 19:55:22 +00002554 }
cerionb85e8bb2005-02-16 08:54:33 +00002555 break;
2556
cerione9d361a2005-03-04 17:35:29 +00002557 case 0x0EB: // mullw (Multiply Low Word, PPC32 p491)
ceriond953ebb2005-11-29 13:27:20 +00002558 DIP("mullw%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002559 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002560 rD_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00002561 if (mode64) {
cerionbb01b7c2005-12-16 13:40:18 +00002562 /* rD[hi32] are undefined: setting them to sign of lo32
2563 - set_XER_OV() and set_CR0() depend on this */
2564 IRExpr *a = unop(Iop_64to32, mkexpr(rA) );
2565 IRExpr *b = unop(Iop_64to32, mkexpr(rB) );
2566 assign( rD, binop(Iop_MullS32, a, b) );
2567 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002568 set_XER_OV( ty, PPCG_FLAG_OP_MULLW,
cerionbb01b7c2005-12-16 13:40:18 +00002569 mkexpr(rD),
2570 unop(Iop_32Uto64, a), unop(Iop_32Uto64, b) );
2571 }
cerion2831b002005-11-30 19:55:22 +00002572 } else {
ceriond953ebb2005-11-29 13:27:20 +00002573 assign( rD, unop(Iop_64to32,
2574 binop(Iop_MullU32,
2575 mkexpr(rA), mkexpr(rB))) );
cerionbb01b7c2005-12-16 13:40:18 +00002576 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002577 set_XER_OV( ty, PPCG_FLAG_OP_MULLW,
cerionbb01b7c2005-12-16 13:40:18 +00002578 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
2579 }
cerion70e24122005-03-16 00:27:37 +00002580 }
cerionb85e8bb2005-02-16 08:54:33 +00002581 break;
cerionc19d5e12005-02-01 15:56:25 +00002582
cerione9d361a2005-03-04 17:35:29 +00002583 case 0x068: // neg (Negate, PPC32 p493)
cerion76de5cf2005-11-18 18:25:12 +00002584 if (rB_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002585 vex_printf("dis_int_arith(ppc)(neg,rB_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002586 return False;
2587 }
ceriond953ebb2005-11-29 13:27:20 +00002588 DIP("neg%s%s r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002589 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002590 rD_addr, rA_addr);
ceriond953ebb2005-11-29 13:27:20 +00002591 // rD = (~rA) + 1
cerion2831b002005-11-30 19:55:22 +00002592 assign( rD, binop( mkSzOp(ty, Iop_Add8),
2593 unop( mkSzOp(ty, Iop_Not8), mkexpr(rA) ),
2594 mkSzImm(ty, 1)) );
cerion70e24122005-03-16 00:27:37 +00002595 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002596 set_XER_OV( ty, PPCG_FLAG_OP_NEG,
cerion76de5cf2005-11-18 18:25:12 +00002597 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00002598 }
cerionb85e8bb2005-02-16 08:54:33 +00002599 break;
cerion91ad5362005-01-27 23:02:41 +00002600
cerione9d361a2005-03-04 17:35:29 +00002601 case 0x028: // subf (Subtract From, PPC32 p537)
ceriond953ebb2005-11-29 13:27:20 +00002602 DIP("subf%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002603 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002604 rD_addr, rA_addr, rB_addr);
cerion01908472005-02-25 16:43:08 +00002605 // rD = rB - rA
cerion2831b002005-11-30 19:55:22 +00002606 assign( rD, binop( mkSzOp(ty, Iop_Sub8),
ceriond953ebb2005-11-29 13:27:20 +00002607 mkexpr(rB), mkexpr(rA)) );
cerion70e24122005-03-16 00:27:37 +00002608 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002609 set_XER_OV( ty, PPCG_FLAG_OP_SUBF,
cerion76de5cf2005-11-18 18:25:12 +00002610 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00002611 }
cerionb85e8bb2005-02-16 08:54:33 +00002612 break;
cerion38674602005-02-08 02:19:25 +00002613
cerione9d361a2005-03-04 17:35:29 +00002614 case 0x008: // subfc (Subtract from Carrying, PPC32 p538)
ceriond953ebb2005-11-29 13:27:20 +00002615 DIP("subfc%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002616 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002617 rD_addr, rA_addr, rB_addr);
cerion01908472005-02-25 16:43:08 +00002618 // rD = rB - rA
cerion2831b002005-11-30 19:55:22 +00002619 assign( rD, binop( mkSzOp(ty, Iop_Sub8),
ceriond953ebb2005-11-29 13:27:20 +00002620 mkexpr(rB), mkexpr(rA)) );
cerion5b2325f2005-12-23 00:55:09 +00002621 set_XER_CA( ty, PPCG_FLAG_OP_SUBFC,
cerion76de5cf2005-11-18 18:25:12 +00002622 mkexpr(rD), mkexpr(rA), mkexpr(rB),
cerion2831b002005-11-30 19:55:22 +00002623 mkSzImm(ty, 0)/*old xer.ca, which is ignored*/ );
cerion70e24122005-03-16 00:27:37 +00002624 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002625 set_XER_OV( ty, PPCG_FLAG_OP_SUBFC,
cerion76de5cf2005-11-18 18:25:12 +00002626 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00002627 }
cerionb85e8bb2005-02-16 08:54:33 +00002628 break;
2629
sewardjb51f0f42005-07-18 11:38:02 +00002630 case 0x088: {// subfe (Subtract from Extended, PPC32 p539)
cerion2831b002005-11-30 19:55:22 +00002631 IRTemp old_xer_ca = newTemp(ty);
ceriond953ebb2005-11-29 13:27:20 +00002632 DIP("subfe%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002633 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002634 rD_addr, rA_addr, rB_addr);
cerionb85e8bb2005-02-16 08:54:33 +00002635 // rD = (log not)rA + rB + XER[CA]
cerion2831b002005-11-30 19:55:22 +00002636 assign( old_xer_ca, mkSzWiden32(ty, getXER_CA32(), False) );
2637 assign( rD, binop( mkSzOp(ty, Iop_Add8),
2638 unop( mkSzOp(ty, Iop_Not8), mkexpr(rA)),
2639 binop( mkSzOp(ty, Iop_Add8),
2640 mkexpr(rB), mkexpr(old_xer_ca))) );
cerion5b2325f2005-12-23 00:55:09 +00002641 set_XER_CA( ty, PPCG_FLAG_OP_SUBFE,
cerion76de5cf2005-11-18 18:25:12 +00002642 mkexpr(rD), mkexpr(rA), mkexpr(rB),
cerion2831b002005-11-30 19:55:22 +00002643 mkexpr(old_xer_ca) );
cerion70e24122005-03-16 00:27:37 +00002644 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002645 set_XER_OV( ty, PPCG_FLAG_OP_SUBFE,
cerion76de5cf2005-11-18 18:25:12 +00002646 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00002647 }
cerionb85e8bb2005-02-16 08:54:33 +00002648 break;
sewardjb51f0f42005-07-18 11:38:02 +00002649 }
2650
cerion5b2325f2005-12-23 00:55:09 +00002651 case 0x0E8: { // subfme (Subtract from -1 Extended, PPC32 p541)
cerion2831b002005-11-30 19:55:22 +00002652 IRTemp old_xer_ca = newTemp(ty);
2653 IRExpr *min_one;
cerion76de5cf2005-11-18 18:25:12 +00002654 if (rB_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002655 vex_printf("dis_int_arith(ppc)(subfme,rB_addr)\n");
sewardj20ef5472005-07-21 14:48:31 +00002656 return False;
2657 }
ceriond953ebb2005-11-29 13:27:20 +00002658 DIP("subfme%s%s r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002659 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002660 rD_addr, rA_addr);
sewardj20ef5472005-07-21 14:48:31 +00002661 // rD = (log not)rA + (-1) + XER[CA]
2662 // => Just another form of subfe
cerion2831b002005-11-30 19:55:22 +00002663 assign( old_xer_ca, mkSzWiden32(ty, getXER_CA32(), False) );
2664 min_one = mkSzImm(ty, (Long)-1);
2665 assign( rD, binop( mkSzOp(ty, Iop_Add8),
2666 unop( mkSzOp(ty, Iop_Not8), mkexpr(rA)),
2667 binop( mkSzOp(ty, Iop_Add8),
2668 min_one, mkexpr(old_xer_ca))) );
cerion5b2325f2005-12-23 00:55:09 +00002669 set_XER_CA( ty, PPCG_FLAG_OP_SUBFE,
ceriond953ebb2005-11-29 13:27:20 +00002670 mkexpr(rD), mkexpr(rA), min_one,
cerion2831b002005-11-30 19:55:22 +00002671 mkexpr(old_xer_ca) );
sewardj20ef5472005-07-21 14:48:31 +00002672 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002673 set_XER_OV( ty, PPCG_FLAG_OP_SUBFE,
ceriond953ebb2005-11-29 13:27:20 +00002674 mkexpr(rD), mkexpr(rA), min_one );
sewardj20ef5472005-07-21 14:48:31 +00002675 }
2676 break;
2677 }
2678
cerion5b2325f2005-12-23 00:55:09 +00002679 case 0x0C8: { // subfze (Subtract from Zero Extended, PPC32 p542)
cerion2831b002005-11-30 19:55:22 +00002680 IRTemp old_xer_ca = newTemp(ty);
cerion76de5cf2005-11-18 18:25:12 +00002681 if (rB_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002682 vex_printf("dis_int_arith(ppc)(subfze,rB_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002683 return False;
2684 }
ceriond953ebb2005-11-29 13:27:20 +00002685 DIP("subfze%s%s r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002686 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002687 rD_addr, rA_addr);
cerion70e24122005-03-16 00:27:37 +00002688 // rD = (log not)rA + (0) + XER[CA]
2689 // => Just another form of subfe
cerion2831b002005-11-30 19:55:22 +00002690 assign( old_xer_ca, mkSzWiden32(ty, getXER_CA32(), False) );
2691 assign( rD, binop( mkSzOp(ty, Iop_Add8),
2692 unop( mkSzOp(ty, Iop_Not8),
2693 mkexpr(rA)), mkexpr(old_xer_ca)) );
cerion5b2325f2005-12-23 00:55:09 +00002694 set_XER_CA( ty, PPCG_FLAG_OP_SUBFE,
cerion2831b002005-11-30 19:55:22 +00002695 mkexpr(rD), mkexpr(rA), mkSzImm(ty, 0),
2696 mkexpr(old_xer_ca) );
cerion70e24122005-03-16 00:27:37 +00002697 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002698 set_XER_OV( ty, PPCG_FLAG_OP_SUBFE,
cerion2831b002005-11-30 19:55:22 +00002699 mkexpr(rD), mkexpr(rA), mkSzImm(ty, 0) );
cerion70e24122005-03-16 00:27:37 +00002700 }
cerionb85e8bb2005-02-16 08:54:33 +00002701 break;
sewardjb51f0f42005-07-18 11:38:02 +00002702 }
cerionae694622005-01-28 17:52:47 +00002703
cerionf0de28c2005-12-13 20:21:11 +00002704
2705 /* 64bit Arithmetic */
cerion5b2325f2005-12-23 00:55:09 +00002706 case 0x49: // mulhd (Multiply High DWord, PPC64 p539)
cerionf0de28c2005-12-13 20:21:11 +00002707 if (flag_OE != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002708 vex_printf("dis_int_arith(ppc)(mulhd,flagOE)\n");
cerionf0de28c2005-12-13 20:21:11 +00002709 return False;
2710 }
cerion5b2325f2005-12-23 00:55:09 +00002711 DIP("mulhd%s r%u,r%u,r%u\n", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00002712 rD_addr, rA_addr, rB_addr);
cerionf0de28c2005-12-13 20:21:11 +00002713 assign( rD, unop(Iop_128HIto64,
cerion07b07a92005-12-22 14:32:35 +00002714 binop(Iop_MullS64,
cerionf0de28c2005-12-13 20:21:11 +00002715 mkexpr(rA), mkexpr(rB))) );
cerion07b07a92005-12-22 14:32:35 +00002716
2717 break;
cerionf0de28c2005-12-13 20:21:11 +00002718
cerion5b2325f2005-12-23 00:55:09 +00002719 case 0x9: // mulhdu (Multiply High DWord Unsigned, PPC64 p540)
cerionf0de28c2005-12-13 20:21:11 +00002720 if (flag_OE != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002721 vex_printf("dis_int_arith(ppc)(mulhdu,flagOE)\n");
cerionf0de28c2005-12-13 20:21:11 +00002722 return False;
2723 }
cerion5b2325f2005-12-23 00:55:09 +00002724 DIP("mulhdu%s r%u,r%u,r%u\n", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00002725 rD_addr, rA_addr, rB_addr);
cerion07b07a92005-12-22 14:32:35 +00002726 assign( rD, unop(Iop_128HIto64,
2727 binop(Iop_MullU64,
2728 mkexpr(rA), mkexpr(rB))) );
2729 break;
cerionf0de28c2005-12-13 20:21:11 +00002730
cerion5b2325f2005-12-23 00:55:09 +00002731 case 0xE9: // mulld (Multiply Low DWord, PPC64 p543)
cerionf0de28c2005-12-13 20:21:11 +00002732 DIP("mulld%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002733 flag_OE ? "o" : "", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00002734 rD_addr, rA_addr, rB_addr);
2735 assign( rD, binop(Iop_Mul64, mkexpr(rA), mkexpr(rB)) );
2736 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002737 set_XER_OV( ty, PPCG_FLAG_OP_MULLW,
cerionf0de28c2005-12-13 20:21:11 +00002738 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
2739 }
2740 break;
2741
cerion5b2325f2005-12-23 00:55:09 +00002742 case 0x1E9: // divd (Divide DWord, PPC64 p419)
cerionf0de28c2005-12-13 20:21:11 +00002743 DIP("divd%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002744 flag_OE ? "o" : "", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00002745 rD_addr, rA_addr, rB_addr);
cerion07b07a92005-12-22 14:32:35 +00002746 assign( rD, binop(Iop_DivS64, mkexpr(rA), mkexpr(rB)) );
2747 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002748 set_XER_OV( ty, PPCG_FLAG_OP_DIVW,
cerion07b07a92005-12-22 14:32:35 +00002749 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
2750 }
2751 break;
cerionf0de28c2005-12-13 20:21:11 +00002752 /* Note:
cerion07b07a92005-12-22 14:32:35 +00002753 if (0x8000_0000_0000_0000 / -1) or (x / 0)
cerionf0de28c2005-12-13 20:21:11 +00002754 => rD=undef, if(flag_rC) CR7=undef, if(flag_OE) XER_OV=1
2755 => But _no_ exception raised. */
2756
cerion5b2325f2005-12-23 00:55:09 +00002757 case 0x1C9: // divdu (Divide DWord Unsigned, PPC64 p420)
cerionf0de28c2005-12-13 20:21:11 +00002758 DIP("divdu%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002759 flag_OE ? "o" : "", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00002760 rD_addr, rA_addr, rB_addr);
cerion07b07a92005-12-22 14:32:35 +00002761 assign( rD, binop(Iop_DivU64, mkexpr(rA), mkexpr(rB)) );
2762 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002763 set_XER_OV( ty, PPCG_FLAG_OP_DIVWU,
cerion07b07a92005-12-22 14:32:35 +00002764 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
2765 }
2766 break;
cerionf0de28c2005-12-13 20:21:11 +00002767 /* Note: ditto comment divd, for (x / 0) */
2768
cerionb85e8bb2005-02-16 08:54:33 +00002769 default:
cerion5b2325f2005-12-23 00:55:09 +00002770 vex_printf("dis_int_arith(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002771 return False;
2772 }
2773 break;
cerionf0de28c2005-12-13 20:21:11 +00002774
cerionb85e8bb2005-02-16 08:54:33 +00002775 default:
cerion5b2325f2005-12-23 00:55:09 +00002776 vex_printf("dis_int_arith(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002777 return False;
2778 }
cerion91ad5362005-01-27 23:02:41 +00002779
cerion76de5cf2005-11-18 18:25:12 +00002780 putIReg( rD_addr, mkexpr(rD) );
2781
2782 if (do_rc && flag_rC) {
2783 set_CR0( mkexpr(rD) );
cerionb85e8bb2005-02-16 08:54:33 +00002784 }
2785 return True;
cerion91ad5362005-01-27 23:02:41 +00002786}
2787
2788
2789
cerion3d870a32005-03-18 12:23:33 +00002790/*
2791 Integer Compare Instructions
2792*/
cerion7aa4bbc2005-01-29 09:32:07 +00002793static Bool dis_int_cmp ( UInt theInstr )
2794{
cerion76de5cf2005-11-18 18:25:12 +00002795 /* D-Form, X-Form */
2796 UChar opc1 = ifieldOPC(theInstr);
2797 UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) );
2798 UChar b22 = toUChar( IFIELD( theInstr, 22, 1 ) );
2799 UChar flag_L = toUChar( IFIELD( theInstr, 21, 1 ) );
2800 UChar rA_addr = ifieldRegA(theInstr);
ceriond953ebb2005-11-29 13:27:20 +00002801 UInt uimm16 = ifieldUIMM16(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00002802 UChar rB_addr = ifieldRegB(theInstr);
2803 UInt opc2 = ifieldOPClo10(theInstr);
2804 UChar b0 = ifieldBIT0(theInstr);
cerion7aa4bbc2005-01-29 09:32:07 +00002805
cerionbb01b7c2005-12-16 13:40:18 +00002806 IRType ty = mode64 ? Ity_I64 : Ity_I32;
2807 IRExpr *a = getIReg(rA_addr);
2808 IRExpr *b;
cerion76de5cf2005-11-18 18:25:12 +00002809
ceriond953ebb2005-11-29 13:27:20 +00002810 if (!mode64 && flag_L==1) { // L==1 invalid for 32 bit.
cerion5b2325f2005-12-23 00:55:09 +00002811 vex_printf("dis_int_cmp(ppc)(flag_L)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002812 return False;
2813 }
2814
cerion76de5cf2005-11-18 18:25:12 +00002815 if (b22 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002816 vex_printf("dis_int_cmp(ppc)(b22)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002817 return False;
2818 }
2819
2820 switch (opc1) {
ceriond953ebb2005-11-29 13:27:20 +00002821 case 0x0B: // cmpi (Compare Immediate, PPC32 p368)
2822 DIP("cmpi cr%u,%u,r%u,%d\n", crfD, flag_L, rA_addr,
2823 (Int)extend_s_16to32(uimm16));
cerion2831b002005-11-30 19:55:22 +00002824 b = mkSzExtendS16( ty, uimm16 );
cerionbb01b7c2005-12-16 13:40:18 +00002825 if (flag_L == 1) {
cerion2831b002005-11-30 19:55:22 +00002826 putCR321(crfD, unop(Iop_64to8, binop(Iop_CmpORD64S, a, b)));
ceriond953ebb2005-11-29 13:27:20 +00002827 } else {
cerionbb01b7c2005-12-16 13:40:18 +00002828 a = mkSzNarrow32( ty, a );
2829 b = mkSzNarrow32( ty, b );
ceriond953ebb2005-11-29 13:27:20 +00002830 putCR321(crfD, unop(Iop_32to8, binop(Iop_CmpORD32S, a, b)));
2831 }
2832 putCR0( crfD, getXER_SO() );
2833 break;
2834
2835 case 0x0A: // cmpli (Compare Logical Immediate, PPC32 p370)
2836 DIP("cmpli cr%u,%u,r%u,0x%x\n", crfD, flag_L, rA_addr, uimm16);
cerion2831b002005-11-30 19:55:22 +00002837 b = mkSzImm( ty, uimm16 );
cerionbb01b7c2005-12-16 13:40:18 +00002838 if (flag_L == 1) {
cerion2831b002005-11-30 19:55:22 +00002839 putCR321(crfD, unop(Iop_64to8, binop(Iop_CmpORD64U, a, b)));
ceriond953ebb2005-11-29 13:27:20 +00002840 } else {
cerionbb01b7c2005-12-16 13:40:18 +00002841 a = mkSzNarrow32( ty, a );
2842 b = mkSzNarrow32( ty, b );
ceriond953ebb2005-11-29 13:27:20 +00002843 putCR321(crfD, unop(Iop_32to8, binop(Iop_CmpORD32U, a, b)));
2844 }
2845 putCR0( crfD, getXER_SO() );
2846 break;
cerionb85e8bb2005-02-16 08:54:33 +00002847
2848 /* X Form */
2849 case 0x1F:
2850 if (b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002851 vex_printf("dis_int_cmp(ppc)(0x1F,b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002852 return False;
2853 }
cerionbb01b7c2005-12-16 13:40:18 +00002854 b = getIReg(rB_addr);
cerion7aa4bbc2005-01-29 09:32:07 +00002855
cerionb85e8bb2005-02-16 08:54:33 +00002856 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00002857 case 0x000: // cmp (Compare, PPC32 p367)
2858 DIP("cmp cr%u,%u,r%u,r%u\n", crfD, flag_L, rA_addr, rB_addr);
cerionbb01b7c2005-12-16 13:40:18 +00002859 if (flag_L == 1) {
2860 putCR321(crfD, unop(Iop_64to8, binop(Iop_CmpORD64S, a, b)));
ceriond953ebb2005-11-29 13:27:20 +00002861 } else {
cerionbb01b7c2005-12-16 13:40:18 +00002862 a = mkSzNarrow32( ty, a );
2863 b = mkSzNarrow32( ty, b );
2864 putCR321(crfD, unop(Iop_32to8,binop(Iop_CmpORD32S, a, b)));
ceriond953ebb2005-11-29 13:27:20 +00002865 }
2866 putCR0( crfD, getXER_SO() );
2867 break;
cerionb85e8bb2005-02-16 08:54:33 +00002868
ceriond953ebb2005-11-29 13:27:20 +00002869 case 0x020: // cmpl (Compare Logical, PPC32 p369)
2870 DIP("cmpl cr%u,%u,r%u,r%u\n", crfD, flag_L, rA_addr, rB_addr);
cerionbb01b7c2005-12-16 13:40:18 +00002871 if (flag_L == 1) {
2872 putCR321(crfD, unop(Iop_64to8, binop(Iop_CmpORD64U, a, b)));
ceriond953ebb2005-11-29 13:27:20 +00002873 } else {
cerionbb01b7c2005-12-16 13:40:18 +00002874 a = mkSzNarrow32( ty, a );
2875 b = mkSzNarrow32( ty, b );
2876 putCR321(crfD, unop(Iop_32to8, binop(Iop_CmpORD32U, a, b)));
ceriond953ebb2005-11-29 13:27:20 +00002877 }
2878 putCR0( crfD, getXER_SO() );
2879 break;
cerion7aa4bbc2005-01-29 09:32:07 +00002880
ceriond953ebb2005-11-29 13:27:20 +00002881 default:
cerion5b2325f2005-12-23 00:55:09 +00002882 vex_printf("dis_int_cmp(ppc)(opc2)\n");
ceriond953ebb2005-11-29 13:27:20 +00002883 return False;
cerionb85e8bb2005-02-16 08:54:33 +00002884 }
2885 break;
ceriond953ebb2005-11-29 13:27:20 +00002886
cerionb85e8bb2005-02-16 08:54:33 +00002887 default:
cerion5b2325f2005-12-23 00:55:09 +00002888 vex_printf("dis_int_cmp(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002889 return False;
2890 }
2891
cerionb85e8bb2005-02-16 08:54:33 +00002892 return True;
cerion7aa4bbc2005-01-29 09:32:07 +00002893}
2894
2895
cerion3d870a32005-03-18 12:23:33 +00002896/*
2897 Integer Logical Instructions
2898*/
cerion7aa4bbc2005-01-29 09:32:07 +00002899static Bool dis_int_logic ( UInt theInstr )
2900{
cerion76de5cf2005-11-18 18:25:12 +00002901 /* D-Form, X-Form */
2902 UChar opc1 = ifieldOPC(theInstr);
2903 UChar rS_addr = ifieldRegDS(theInstr);
2904 UChar rA_addr = ifieldRegA(theInstr);
ceriond953ebb2005-11-29 13:27:20 +00002905 UInt uimm16 = ifieldUIMM16(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00002906 UChar rB_addr = ifieldRegB(theInstr);
2907 UInt opc2 = ifieldOPClo10(theInstr);
2908 UChar flag_rC = ifieldBIT0(theInstr);
cerionb85e8bb2005-02-16 08:54:33 +00002909
ceriond953ebb2005-11-29 13:27:20 +00002910 IRType ty = mode64 ? Ity_I64 : Ity_I32;
2911 IRTemp rS = newTemp(ty);
2912 IRTemp rA = newTemp(ty);
2913 IRTemp rB = newTemp(ty);
cerione9d361a2005-03-04 17:35:29 +00002914 IRExpr* irx;
ceriond953ebb2005-11-29 13:27:20 +00002915 Bool do_rc = False;
2916
cerion76de5cf2005-11-18 18:25:12 +00002917 assign( rS, getIReg(rS_addr) );
2918 assign( rB, getIReg(rB_addr) );
cerionb85e8bb2005-02-16 08:54:33 +00002919
2920 switch (opc1) {
cerione9d361a2005-03-04 17:35:29 +00002921 case 0x1C: // andi. (AND Immediate, PPC32 p358)
ceriond953ebb2005-11-29 13:27:20 +00002922 DIP("andi. r%u,r%u,0x%x\n", rA_addr, rS_addr, uimm16);
cerion2831b002005-11-30 19:55:22 +00002923 assign( rA, binop( mkSzOp(ty, Iop_And8), mkexpr(rS),
2924 mkSzImm(ty, uimm16)) );
cerion70e24122005-03-16 00:27:37 +00002925 do_rc = True; // Always record to CR
cerion76de5cf2005-11-18 18:25:12 +00002926 flag_rC = 1;
cerionb85e8bb2005-02-16 08:54:33 +00002927 break;
2928
cerione9d361a2005-03-04 17:35:29 +00002929 case 0x1D: // andis. (AND Immediate Shifted, PPC32 p359)
ceriond953ebb2005-11-29 13:27:20 +00002930 DIP("andis r%u,r%u,0x%x\n", rA_addr, rS_addr, uimm16);
cerion2831b002005-11-30 19:55:22 +00002931 assign( rA, binop( mkSzOp(ty, Iop_And8), mkexpr(rS),
2932 mkSzImm(ty, uimm16 << 16)) );
cerion70e24122005-03-16 00:27:37 +00002933 do_rc = True; // Always record to CR
cerion76de5cf2005-11-18 18:25:12 +00002934 flag_rC = 1;
cerionb85e8bb2005-02-16 08:54:33 +00002935 break;
cerion7aa4bbc2005-01-29 09:32:07 +00002936
cerione9d361a2005-03-04 17:35:29 +00002937 case 0x18: // ori (OR Immediate, PPC32 p497)
ceriond953ebb2005-11-29 13:27:20 +00002938 DIP("ori r%u,r%u,0x%x\n", rA_addr, rS_addr, uimm16);
cerion2831b002005-11-30 19:55:22 +00002939 assign( rA, binop( mkSzOp(ty, Iop_Or8), mkexpr(rS),
2940 mkSzImm(ty, uimm16)) );
cerionb85e8bb2005-02-16 08:54:33 +00002941 break;
cerion7aa4bbc2005-01-29 09:32:07 +00002942
cerione9d361a2005-03-04 17:35:29 +00002943 case 0x19: // oris (OR Immediate Shifted, PPC32 p498)
ceriond953ebb2005-11-29 13:27:20 +00002944 DIP("oris r%u,r%u,0x%x\n", rA_addr, rS_addr, uimm16);
cerion2831b002005-11-30 19:55:22 +00002945 assign( rA, binop( mkSzOp(ty, Iop_Or8), mkexpr(rS),
2946 mkSzImm(ty, uimm16 << 16)) );
cerionb85e8bb2005-02-16 08:54:33 +00002947 break;
cerionaabdfbf2005-01-29 12:56:15 +00002948
cerione9d361a2005-03-04 17:35:29 +00002949 case 0x1A: // xori (XOR Immediate, PPC32 p550)
ceriond953ebb2005-11-29 13:27:20 +00002950 DIP("xori r%u,r%u,0x%x\n", rA_addr, rS_addr, uimm16);
cerion2831b002005-11-30 19:55:22 +00002951 assign( rA, binop( mkSzOp(ty, Iop_Xor8), mkexpr(rS),
2952 mkSzImm(ty, uimm16)) );
cerionb85e8bb2005-02-16 08:54:33 +00002953 break;
cerion38674602005-02-08 02:19:25 +00002954
cerione9d361a2005-03-04 17:35:29 +00002955 case 0x1B: // xoris (XOR Immediate Shifted, PPC32 p551)
ceriond953ebb2005-11-29 13:27:20 +00002956 DIP("xoris r%u,r%u,0x%x\n", rA_addr, rS_addr, uimm16);
cerion2831b002005-11-30 19:55:22 +00002957 assign( rA, binop( mkSzOp(ty, Iop_Xor8), mkexpr(rS),
2958 mkSzImm(ty, uimm16 << 16)) );
cerionb85e8bb2005-02-16 08:54:33 +00002959 break;
cerionaabdfbf2005-01-29 12:56:15 +00002960
cerionb85e8bb2005-02-16 08:54:33 +00002961 /* X Form */
2962 case 0x1F:
cerion70e24122005-03-16 00:27:37 +00002963 do_rc = True; // All below record to CR
2964
cerionb85e8bb2005-02-16 08:54:33 +00002965 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00002966 case 0x01C: // and (AND, PPC32 p356)
2967 DIP("and%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002968 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00002969 assign(rA, binop( mkSzOp(ty, Iop_And8),
ceriond953ebb2005-11-29 13:27:20 +00002970 mkexpr(rS), mkexpr(rB)));
2971 break;
2972
2973 case 0x03C: // andc (AND with Complement, PPC32 p357)
2974 DIP("andc%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002975 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00002976 assign(rA, binop( mkSzOp(ty, Iop_And8), mkexpr(rS),
2977 unop( mkSzOp(ty, Iop_Not8),
ceriond953ebb2005-11-29 13:27:20 +00002978 mkexpr(rB))));
2979 break;
2980
2981 case 0x01A: { // cntlzw (Count Leading Zeros Word, PPC32 p371)
2982 IRExpr* lo32;
2983 if (rB_addr!=0) {
cerion5b2325f2005-12-23 00:55:09 +00002984 vex_printf("dis_int_logic(ppc)(cntlzw,rB_addr)\n");
ceriond953ebb2005-11-29 13:27:20 +00002985 return False;
2986 }
2987 DIP("cntlzw%s r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002988 flag_rC ? ".":"", rA_addr, rS_addr);
ceriond953ebb2005-11-29 13:27:20 +00002989
2990 // mode64: count in low word only
2991 lo32 = mode64 ? unop(Iop_64to32, mkexpr(rS)) : mkexpr(rS);
2992
2993 // Iop_Clz32 undefined for arg==0, so deal with that case:
2994 irx = binop(Iop_CmpNE32, lo32, mkU32(0));
cerion5b2325f2005-12-23 00:55:09 +00002995 assign(rA, mkSzWiden32(ty,
2996 IRExpr_Mux0X( unop(Iop_1Uto8, irx),
2997 mkU32(32),
2998 unop(Iop_Clz32, lo32)),
2999 False));
3000
ceriond953ebb2005-11-29 13:27:20 +00003001 // TODO: alternatively: assign(rA, verbose_Clz32(rS));
3002 break;
3003 }
3004
3005 case 0x11C: // eqv (Equivalent, PPC32 p396)
3006 DIP("eqv%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003007 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003008 assign( rA, unop( mkSzOp(ty, Iop_Not8),
3009 binop( mkSzOp(ty, Iop_Xor8),
ceriond953ebb2005-11-29 13:27:20 +00003010 mkexpr(rS), mkexpr(rB))) );
sewardj20ef5472005-07-21 14:48:31 +00003011 break;
cerion7aa4bbc2005-01-29 09:32:07 +00003012
cerione9d361a2005-03-04 17:35:29 +00003013 case 0x3BA: // extsb (Extend Sign Byte, PPC32 p397
cerion76de5cf2005-11-18 18:25:12 +00003014 if (rB_addr!=0) {
cerion5b2325f2005-12-23 00:55:09 +00003015 vex_printf("dis_int_logic(ppc)(extsb,rB_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003016 return False;
3017 }
ceriond953ebb2005-11-29 13:27:20 +00003018 DIP("extsb%s r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003019 flag_rC ? ".":"", rA_addr, rS_addr);
ceriond953ebb2005-11-29 13:27:20 +00003020 if (mode64)
3021 assign( rA, unop(Iop_8Sto64, unop(Iop_64to8, mkexpr(rS))) );
3022 else
3023 assign( rA, unop(Iop_8Sto32, unop(Iop_32to8, mkexpr(rS))) );
cerionb85e8bb2005-02-16 08:54:33 +00003024 break;
cerion7aa4bbc2005-01-29 09:32:07 +00003025
cerione9d361a2005-03-04 17:35:29 +00003026 case 0x39A: // extsh (Extend Sign Half Word, PPC32 p398)
cerion76de5cf2005-11-18 18:25:12 +00003027 if (rB_addr!=0) {
cerion5b2325f2005-12-23 00:55:09 +00003028 vex_printf("dis_int_logic(ppc)(extsh,rB_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003029 return False;
3030 }
ceriond953ebb2005-11-29 13:27:20 +00003031 DIP("extsh%s r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003032 flag_rC ? ".":"", rA_addr, rS_addr);
ceriond953ebb2005-11-29 13:27:20 +00003033 if (mode64)
cerion5b2325f2005-12-23 00:55:09 +00003034 assign( rA, unop(Iop_16Sto64,
3035 unop(Iop_64to16, mkexpr(rS))) );
ceriond953ebb2005-11-29 13:27:20 +00003036 else
cerion5b2325f2005-12-23 00:55:09 +00003037 assign( rA, unop(Iop_16Sto32,
3038 unop(Iop_32to16, mkexpr(rS))) );
cerionb85e8bb2005-02-16 08:54:33 +00003039 break;
cerion7aa4bbc2005-01-29 09:32:07 +00003040
cerione9d361a2005-03-04 17:35:29 +00003041 case 0x1DC: // nand (NAND, PPC32 p492)
ceriond953ebb2005-11-29 13:27:20 +00003042 DIP("nand%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003043 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003044 assign( rA, unop( mkSzOp(ty, Iop_Not8),
3045 binop( mkSzOp(ty, Iop_And8),
ceriond953ebb2005-11-29 13:27:20 +00003046 mkexpr(rS), mkexpr(rB))) );
cerionb85e8bb2005-02-16 08:54:33 +00003047 break;
3048
cerione9d361a2005-03-04 17:35:29 +00003049 case 0x07C: // nor (NOR, PPC32 p494)
ceriond953ebb2005-11-29 13:27:20 +00003050 DIP("nor%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003051 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003052 assign( rA, unop( mkSzOp(ty, Iop_Not8),
3053 binop( mkSzOp(ty, Iop_Or8),
ceriond953ebb2005-11-29 13:27:20 +00003054 mkexpr(rS), mkexpr(rB))) );
cerionb85e8bb2005-02-16 08:54:33 +00003055 break;
cerion7aa4bbc2005-01-29 09:32:07 +00003056
cerione9d361a2005-03-04 17:35:29 +00003057 case 0x1BC: // or (OR, PPC32 p495)
cerion76de5cf2005-11-18 18:25:12 +00003058 if ((!flag_rC) && rS_addr == rB_addr) {
ceriond953ebb2005-11-29 13:27:20 +00003059 DIP("mr r%u,r%u\n", rA_addr, rS_addr);
cerion76de5cf2005-11-18 18:25:12 +00003060 assign( rA, mkexpr(rS) );
sewardjb51f0f42005-07-18 11:38:02 +00003061 } else {
ceriond953ebb2005-11-29 13:27:20 +00003062 DIP("or%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003063 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003064 assign( rA, binop( mkSzOp(ty, Iop_Or8),
ceriond953ebb2005-11-29 13:27:20 +00003065 mkexpr(rS), mkexpr(rB)) );
sewardjb51f0f42005-07-18 11:38:02 +00003066 }
cerionb85e8bb2005-02-16 08:54:33 +00003067 break;
cerion7aa4bbc2005-01-29 09:32:07 +00003068
cerione9d361a2005-03-04 17:35:29 +00003069 case 0x19C: // orc (OR with Complement, PPC32 p496)
ceriond953ebb2005-11-29 13:27:20 +00003070 DIP("orc%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003071 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003072 assign( rA, binop( mkSzOp(ty, Iop_Or8), mkexpr(rS),
3073 unop(mkSzOp(ty, Iop_Not8), mkexpr(rB))));
cerionb85e8bb2005-02-16 08:54:33 +00003074 break;
3075
cerione9d361a2005-03-04 17:35:29 +00003076 case 0x13C: // xor (XOR, PPC32 p549)
ceriond953ebb2005-11-29 13:27:20 +00003077 DIP("xor%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003078 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003079 assign( rA, binop( mkSzOp(ty, Iop_Xor8),
ceriond953ebb2005-11-29 13:27:20 +00003080 mkexpr(rS), mkexpr(rB)) );
cerionb85e8bb2005-02-16 08:54:33 +00003081 break;
cerion7aa4bbc2005-01-29 09:32:07 +00003082
cerionf0de28c2005-12-13 20:21:11 +00003083
3084 /* 64bit Integer Logical Instructions */
3085 case 0x3DA: // extsw (Extend Sign Word, PPC64 p430)
3086 if (rB_addr!=0) {
cerion5b2325f2005-12-23 00:55:09 +00003087 vex_printf("dis_int_logic(ppc)(extsw,rB_addr)\n");
cerionf0de28c2005-12-13 20:21:11 +00003088 return False;
3089 }
cerion5b2325f2005-12-23 00:55:09 +00003090 DIP("extsw%s r%u,r%u\n", flag_rC ? ".":"", rA_addr, rS_addr);
cerionf0de28c2005-12-13 20:21:11 +00003091 assign(rA, unop(Iop_32Sto64, unop(Iop_64to32, mkexpr(rS))));
3092 break;
3093
cerion5b2325f2005-12-23 00:55:09 +00003094 case 0x03A: // cntlzd (Count Leading Zeros DWord, PPC64 p401)
cerionf0de28c2005-12-13 20:21:11 +00003095 if (rB_addr!=0) {
cerion5b2325f2005-12-23 00:55:09 +00003096 vex_printf("dis_int_logic(ppc)(cntlzd,rB_addr)\n");
cerionf0de28c2005-12-13 20:21:11 +00003097 return False;
3098 }
cerion5b2325f2005-12-23 00:55:09 +00003099 DIP("cntlzd%s r%u,r%u\n",
3100 flag_rC ? ".":"", rA_addr, rS_addr);
cerion07b07a92005-12-22 14:32:35 +00003101 // Iop_Clz64 undefined for arg==0, so deal with that case:
3102 irx = binop(Iop_CmpNE64, mkexpr(rS), mkU64(0));
3103 assign(rA, IRExpr_Mux0X( unop(Iop_1Uto8, irx),
3104 mkU64(64),
3105 unop(Iop_Clz64, mkexpr(rS)) ));
cerion5b2325f2005-12-23 00:55:09 +00003106 // TODO: alternatively: assign(rA, verbose_Clz64(rS));
cerion07b07a92005-12-22 14:32:35 +00003107 break;
cerionf0de28c2005-12-13 20:21:11 +00003108
cerionb85e8bb2005-02-16 08:54:33 +00003109 default:
cerion5b2325f2005-12-23 00:55:09 +00003110 vex_printf("dis_int_logic(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003111 return False;
3112 }
cerionb85e8bb2005-02-16 08:54:33 +00003113 break;
3114
3115 default:
cerion5b2325f2005-12-23 00:55:09 +00003116 vex_printf("dis_int_logic(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003117 return False;
3118 }
cerion70e24122005-03-16 00:27:37 +00003119
cerion76de5cf2005-11-18 18:25:12 +00003120 putIReg( rA_addr, mkexpr(rA) );
3121
3122 if (do_rc && flag_rC) {
3123 set_CR0( mkexpr(rA) );
cerionb85e8bb2005-02-16 08:54:33 +00003124 }
3125 return True;
cerion645c9302005-01-31 10:09:59 +00003126}
3127
3128
3129
cerion3d870a32005-03-18 12:23:33 +00003130/*
3131 Integer Rotate Instructions
3132*/
cerion645c9302005-01-31 10:09:59 +00003133static Bool dis_int_rot ( UInt theInstr )
3134{
cerionf0de28c2005-12-13 20:21:11 +00003135 /* M-Form, MDS-Form */
ceriond953ebb2005-11-29 13:27:20 +00003136 UChar opc1 = ifieldOPC(theInstr);
3137 UChar rS_addr = ifieldRegDS(theInstr);
3138 UChar rA_addr = ifieldRegA(theInstr);
3139 UChar rB_addr = ifieldRegB(theInstr);
3140 UChar sh_imm = rB_addr;
3141 UChar MaskBeg = toUChar( IFIELD( theInstr, 6, 5 ) );
3142 UChar MaskEnd = toUChar( IFIELD( theInstr, 1, 5 ) );
cerionf0de28c2005-12-13 20:21:11 +00003143 UChar msk_imm = toUChar( IFIELD( theInstr, 5, 6 ) );
3144 UChar opc2 = toUChar( IFIELD( theInstr, 2, 3 ) );
3145 UChar b1 = ifieldBIT1(theInstr);
ceriond953ebb2005-11-29 13:27:20 +00003146 UChar flag_rC = ifieldBIT0(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00003147
ceriond953ebb2005-11-29 13:27:20 +00003148 IRType ty = mode64 ? Ity_I64 : Ity_I32;
3149 IRTemp rS = newTemp(ty);
3150 IRTemp rA = newTemp(ty);
3151 IRTemp rB = newTemp(ty);
cerionbb01b7c2005-12-16 13:40:18 +00003152 IRTemp rot = newTemp(ty);
ceriond953ebb2005-11-29 13:27:20 +00003153 IRExpr *r;
cerionf0de28c2005-12-13 20:21:11 +00003154 UInt mask32;
3155 ULong mask64;
ceriond953ebb2005-11-29 13:27:20 +00003156
cerion76de5cf2005-11-18 18:25:12 +00003157 assign( rS, getIReg(rS_addr) );
3158 assign( rB, getIReg(rB_addr) );
ceriond953ebb2005-11-29 13:27:20 +00003159
cerionb85e8bb2005-02-16 08:54:33 +00003160 switch (opc1) {
ceriond953ebb2005-11-29 13:27:20 +00003161 case 0x14: {
cerion5b2325f2005-12-23 00:55:09 +00003162 // rlwimi (Rotate Left Word Imm then Mask Insert, PPC32 p500)
3163 DIP("rlwimi%s r%u,r%u,%d,%d,%d\n", flag_rC ? ".":"",
ceriond953ebb2005-11-29 13:27:20 +00003164 rA_addr, rS_addr, sh_imm, MaskBeg, MaskEnd);
3165 if (mode64) {
3166 // tmp32 = (ROTL(rS_Lo32, Imm)
3167 // rA = ((tmp32 || tmp32) & mask64) | (rA & ~mask64)
cerionf0de28c2005-12-13 20:21:11 +00003168 mask64 = MASK64(31-MaskEnd, 31-MaskBeg);
ceriond953ebb2005-11-29 13:27:20 +00003169 r = ROTL( unop(Iop_64to32, mkexpr(rS) ), mkU8(sh_imm) );
3170 r = unop(Iop_32Uto64, r);
cerion5b2325f2005-12-23 00:55:09 +00003171 assign( rot, binop(Iop_Or64, r,
3172 binop(Iop_Shl64, r, mkU8(32))) );
ceriond953ebb2005-11-29 13:27:20 +00003173 assign( rA,
3174 binop(Iop_Or64,
cerionbb01b7c2005-12-16 13:40:18 +00003175 binop(Iop_And64, mkexpr(rot), mkU64(mask64)),
cerionf0de28c2005-12-13 20:21:11 +00003176 binop(Iop_And64, getIReg(rA_addr), mkU64(~mask64))) );
sewardj26b33202005-10-07 09:45:16 +00003177 }
3178 else {
ceriond953ebb2005-11-29 13:27:20 +00003179 // rA = (ROTL(rS, Imm) & mask) | (rA & ~mask);
cerionf0de28c2005-12-13 20:21:11 +00003180 mask32 = MASK32(31-MaskEnd, 31-MaskBeg);
3181 r = ROTL(mkexpr(rS), mkU8(sh_imm));
ceriond953ebb2005-11-29 13:27:20 +00003182 assign( rA,
3183 binop(Iop_Or32,
cerionf0de28c2005-12-13 20:21:11 +00003184 binop(Iop_And32, mkU32(mask32), r),
3185 binop(Iop_And32, getIReg(rA_addr), mkU32(~mask32))) );
sewardj26b33202005-10-07 09:45:16 +00003186 }
cerionb85e8bb2005-02-16 08:54:33 +00003187 break;
ceriond953ebb2005-11-29 13:27:20 +00003188 }
cerion45b70ff2005-01-31 17:03:25 +00003189
ceriond953ebb2005-11-29 13:27:20 +00003190 case 0x15: {
cerion5b2325f2005-12-23 00:55:09 +00003191 // rlwinm (Rotate Left Word Imm then AND with Mask, PPC32 p501)
ceriond953ebb2005-11-29 13:27:20 +00003192 vassert(MaskBeg < 32);
3193 vassert(MaskEnd < 32);
3194 vassert(sh_imm < 32);
3195
3196 if (mode64) {
cerionf0de28c2005-12-13 20:21:11 +00003197 mask64 = MASK64(31-MaskEnd, 31-MaskBeg);
cerion5b2325f2005-12-23 00:55:09 +00003198 DIP("rlwinm%s r%u,r%u,%d,%d,%d\n", flag_rC ? ".":"",
ceriond953ebb2005-11-29 13:27:20 +00003199 rA_addr, rS_addr, sh_imm, MaskBeg, MaskEnd);
3200 // tmp32 = (ROTL(rS_Lo32, Imm)
3201 // rA = ((tmp32 || tmp32) & mask64)
3202 r = ROTL( unop(Iop_64to32, mkexpr(rS) ), mkU8(sh_imm) );
3203 r = unop(Iop_32Uto64, r);
cerion5b2325f2005-12-23 00:55:09 +00003204 assign( rot, binop(Iop_Or64, r,
3205 binop(Iop_Shl64, r, mkU8(32))) );
cerionbb01b7c2005-12-16 13:40:18 +00003206 assign( rA, binop(Iop_And64, mkexpr(rot), mkU64(mask64)) );
ceriond953ebb2005-11-29 13:27:20 +00003207 }
3208 else {
3209 if (MaskBeg == 0 && sh_imm+MaskEnd == 31) {
3210 /* Special-case the ,n,0,31-n form as that is just n-bit
cerion5b2325f2005-12-23 00:55:09 +00003211 shift left, PPC32 p501 */
3212 DIP("slwi%s r%u,r%u,%d\n", flag_rC ? ".":"",
ceriond953ebb2005-11-29 13:27:20 +00003213 rA_addr, rS_addr, sh_imm);
3214 assign( rA, binop(Iop_Shl32, mkexpr(rS), mkU8(sh_imm)) );
3215 }
cerion2831b002005-11-30 19:55:22 +00003216 else if (MaskEnd == 31 && sh_imm+MaskBeg == 32) {
3217 /* Special-case the ,32-n,n,31 form as that is just n-bit
cerion5b2325f2005-12-23 00:55:09 +00003218 unsigned shift right, PPC32 p501 */
3219 DIP("srwi%s r%u,r%u,%d\n", flag_rC ? ".":"",
cerion2831b002005-11-30 19:55:22 +00003220 rA_addr, rS_addr, sh_imm);
3221 assign( rA, binop(Iop_Shr32, mkexpr(rS), mkU8(MaskBeg)) );
3222 }
3223 else {
3224 /* General case. */
cerionf0de28c2005-12-13 20:21:11 +00003225 mask32 = MASK32(31-MaskEnd, 31-MaskBeg);
cerion5b2325f2005-12-23 00:55:09 +00003226 DIP("rlwinm%s r%u,r%u,%d,%d,%d\n", flag_rC ? ".":"",
cerion2831b002005-11-30 19:55:22 +00003227 rA_addr, rS_addr, sh_imm, MaskBeg, MaskEnd);
3228 // rA = ROTL(rS, Imm) & mask
cerion5b2325f2005-12-23 00:55:09 +00003229 assign( rA, binop(Iop_And32,
3230 ROTL(mkexpr(rS), mkU8(sh_imm)),
cerionf0de28c2005-12-13 20:21:11 +00003231 mkU32(mask32)) );
cerion2831b002005-11-30 19:55:22 +00003232 }
ceriond953ebb2005-11-29 13:27:20 +00003233 }
sewardjc9659532005-07-21 21:33:57 +00003234 break;
ceriond953ebb2005-11-29 13:27:20 +00003235 }
3236
3237 case 0x17: {
3238 // rlwnm (Rotate Left Word then AND with Mask, PPC32 p503
cerion5b2325f2005-12-23 00:55:09 +00003239 DIP("rlwnm%s r%u,r%u,r%u,%d,%d\n", flag_rC ? ".":"",
ceriond953ebb2005-11-29 13:27:20 +00003240 rA_addr, rS_addr, rB_addr, MaskBeg, MaskEnd);
3241 if (mode64) {
cerionf0de28c2005-12-13 20:21:11 +00003242 mask64 = MASK64(31-MaskEnd, 31-MaskBeg);
cerionbb01b7c2005-12-16 13:40:18 +00003243 /* weird insn alert!
3244 tmp32 = (ROTL(rS_Lo32, rB[0-4])
3245 rA = ((tmp32 || tmp32) & mask64)
3246 */
ceriond953ebb2005-11-29 13:27:20 +00003247 // note, ROTL does the masking, so we don't do it here
3248 r = ROTL( unop(Iop_64to32, mkexpr(rS)),
cerionbb01b7c2005-12-16 13:40:18 +00003249 unop(Iop_64to8, mkexpr(rB)) );
ceriond953ebb2005-11-29 13:27:20 +00003250 r = unop(Iop_32Uto64, r);
cerionbb01b7c2005-12-16 13:40:18 +00003251 assign(rot, binop(Iop_Or64, r, binop(Iop_Shl64, r, mkU8(32))));
3252 assign( rA, binop(Iop_And64, mkexpr(rot), mkU64(mask64)) );
ceriond953ebb2005-11-29 13:27:20 +00003253 } else {
cerionf0de28c2005-12-13 20:21:11 +00003254 mask32 = MASK32(31-MaskEnd, 31-MaskBeg);
ceriond953ebb2005-11-29 13:27:20 +00003255 // rA = ROTL(rS, rB[0-4]) & mask
3256 // note, ROTL does the masking, so we don't do it here
3257 assign( rA, binop(Iop_And32,
cerion5b2325f2005-12-23 00:55:09 +00003258 ROTL(mkexpr(rS),
3259 unop(Iop_32to8, mkexpr(rB))),
cerionf0de28c2005-12-13 20:21:11 +00003260 mkU32(mask32)) );
ceriond953ebb2005-11-29 13:27:20 +00003261 }
3262 break;
3263 }
cerion45b70ff2005-01-31 17:03:25 +00003264
cerionf0de28c2005-12-13 20:21:11 +00003265
3266 /* 64bit Integer Rotates */
3267 case 0x1E: {
3268 msk_imm = ((msk_imm & 1) << 5) | (msk_imm >> 1);
3269 sh_imm |= b1 << 5;
3270
3271 vassert( msk_imm < 64 );
3272 vassert( sh_imm < 64 );
3273
3274 switch (opc2) {
cerion07b07a92005-12-22 14:32:35 +00003275 case 0x4: {
3276 /* r = ROTL64( rS, rB_lo6) */
3277 r = ROTL( mkexpr(rS), unop(Iop_64to8, mkexpr(rB)) );
3278
cerion5b2325f2005-12-23 00:55:09 +00003279 if (b1 == 0) { // rldcl (Rotl DWord, Clear Left, PPC64 p555)
3280 DIP("rldcl%s r%u,r%u,r%u,%u\n", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00003281 rA_addr, rS_addr, rB_addr, msk_imm);
cerion07b07a92005-12-22 14:32:35 +00003282 // note, ROTL does the masking, so we don't do it here
cerionf0de28c2005-12-13 20:21:11 +00003283 mask64 = MASK64(0, 63-msk_imm);
3284 assign( rA, binop(Iop_And64, r, mkU64(mask64)) );
3285 break;
cerion5b2325f2005-12-23 00:55:09 +00003286 } else { // rldcr (Rotl DWord, Clear Right, PPC64 p556)
3287 DIP("rldcr%s r%u,r%u,r%u,%u\n", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00003288 rA_addr, rS_addr, rB_addr, msk_imm);
cerionf0de28c2005-12-13 20:21:11 +00003289 mask64 = MASK64(63-msk_imm, 63);
3290 assign( rA, binop(Iop_And64, r, mkU64(mask64)) );
3291 break;
3292 }
3293 break;
cerion07b07a92005-12-22 14:32:35 +00003294 }
cerion5b2325f2005-12-23 00:55:09 +00003295 case 0x2: // rldic (Rotl DWord Imm, Clear, PPC64 p557)
3296 DIP("rldic%s r%u,r%u,%u,%u\n", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00003297 rA_addr, rS_addr, sh_imm, msk_imm);
3298 r = ROTL(mkexpr(rS), mkU8(sh_imm));
3299 mask64 = MASK64(sh_imm, 63-msk_imm);
3300 assign( rA, binop(Iop_And64, r, mkU64(mask64)) );
3301 break;
3302 // later: deal with special case: (msk_imm==0) => SHL(sh_imm)
3303 /*
3304 Hmm... looks like this'll do the job more simply:
3305 r = SHL(rS, sh_imm)
3306 m = ~(1 << (63-msk_imm))
3307 assign(rA, r & m);
3308 */
3309
cerion5b2325f2005-12-23 00:55:09 +00003310 case 0x0: // rldicl (Rotl DWord Imm, Clear Left, PPC64 p558)
3311 DIP("rldicl%s r%u,r%u,%u,%u\n", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00003312 rA_addr, rS_addr, sh_imm, msk_imm);
3313 r = ROTL(mkexpr(rS), mkU8(sh_imm));
3314 mask64 = MASK64(0, 63-msk_imm);
3315 assign( rA, binop(Iop_And64, r, mkU64(mask64)) );
3316 break;
cerion5b2325f2005-12-23 00:55:09 +00003317 /* later: deal with special case:
3318 (msk_imm + sh_imm == 63) => SHR(63 - sh_imm) */
cerionf0de28c2005-12-13 20:21:11 +00003319
cerion5b2325f2005-12-23 00:55:09 +00003320 case 0x1: // rldicr (Rotl DWord Imm, Clear Right, PPC64 p559)
3321 DIP("rldicr%s r%u,r%u,%u,%u\n", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00003322 rA_addr, rS_addr, sh_imm, msk_imm);
3323 r = ROTL(mkexpr(rS), mkU8(sh_imm));
3324 mask64 = MASK64(63-msk_imm, 63);
3325 assign( rA, binop(Iop_And64, r, mkU64(mask64)) );
3326 break;
cerion5b2325f2005-12-23 00:55:09 +00003327 /* later: deal with special case:
3328 (msk_imm == sh_imm) => SHL(sh_imm) */
cerionf0de28c2005-12-13 20:21:11 +00003329
cerion5b2325f2005-12-23 00:55:09 +00003330 case 0x3: { // rldimi (Rotl DWord Imm, Mask Insert, PPC64 p560)
cerion07b07a92005-12-22 14:32:35 +00003331 IRTemp rA_orig = newTemp(ty);
cerion5b2325f2005-12-23 00:55:09 +00003332 DIP("rldimi%s r%u,r%u,%u,%u\n", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00003333 rA_addr, rS_addr, sh_imm, msk_imm);
3334 r = ROTL(mkexpr(rS), mkU8(sh_imm));
3335 mask64 = MASK64(sh_imm, 63-msk_imm);
cerion07b07a92005-12-22 14:32:35 +00003336 assign( rA_orig, getIReg(rA_addr) );
cerionf0de28c2005-12-13 20:21:11 +00003337 assign( rA, binop(Iop_Or64,
3338 binop(Iop_And64, mkU64(mask64), r),
cerion5b2325f2005-12-23 00:55:09 +00003339 binop(Iop_And64, mkU64(~mask64),
3340 mkexpr(rA_orig))) );
cerionf0de28c2005-12-13 20:21:11 +00003341 break;
cerion07b07a92005-12-22 14:32:35 +00003342 }
cerionf0de28c2005-12-13 20:21:11 +00003343 default:
cerion5b2325f2005-12-23 00:55:09 +00003344 vex_printf("dis_int_rot(ppc)(opc2)\n");
cerionf0de28c2005-12-13 20:21:11 +00003345 return False;
3346 }
3347 break;
3348 }
3349
cerionb85e8bb2005-02-16 08:54:33 +00003350 default:
cerion5b2325f2005-12-23 00:55:09 +00003351 vex_printf("dis_int_rot(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003352 return False;
3353 }
cerion645c9302005-01-31 10:09:59 +00003354
cerion76de5cf2005-11-18 18:25:12 +00003355 putIReg( rA_addr, mkexpr(rA) );
3356
3357 if (flag_rC) {
3358 set_CR0( mkexpr(rA) );
cerionb85e8bb2005-02-16 08:54:33 +00003359 }
3360 return True;
cerion645c9302005-01-31 10:09:59 +00003361}
3362
3363
cerion3d870a32005-03-18 12:23:33 +00003364/*
3365 Integer Load Instructions
3366*/
cerion645c9302005-01-31 10:09:59 +00003367static Bool dis_int_load ( UInt theInstr )
3368{
cerionf0de28c2005-12-13 20:21:11 +00003369 /* D-Form, X-Form, DS-Form */
cerion76de5cf2005-11-18 18:25:12 +00003370 UChar opc1 = ifieldOPC(theInstr);
3371 UChar rD_addr = ifieldRegDS(theInstr);
3372 UChar rA_addr = ifieldRegA(theInstr);
ceriond953ebb2005-11-29 13:27:20 +00003373 UInt uimm16 = ifieldUIMM16(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00003374 UChar rB_addr = ifieldRegB(theInstr);
3375 UInt opc2 = ifieldOPClo10(theInstr);
cerionf0de28c2005-12-13 20:21:11 +00003376 UChar b1 = ifieldBIT1(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00003377 UChar b0 = ifieldBIT0(theInstr);
3378
ceriond953ebb2005-11-29 13:27:20 +00003379 Int simm16 = extend_s_16to32(uimm16);
3380 IRType ty = mode64 ? Ity_I64 : Ity_I32;
ceriond953ebb2005-11-29 13:27:20 +00003381 IRTemp EA = newTemp(ty);
3382 IRExpr* val;
cerionedf7fc52005-11-18 20:57:41 +00003383
cerionf0de28c2005-12-13 20:21:11 +00003384 switch (opc1) {
3385 case 0x1F: // register offset
ceriond953ebb2005-11-29 13:27:20 +00003386 assign( EA, ea_rAor0_idxd( rA_addr, rB_addr ) );
cerionf0de28c2005-12-13 20:21:11 +00003387 break;
3388 case 0x3A: // immediate offset: 64bit
3389 simm16 = simm16 & 0xFFFFFFFC;
3390 default: // immediate offset
3391 assign( EA, ea_rAor0_simm( rA_addr, simm16 ) );
3392 break;
ceriond953ebb2005-11-29 13:27:20 +00003393 }
cerione9d361a2005-03-04 17:35:29 +00003394
cerionb85e8bb2005-02-16 08:54:33 +00003395 switch (opc1) {
cerione9d361a2005-03-04 17:35:29 +00003396 case 0x22: // lbz (Load B & Zero, PPC32 p433)
ceriond953ebb2005-11-29 13:27:20 +00003397 DIP("lbz r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3398 val = loadBE(Ity_I8, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003399 putIReg( rD_addr, mkSzWiden8(ty, val, False) );
cerionb85e8bb2005-02-16 08:54:33 +00003400 break;
3401
cerion5b2325f2005-12-23 00:55:09 +00003402 case 0x23: // lbzu (Load B & Zero, Update, PPC32 p434)
cerion76de5cf2005-11-18 18:25:12 +00003403 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003404 vex_printf("dis_int_load(ppc)(lbzu,rA_addr|rD_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003405 return False;
3406 }
ceriond953ebb2005-11-29 13:27:20 +00003407 DIP("lbzu r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3408 val = loadBE(Ity_I8, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003409 putIReg( rD_addr, mkSzWiden8(ty, val, False) );
ceriond953ebb2005-11-29 13:27:20 +00003410 putIReg( rA_addr, mkexpr(EA) );
cerionb85e8bb2005-02-16 08:54:33 +00003411 break;
3412
cerion5b2325f2005-12-23 00:55:09 +00003413 case 0x2A: // lha (Load HW Alg, PPC32 p445)
ceriond953ebb2005-11-29 13:27:20 +00003414 DIP("lha r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3415 val = loadBE(Ity_I16, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003416 putIReg( rD_addr, mkSzWiden16(ty, val, True) );
cerionb85e8bb2005-02-16 08:54:33 +00003417 break;
cerion645c9302005-01-31 10:09:59 +00003418
cerion5b2325f2005-12-23 00:55:09 +00003419 case 0x2B: // lhau (Load HW Alg, Update, PPC32 p446)
cerion76de5cf2005-11-18 18:25:12 +00003420 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003421 vex_printf("dis_int_load(ppc)(lhau,rA_addr|rD_addr)\n");
cerioncb14e732005-09-09 16:38:19 +00003422 return False;
3423 }
ceriond953ebb2005-11-29 13:27:20 +00003424 DIP("lhau r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3425 val = loadBE(Ity_I16, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003426 putIReg( rD_addr, mkSzWiden16(ty, val, True) );
ceriond953ebb2005-11-29 13:27:20 +00003427 putIReg( rA_addr, mkexpr(EA) );
cerioncb14e732005-09-09 16:38:19 +00003428 break;
cerionb85e8bb2005-02-16 08:54:33 +00003429
cerione9d361a2005-03-04 17:35:29 +00003430 case 0x28: // lhz (Load HW & Zero, PPC32 p450)
ceriond953ebb2005-11-29 13:27:20 +00003431 DIP("lhz r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3432 val = loadBE(Ity_I16, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003433 putIReg( rD_addr, mkSzWiden16(ty, val, False) );
cerionb85e8bb2005-02-16 08:54:33 +00003434 break;
3435
cerion5b2325f2005-12-23 00:55:09 +00003436 case 0x29: // lhzu (Load HW & and Zero, Update, PPC32 p451)
cerion76de5cf2005-11-18 18:25:12 +00003437 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003438 vex_printf("dis_int_load(ppc)(lhzu,rA_addr|rD_addr)\n");
sewardj0e2cc672005-07-29 21:58:51 +00003439 return False;
3440 }
ceriond953ebb2005-11-29 13:27:20 +00003441 DIP("lhzu r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3442 val = loadBE(Ity_I16, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003443 putIReg( rD_addr, mkSzWiden16(ty, val, False) );
ceriond953ebb2005-11-29 13:27:20 +00003444 putIReg( rA_addr, mkexpr(EA) );
sewardj0e2cc672005-07-29 21:58:51 +00003445 break;
cerion645c9302005-01-31 10:09:59 +00003446
cerione9d361a2005-03-04 17:35:29 +00003447 case 0x20: // lwz (Load W & Zero, PPC32 p460)
ceriond953ebb2005-11-29 13:27:20 +00003448 DIP("lwz r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3449 val = loadBE(Ity_I32, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003450 putIReg( rD_addr, mkSzWiden32(ty, val, False) );
cerionb85e8bb2005-02-16 08:54:33 +00003451 break;
3452
cerion5b2325f2005-12-23 00:55:09 +00003453 case 0x21: // lwzu (Load W & Zero, Update, PPC32 p461))
cerion76de5cf2005-11-18 18:25:12 +00003454 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003455 vex_printf("dis_int_load(ppc)(lwzu,rA_addr|rD_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003456 return False;
3457 }
ceriond953ebb2005-11-29 13:27:20 +00003458 DIP("lwzu r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3459 val = loadBE(Ity_I32, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003460 putIReg( rD_addr, mkSzWiden32(ty, val, False) );
ceriond953ebb2005-11-29 13:27:20 +00003461 putIReg( rA_addr, mkexpr(EA) );
cerionb85e8bb2005-02-16 08:54:33 +00003462 break;
3463
3464 /* X Form */
3465 case 0x1F:
3466 if (b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00003467 vex_printf("dis_int_load(ppc)(Ox1F,b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003468 return False;
3469 }
cerion645c9302005-01-31 10:09:59 +00003470
cerionb85e8bb2005-02-16 08:54:33 +00003471 switch (opc2) {
cerion5b2325f2005-12-23 00:55:09 +00003472 case 0x077: // lbzux (Load B & Zero, Update Indexed, PPC32 p435)
ceriond953ebb2005-11-29 13:27:20 +00003473 DIP("lbzux r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
cerion76de5cf2005-11-18 18:25:12 +00003474 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003475 vex_printf("dis_int_load(ppc)(lwzux,rA_addr|rD_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003476 return False;
3477 }
ceriond953ebb2005-11-29 13:27:20 +00003478 val = loadBE(Ity_I8, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003479 putIReg( rD_addr, mkSzWiden8(ty, val, False) );
ceriond953ebb2005-11-29 13:27:20 +00003480 putIReg( rA_addr, mkexpr(EA) );
cerionb85e8bb2005-02-16 08:54:33 +00003481 break;
3482
cerion5b2325f2005-12-23 00:55:09 +00003483 case 0x057: // lbzx (Load B & Zero, Indexed, PPC32 p436)
ceriond953ebb2005-11-29 13:27:20 +00003484 DIP("lbzx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3485 val = loadBE(Ity_I8, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003486 putIReg( rD_addr, mkSzWiden8(ty, val, False) );
cerionb85e8bb2005-02-16 08:54:33 +00003487 break;
3488
cerion5b2325f2005-12-23 00:55:09 +00003489 case 0x177: // lhaux (Load HW Alg, Update Indexed, PPC32 p447)
cerion76de5cf2005-11-18 18:25:12 +00003490 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003491 vex_printf("dis_int_load(ppc)(lhaux,rA_addr|rD_addr)\n");
cerioncb14e732005-09-09 16:38:19 +00003492 return False;
3493 }
ceriond953ebb2005-11-29 13:27:20 +00003494 DIP("lhaux r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3495 val = loadBE(Ity_I16, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003496 putIReg( rD_addr, mkSzWiden16(ty, val, True) );
ceriond953ebb2005-11-29 13:27:20 +00003497 putIReg( rA_addr, mkexpr(EA) );
cerioncb14e732005-09-09 16:38:19 +00003498 break;
cerionb85e8bb2005-02-16 08:54:33 +00003499
cerion5b2325f2005-12-23 00:55:09 +00003500 case 0x157: // lhax (Load HW Alg, Indexed, PPC32 p448)
ceriond953ebb2005-11-29 13:27:20 +00003501 DIP("lhax r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3502 val = loadBE(Ity_I16, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003503 putIReg( rD_addr, mkSzWiden16(ty, val, True) );
cerionb85e8bb2005-02-16 08:54:33 +00003504 break;
3505
cerion5b2325f2005-12-23 00:55:09 +00003506 case 0x137: // lhzux (Load HW & Zero, Update Indexed, PPC32 p452)
cerion76de5cf2005-11-18 18:25:12 +00003507 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003508 vex_printf("dis_int_load(ppc)(lhzux,rA_addr|rD_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003509 return False;
3510 }
ceriond953ebb2005-11-29 13:27:20 +00003511 DIP("lhzux r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3512 val = loadBE(Ity_I16, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003513 putIReg( rD_addr, mkSzWiden16(ty, val, False) );
ceriond953ebb2005-11-29 13:27:20 +00003514 putIReg( rA_addr, mkexpr(EA) );
cerionb85e8bb2005-02-16 08:54:33 +00003515 break;
3516
cerion5b2325f2005-12-23 00:55:09 +00003517 case 0x117: // lhzx (Load HW & Zero, Indexed, PPC32 p453)
ceriond953ebb2005-11-29 13:27:20 +00003518 DIP("lhzx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3519 val = loadBE(Ity_I16, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003520 putIReg( rD_addr, mkSzWiden16(ty, val, False) );
cerionb85e8bb2005-02-16 08:54:33 +00003521 break;
cerion44997f22005-01-31 18:45:59 +00003522
cerion5b2325f2005-12-23 00:55:09 +00003523 case 0x037: // lwzux (Load W & Zero, Update Indexed, PPC32 p462)
cerion76de5cf2005-11-18 18:25:12 +00003524 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003525 vex_printf("dis_int_load(ppc)(lwzux,rA_addr|rD_addr)\n");
sewardj7787af42005-08-04 18:32:19 +00003526 return False;
3527 }
ceriond953ebb2005-11-29 13:27:20 +00003528 DIP("lwzux r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3529 val = loadBE(Ity_I32, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003530 putIReg( rD_addr, mkSzWiden32(ty, val, False) );
ceriond953ebb2005-11-29 13:27:20 +00003531 putIReg( rA_addr, mkexpr(EA) );
sewardj7787af42005-08-04 18:32:19 +00003532 break;
cerionb85e8bb2005-02-16 08:54:33 +00003533
cerion5b2325f2005-12-23 00:55:09 +00003534 case 0x017: // lwzx (Load W & Zero, Indexed, PPC32 p463)
ceriond953ebb2005-11-29 13:27:20 +00003535 DIP("lwzx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3536 val = loadBE(Ity_I32, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003537 putIReg( rD_addr, mkSzWiden32(ty, val, False) );
cerionb85e8bb2005-02-16 08:54:33 +00003538 break;
cerion44997f22005-01-31 18:45:59 +00003539
cerionf0de28c2005-12-13 20:21:11 +00003540
3541 /* 64bit Loads */
cerion5b2325f2005-12-23 00:55:09 +00003542 case 0x035: // ldux (Load DWord, Update Indexed, PPC64 p475)
cerionf0de28c2005-12-13 20:21:11 +00003543 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003544 vex_printf("dis_int_load(ppc)(ldux,rA_addr|rD_addr)\n");
cerionf0de28c2005-12-13 20:21:11 +00003545 return False;
3546 }
3547 DIP("ldux r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3548 putIReg( rD_addr, loadBE(Ity_I64, mkexpr(EA)) );
3549 putIReg( rA_addr, mkexpr(EA) );
3550 break;
3551
cerion5b2325f2005-12-23 00:55:09 +00003552 case 0x015: // ldx (Load DWord, Indexed, PPC64 p476)
cerionf0de28c2005-12-13 20:21:11 +00003553 DIP("ldx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3554 putIReg( rD_addr, loadBE(Ity_I64, mkexpr(EA)) );
3555 break;
3556
cerion5b2325f2005-12-23 00:55:09 +00003557 case 0x175: // lwaux (Load W Alg, Update Indexed, PPC64 p501)
cerionf0de28c2005-12-13 20:21:11 +00003558 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003559 vex_printf("dis_int_load(ppc)(lwaux,rA_addr|rD_addr)\n");
cerionf0de28c2005-12-13 20:21:11 +00003560 return False;
3561 }
3562 DIP("lwaux r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
cerion5b2325f2005-12-23 00:55:09 +00003563 putIReg( rD_addr,
3564 unop(Iop_32Sto64, loadBE(Ity_I32, mkexpr(EA))) );
cerionf0de28c2005-12-13 20:21:11 +00003565 putIReg( rA_addr, mkexpr(EA) );
3566 break;
3567
cerion5b2325f2005-12-23 00:55:09 +00003568 case 0x155: // lwax (Load W Alg, Indexed, PPC64 p502)
cerionf0de28c2005-12-13 20:21:11 +00003569 DIP("lwax r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
cerion5b2325f2005-12-23 00:55:09 +00003570 putIReg( rD_addr,
3571 unop(Iop_32Sto64, loadBE(Ity_I32, mkexpr(EA))) );
cerionf0de28c2005-12-13 20:21:11 +00003572 break;
3573
cerionb85e8bb2005-02-16 08:54:33 +00003574 default:
cerion5b2325f2005-12-23 00:55:09 +00003575 vex_printf("dis_int_load(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003576 return False;
3577 }
3578 break;
cerionf0de28c2005-12-13 20:21:11 +00003579
3580 /* DS Form - 64bit Loads */
3581 case 0x3A:
3582 switch (b1<<1 | b0) {
cerion5b2325f2005-12-23 00:55:09 +00003583 case 0x0: // ld (Load DWord, PPC64 p472)
cerionf0de28c2005-12-13 20:21:11 +00003584 DIP("ld r%u,%d(r%u)\n", rD_addr, simm16, rA_addr);
3585 putIReg( rD_addr, loadBE(Ity_I64, mkexpr(EA)) );
3586 break;
3587
cerion5b2325f2005-12-23 00:55:09 +00003588 case 0x1: // ldu (Load DWord, Update, PPC64 p474)
cerionf0de28c2005-12-13 20:21:11 +00003589 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003590 vex_printf("dis_int_load(ppc)(ldu,rA_addr|rD_addr)\n");
cerionf0de28c2005-12-13 20:21:11 +00003591 return False;
3592 }
3593 DIP("ldu r%u,%d(r%u)\n", rD_addr, simm16, rA_addr);
3594 simm16 = simm16 & ~0x3;
3595 putIReg( rD_addr, loadBE(Ity_I64, mkexpr(EA)) );
3596 putIReg( rA_addr, mkexpr(EA) );
3597 break;
3598
cerion5b2325f2005-12-23 00:55:09 +00003599 case 0x2: // lwa (Load Word Alg, PPC64 p499)
cerionf0de28c2005-12-13 20:21:11 +00003600 DIP("lwa r%u,%d(r%u)\n", rD_addr, simm16, rA_addr);
cerion5b2325f2005-12-23 00:55:09 +00003601 putIReg( rD_addr,
3602 unop(Iop_32Sto64, loadBE(Ity_I32, mkexpr(EA))) );
cerionf0de28c2005-12-13 20:21:11 +00003603 break;
3604
3605 default:
cerion5b2325f2005-12-23 00:55:09 +00003606 vex_printf("dis_int_load(ppc)(0x3A, opc2)\n");
cerionf0de28c2005-12-13 20:21:11 +00003607 return False;
3608 }
3609 break;
3610
cerionb85e8bb2005-02-16 08:54:33 +00003611 default:
cerion5b2325f2005-12-23 00:55:09 +00003612 vex_printf("dis_int_load(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003613 return False;
3614 }
3615 return True;
cerion7aa4bbc2005-01-29 09:32:07 +00003616}
3617
3618
3619
cerion3d870a32005-03-18 12:23:33 +00003620/*
3621 Integer Store Instructions
3622*/
ceriond23be4e2005-01-31 07:23:07 +00003623static Bool dis_int_store ( UInt theInstr )
3624{
cerionf0de28c2005-12-13 20:21:11 +00003625 /* D-Form, X-Form, DS-Form */
cerionedf7fc52005-11-18 20:57:41 +00003626 UChar opc1 = ifieldOPC(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00003627 UInt rS_addr = ifieldRegDS(theInstr);
3628 UInt rA_addr = ifieldRegA(theInstr);
ceriond953ebb2005-11-29 13:27:20 +00003629 UInt uimm16 = ifieldUIMM16(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00003630 UInt rB_addr = ifieldRegB(theInstr);
3631 UInt opc2 = ifieldOPClo10(theInstr);
cerionf0de28c2005-12-13 20:21:11 +00003632 UChar b1 = ifieldBIT1(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00003633 UChar b0 = ifieldBIT0(theInstr);
3634
ceriond953ebb2005-11-29 13:27:20 +00003635 Int simm16 = extend_s_16to32(uimm16);
3636 IRType ty = mode64 ? Ity_I64 : Ity_I32;
3637 IRTemp rS = newTemp(ty);
3638 IRTemp rB = newTemp(ty);
3639 IRTemp EA = newTemp(ty);
cerionb85e8bb2005-02-16 08:54:33 +00003640
cerion76de5cf2005-11-18 18:25:12 +00003641 assign( rB, getIReg(rB_addr) );
3642 assign( rS, getIReg(rS_addr) );
cerionb85e8bb2005-02-16 08:54:33 +00003643
cerionf0de28c2005-12-13 20:21:11 +00003644 switch (opc1) {
3645 case 0x1F: // register offset
ceriond953ebb2005-11-29 13:27:20 +00003646 assign( EA, ea_rAor0_idxd( rA_addr, rB_addr ) );
cerionf0de28c2005-12-13 20:21:11 +00003647 break;
3648 case 0x3E: // immediate offset: 64bit
3649 simm16 = simm16 & 0xFFFFFFFC;
3650 default: // immediate offset
3651 assign( EA, ea_rAor0_simm( rA_addr, simm16 ) );
3652 break;
ceriond953ebb2005-11-29 13:27:20 +00003653 }
3654
cerionb85e8bb2005-02-16 08:54:33 +00003655 switch (opc1) {
sewardjafe85832005-09-09 10:25:39 +00003656 case 0x26: // stb (Store B, PPC32 p509)
cerion76de5cf2005-11-18 18:25:12 +00003657 DIP("stb r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
cerion2831b002005-11-30 19:55:22 +00003658 storeBE( mkexpr(EA), mkSzNarrow8(ty, mkexpr(rS)) );
sewardjafe85832005-09-09 10:25:39 +00003659 break;
sewardjb51f0f42005-07-18 11:38:02 +00003660
cerion5b2325f2005-12-23 00:55:09 +00003661 case 0x27: // stbu (Store B, Update, PPC32 p510)
cerion76de5cf2005-11-18 18:25:12 +00003662 if (rA_addr == 0 ) {
cerion5b2325f2005-12-23 00:55:09 +00003663 vex_printf("dis_int_store(ppc)(stbu,rA_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003664 return False;
3665 }
cerion76de5cf2005-11-18 18:25:12 +00003666 DIP("stbu r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
ceriond953ebb2005-11-29 13:27:20 +00003667 putIReg( rA_addr, mkexpr(EA) );
cerion2831b002005-11-30 19:55:22 +00003668 storeBE( mkexpr(EA), mkSzNarrow8(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003669 break;
ceriond23be4e2005-01-31 07:23:07 +00003670
cerione9d361a2005-03-04 17:35:29 +00003671 case 0x2C: // sth (Store HW, PPC32 p522)
cerion76de5cf2005-11-18 18:25:12 +00003672 DIP("sth r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
cerion2831b002005-11-30 19:55:22 +00003673 storeBE( mkexpr(EA), mkSzNarrow16(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003674 break;
3675
cerion5b2325f2005-12-23 00:55:09 +00003676 case 0x2D: // sthu (Store HW, Update, PPC32 p524)
cerion76de5cf2005-11-18 18:25:12 +00003677 if (rA_addr == 0) {
cerion5b2325f2005-12-23 00:55:09 +00003678 vex_printf("dis_int_store(ppc)(sthu,rA_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003679 return False;
3680 }
cerion76de5cf2005-11-18 18:25:12 +00003681 DIP("sthu r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
ceriond953ebb2005-11-29 13:27:20 +00003682 putIReg( rA_addr, mkexpr(EA) );
cerion2831b002005-11-30 19:55:22 +00003683 storeBE( mkexpr(EA), mkSzNarrow16(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003684 break;
ceriond23be4e2005-01-31 07:23:07 +00003685
cerione9d361a2005-03-04 17:35:29 +00003686 case 0x24: // stw (Store W, PPC32 p530)
cerion76de5cf2005-11-18 18:25:12 +00003687 DIP("stw r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
cerion2831b002005-11-30 19:55:22 +00003688 storeBE( mkexpr(EA), mkSzNarrow32(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003689 break;
ceriond23be4e2005-01-31 07:23:07 +00003690
cerion5b2325f2005-12-23 00:55:09 +00003691 case 0x25: // stwu (Store W, Update, PPC32 p534)
cerion76de5cf2005-11-18 18:25:12 +00003692 if (rA_addr == 0) {
cerion5b2325f2005-12-23 00:55:09 +00003693 vex_printf("dis_int_store(ppc)(stwu,rA_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003694 return False;
3695 }
cerion76de5cf2005-11-18 18:25:12 +00003696 DIP("stwu r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
ceriond953ebb2005-11-29 13:27:20 +00003697 putIReg( rA_addr, mkexpr(EA) );
cerion2831b002005-11-30 19:55:22 +00003698 storeBE( mkexpr(EA), mkSzNarrow32(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003699 break;
3700
cerionf0de28c2005-12-13 20:21:11 +00003701 /* X Form : all these use EA_indexed */
cerionb85e8bb2005-02-16 08:54:33 +00003702 case 0x1F:
3703 if (b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00003704 vex_printf("dis_int_store(ppc)(0x1F,b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003705 return False;
3706 }
cerion44997f22005-01-31 18:45:59 +00003707
cerionb85e8bb2005-02-16 08:54:33 +00003708 switch (opc2) {
cerion5b2325f2005-12-23 00:55:09 +00003709 case 0x0F7: // stbux (Store B, Update Indexed, PPC32 p511)
cerion76de5cf2005-11-18 18:25:12 +00003710 if (rA_addr == 0) {
cerion5b2325f2005-12-23 00:55:09 +00003711 vex_printf("dis_int_store(ppc)(stbux,rA_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003712 return False;
3713 }
cerion76de5cf2005-11-18 18:25:12 +00003714 DIP("stbux r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
ceriond953ebb2005-11-29 13:27:20 +00003715 putIReg( rA_addr, mkexpr(EA) );
cerion2831b002005-11-30 19:55:22 +00003716 storeBE( mkexpr(EA), mkSzNarrow8(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003717 break;
3718
cerione9d361a2005-03-04 17:35:29 +00003719 case 0x0D7: // stbx (Store B Indexed, PPC32 p512)
cerion76de5cf2005-11-18 18:25:12 +00003720 DIP("stbx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003721 storeBE( mkexpr(EA), mkSzNarrow8(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003722 break;
3723
cerion5b2325f2005-12-23 00:55:09 +00003724 case 0x1B7: // sthux (Store HW, Update Indexed, PPC32 p525)
cerion76de5cf2005-11-18 18:25:12 +00003725 if (rA_addr == 0) {
cerion5b2325f2005-12-23 00:55:09 +00003726 vex_printf("dis_int_store(ppc)(sthux,rA_addr)\n");
cerioncb14e732005-09-09 16:38:19 +00003727 return False;
3728 }
cerion76de5cf2005-11-18 18:25:12 +00003729 DIP("sthux r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
ceriond953ebb2005-11-29 13:27:20 +00003730 putIReg( rA_addr, mkexpr(EA) );
cerion2831b002005-11-30 19:55:22 +00003731 storeBE( mkexpr(EA), mkSzNarrow16(ty, mkexpr(rS)) );
cerioncb14e732005-09-09 16:38:19 +00003732 break;
cerionb85e8bb2005-02-16 08:54:33 +00003733
cerione9d361a2005-03-04 17:35:29 +00003734 case 0x197: // sthx (Store HW Indexed, PPC32 p526)
cerion76de5cf2005-11-18 18:25:12 +00003735 DIP("sthx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003736 storeBE( mkexpr(EA), mkSzNarrow16(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003737 break;
3738
cerion5b2325f2005-12-23 00:55:09 +00003739 case 0x0B7: // stwux (Store W, Update Indexed, PPC32 p535)
cerion76de5cf2005-11-18 18:25:12 +00003740 if (rA_addr == 0) {
cerion5b2325f2005-12-23 00:55:09 +00003741 vex_printf("dis_int_store(ppc)(stwux,rA_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003742 return False;
3743 }
cerion76de5cf2005-11-18 18:25:12 +00003744 DIP("stwux r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
ceriond953ebb2005-11-29 13:27:20 +00003745 putIReg( rA_addr, mkexpr(EA) );
cerion2831b002005-11-30 19:55:22 +00003746 storeBE( mkexpr(EA), mkSzNarrow32(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003747 break;
cerion44997f22005-01-31 18:45:59 +00003748
cerione9d361a2005-03-04 17:35:29 +00003749 case 0x097: // stwx (Store W Indexed, PPC32 p536)
cerion76de5cf2005-11-18 18:25:12 +00003750 DIP("stwx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003751 storeBE( mkexpr(EA), mkSzNarrow32(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003752 break;
3753
cerionf0de28c2005-12-13 20:21:11 +00003754
3755 /* 64bit Stores */
cerion5b2325f2005-12-23 00:55:09 +00003756 case 0x0B5: // stdux (Store DWord, Update Indexed, PPC64 p584)
cerionf0de28c2005-12-13 20:21:11 +00003757 if (rA_addr == 0) {
cerion5b2325f2005-12-23 00:55:09 +00003758 vex_printf("dis_int_store(ppc)(stdux,rA_addr)\n");
cerionf0de28c2005-12-13 20:21:11 +00003759 return False;
3760 }
3761 DIP("stdux r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
3762 putIReg( rA_addr, mkexpr(EA) );
3763 storeBE( mkexpr(EA), mkexpr(rS) );
3764 break;
3765
cerion5b2325f2005-12-23 00:55:09 +00003766 case 0x095: // stdx (Store DWord Indexed, PPC64 p585)
cerionf0de28c2005-12-13 20:21:11 +00003767 DIP("stdx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
3768 storeBE( mkexpr(EA), mkexpr(rS) );
3769 break;
3770
cerionb85e8bb2005-02-16 08:54:33 +00003771 default:
cerion5b2325f2005-12-23 00:55:09 +00003772 vex_printf("dis_int_store(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003773 return False;
3774 }
3775 break;
cerionf0de28c2005-12-13 20:21:11 +00003776
3777 /* DS Form - 64bit Stores */
3778 case 0x3E:
3779 switch (b1<<1 | b0) {
cerion5b2325f2005-12-23 00:55:09 +00003780 case 0x0: // std (Store DWord, PPC64 p580)
cerionf0de28c2005-12-13 20:21:11 +00003781 DIP("std r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
3782 storeBE( mkexpr(EA), mkexpr(rS) );
3783 break;
3784
cerion5b2325f2005-12-23 00:55:09 +00003785 case 0x1: // stdu (Store DWord, Update, PPC64 p583)
cerionf0de28c2005-12-13 20:21:11 +00003786 DIP("stdu r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
3787 putIReg( rA_addr, mkexpr(EA) );
3788 storeBE( mkexpr(EA), mkexpr(rS) );
3789 break;
3790
3791 default:
cerion5b2325f2005-12-23 00:55:09 +00003792 vex_printf("dis_int_load(ppc)(0x3A, opc2)\n");
cerionf0de28c2005-12-13 20:21:11 +00003793 return False;
3794 }
3795 break;
3796
cerionb85e8bb2005-02-16 08:54:33 +00003797 default:
cerion5b2325f2005-12-23 00:55:09 +00003798 vex_printf("dis_int_store(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003799 return False;
3800 }
3801 return True;
ceriond23be4e2005-01-31 07:23:07 +00003802}
3803
3804
3805
sewardj7787af42005-08-04 18:32:19 +00003806/*
3807 Integer Load/Store Multiple Instructions
3808*/
3809static Bool dis_int_ldst_mult ( UInt theInstr )
3810{
3811 /* D-Form */
cerion76de5cf2005-11-18 18:25:12 +00003812 UChar opc1 = ifieldOPC(theInstr);
3813 UChar rD_addr = ifieldRegDS(theInstr);
3814 UChar rS_addr = rD_addr;
3815 UChar rA_addr = ifieldRegA(theInstr);
cerion2831b002005-11-30 19:55:22 +00003816 UInt uimm16 = ifieldUIMM16(theInstr);
cerionedf7fc52005-11-18 20:57:41 +00003817
ceriond953ebb2005-11-29 13:27:20 +00003818 Int simm16 = extend_s_16to32(uimm16);
3819 IRType ty = mode64 ? Ity_I64 : Ity_I32;
3820 IRTemp EA = newTemp(ty);
3821 UInt r = 0;
3822 UInt ea_off = 0;
sewardj7787af42005-08-04 18:32:19 +00003823 IRExpr* irx_addr;
cerionedf7fc52005-11-18 20:57:41 +00003824
ceriond953ebb2005-11-29 13:27:20 +00003825 assign( EA, ea_rAor0_simm( rA_addr, simm16 ) );
3826
sewardj7787af42005-08-04 18:32:19 +00003827 switch (opc1) {
ceriond953ebb2005-11-29 13:27:20 +00003828 case 0x2E: // lmw (Load Multiple Word, PPC32 p454)
3829 if (rA_addr >= rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003830 vex_printf("dis_int_ldst_mult(ppc)(lmw,rA_addr)\n");
sewardj7787af42005-08-04 18:32:19 +00003831 return False;
ceriond953ebb2005-11-29 13:27:20 +00003832 }
3833 DIP("lmw r%u,%d(r%u)\n", rD_addr, simm16, rA_addr);
3834 for (r = rD_addr; r <= 31; r++) {
3835 irx_addr = binop(Iop_Add32, mkexpr(EA), mkU32(ea_off));
cerion5b2325f2005-12-23 00:55:09 +00003836 putIReg( r, mkSzWiden32(ty, loadBE(Ity_I32, irx_addr ),
3837 False) );
ceriond953ebb2005-11-29 13:27:20 +00003838 ea_off += 4;
3839 }
3840 break;
3841
3842 case 0x2F: // stmw (Store Multiple Word, PPC32 p527)
3843 DIP("stmw r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
3844 for (r = rS_addr; r <= 31; r++) {
3845 irx_addr = binop(Iop_Add32, mkexpr(EA), mkU32(ea_off));
cerion2831b002005-11-30 19:55:22 +00003846 storeBE( irx_addr, mkSzNarrow32(ty, getIReg(r)) );
ceriond953ebb2005-11-29 13:27:20 +00003847 ea_off += 4;
3848 }
3849 break;
3850
3851 default:
cerion5b2325f2005-12-23 00:55:09 +00003852 vex_printf("dis_int_ldst_mult(ppc)(opc1)\n");
ceriond953ebb2005-11-29 13:27:20 +00003853 return False;
sewardj7787af42005-08-04 18:32:19 +00003854 }
3855 return True;
3856}
3857
3858
3859
sewardj87e651f2005-09-09 08:31:18 +00003860/*
3861 Integer Load/Store String Instructions
3862*/
3863static
3864void generate_lsw_sequence ( IRTemp tNBytes, // # bytes, :: Ity_I32
3865 IRTemp EA, // EA
3866 Int rD, // first dst register
ceriond953ebb2005-11-29 13:27:20 +00003867 Int maxBytes ) // 32 or 128
sewardj87e651f2005-09-09 08:31:18 +00003868{
3869 Int i, shift = 24;
3870 IRExpr* e_nbytes = mkexpr(tNBytes);
ceriond953ebb2005-11-29 13:27:20 +00003871 IRExpr* e_EA = mkexpr(EA);
3872 IRType ty = mode64 ? Ity_I64 : Ity_I32;
sewardj87e651f2005-09-09 08:31:18 +00003873
sewardj5876fa12005-09-09 09:35:29 +00003874 vassert(rD >= 0 && rD < 32);
sewardj87e651f2005-09-09 08:31:18 +00003875 rD--; if (rD < 0) rD = 31;
3876
3877 for (i = 0; i < maxBytes; i++) {
sewardj87e651f2005-09-09 08:31:18 +00003878 /* if (nBytes < (i+1)) goto NIA; */
3879 stmt( IRStmt_Exit( binop(Iop_CmpLT32U, e_nbytes, mkU32(i+1)),
3880 Ijk_Boring,
cerion2831b002005-11-30 19:55:22 +00003881 mkSzConst( ty, nextInsnAddr()) ));
sewardj87e651f2005-09-09 08:31:18 +00003882 /* when crossing into a new dest register, set it to zero. */
3883 if ((i % 4) == 0) {
3884 rD++; if (rD == 32) rD = 0;
cerion2831b002005-11-30 19:55:22 +00003885 putIReg(rD, mkSzImm(ty, 0));
sewardj87e651f2005-09-09 08:31:18 +00003886 shift = 24;
3887 }
3888 /* rD |= (8Uto32(*(EA+i))) << shift */
3889 vassert(shift == 0 || shift == 8 || shift == 16 || shift == 24);
cerion2831b002005-11-30 19:55:22 +00003890 putIReg( rD,
3891 mkSzWiden32(ty,
3892 binop(Iop_Or32,
3893 mkSzNarrow32(ty, getIReg(rD)),
3894 binop(Iop_Shl32,
3895 unop(Iop_8Uto32,
3896 loadBE(Ity_I8,
3897 binop(Iop_Add32, e_EA, mkU32(i)))),
3898 mkU8(toUChar(shift)))),
3899 /*Signed*/False) );
sewardj87e651f2005-09-09 08:31:18 +00003900 shift -= 8;
3901 }
3902}
3903
sewardj5876fa12005-09-09 09:35:29 +00003904static
3905void generate_stsw_sequence ( IRTemp tNBytes, // # bytes, :: Ity_I32
3906 IRTemp EA, // EA
3907 Int rS, // first src register
ceriond953ebb2005-11-29 13:27:20 +00003908 Int maxBytes ) // 32 or 128
sewardj5876fa12005-09-09 09:35:29 +00003909{
3910 Int i, shift = 24;
3911 IRExpr* e_nbytes = mkexpr(tNBytes);
ceriond953ebb2005-11-29 13:27:20 +00003912 IRExpr* e_EA = mkexpr(EA);
3913 IRType ty = mode64 ? Ity_I64 : Ity_I32;
sewardj5876fa12005-09-09 09:35:29 +00003914
3915 vassert(rS >= 0 && rS < 32);
3916 rS--; if (rS < 0) rS = 31;
3917
3918 for (i = 0; i < maxBytes; i++) {
3919 /* if (nBytes < (i+1)) goto NIA; */
3920 stmt( IRStmt_Exit( binop(Iop_CmpLT32U, e_nbytes, mkU32(i+1)),
3921 Ijk_Boring,
cerion2831b002005-11-30 19:55:22 +00003922 mkSzConst( ty, nextInsnAddr() ) ));
sewardj5876fa12005-09-09 09:35:29 +00003923 /* check for crossing into a new src register. */
3924 if ((i % 4) == 0) {
3925 rS++; if (rS == 32) rS = 0;
3926 shift = 24;
3927 }
3928 /* *(EA+i) = 32to8(rS >> shift) */
3929 vassert(shift == 0 || shift == 8 || shift == 16 || shift == 24);
3930 storeBE(
cerion2831b002005-11-30 19:55:22 +00003931 binop(mkSzOp(ty,Iop_Add8), e_EA, mkSzImm(ty,i)),
sewardj5876fa12005-09-09 09:35:29 +00003932 unop(Iop_32to8,
cerion2831b002005-11-30 19:55:22 +00003933 binop(Iop_Shr32,
3934 mkSzNarrow32(ty, getIReg(rS)),
3935 mkU8(toUChar(shift))))
sewardj5876fa12005-09-09 09:35:29 +00003936 );
3937 shift -= 8;
3938 }
3939}
3940
sewardj87e651f2005-09-09 08:31:18 +00003941static Bool dis_int_ldst_str ( UInt theInstr, /*OUT*/Bool* stopHere )
3942{
3943 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00003944 UChar opc1 = ifieldOPC(theInstr);
3945 UChar rD_addr = ifieldRegDS(theInstr);
3946 UChar rS_addr = rD_addr;
3947 UChar rA_addr = ifieldRegA(theInstr);
3948 UChar rB_addr = ifieldRegB(theInstr);
3949 UChar NumBytes = rB_addr;
3950 UInt opc2 = ifieldOPClo10(theInstr);
3951 UChar b0 = ifieldBIT0(theInstr);
cerionedf7fc52005-11-18 20:57:41 +00003952
ceriond953ebb2005-11-29 13:27:20 +00003953 IRType ty = mode64 ? Ity_I64 : Ity_I32;
3954 IRTemp t_EA = newTemp(ty);
sewardj87e651f2005-09-09 08:31:18 +00003955 IRTemp t_nbytes = IRTemp_INVALID;
cerionedf7fc52005-11-18 20:57:41 +00003956
sewardj87e651f2005-09-09 08:31:18 +00003957 *stopHere = False;
cerionedf7fc52005-11-18 20:57:41 +00003958
sewardj87e651f2005-09-09 08:31:18 +00003959 if (opc1 != 0x1F || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00003960 vex_printf("dis_int_ldst_str(ppc)(opc1)\n");
sewardj87e651f2005-09-09 08:31:18 +00003961 return False;
3962 }
3963
3964 switch (opc2) {
3965 case 0x255: // lswi (Load String Word Immediate, PPC32 p455)
3966 /* NB: does not reject the case where RA is in the range of
3967 registers to be loaded. It should. */
ceriond953ebb2005-11-29 13:27:20 +00003968 DIP("lswi r%u,r%u,%d\n", rD_addr, rA_addr, NumBytes);
3969 assign( t_EA, ea_rAor0(rA_addr) );
3970 if (!mode64 && NumBytes == 8) {
sewardj87e651f2005-09-09 08:31:18 +00003971 /* Special case hack */
cerion76de5cf2005-11-18 18:25:12 +00003972 /* rD = Mem[EA]; (rD+1)%32 = Mem[EA+4] */
3973 putIReg( rD_addr,
sewardj87e651f2005-09-09 08:31:18 +00003974 loadBE(Ity_I32, mkexpr(t_EA)) );
cerion76de5cf2005-11-18 18:25:12 +00003975 putIReg( (rD_addr+1) % 32,
ceriond953ebb2005-11-29 13:27:20 +00003976 loadBE(Ity_I32,
3977 binop(Iop_Add32, mkexpr(t_EA), mkU32(4))) );
sewardj87e651f2005-09-09 08:31:18 +00003978 } else {
3979 t_nbytes = newTemp(Ity_I32);
cerion76de5cf2005-11-18 18:25:12 +00003980 assign( t_nbytes, mkU32(NumBytes==0 ? 32 : NumBytes) );
ceriond953ebb2005-11-29 13:27:20 +00003981 generate_lsw_sequence( t_nbytes, t_EA, rD_addr, 32 );
sewardj87e651f2005-09-09 08:31:18 +00003982 *stopHere = True;
3983 }
3984 return True;
3985
3986 case 0x215: // lswx (Load String Word Indexed, PPC32 p456)
3987 /* NB: does not reject the case where RA is in the range of
3988 registers to be loaded. It should. Although considering
3989 that that can only be detected at run time, it's not easy to
3990 do so. */
cerion76de5cf2005-11-18 18:25:12 +00003991 if (rD_addr == rA_addr || rD_addr == rB_addr)
sewardj87e651f2005-09-09 08:31:18 +00003992 return False;
cerion76de5cf2005-11-18 18:25:12 +00003993 if (rD_addr == 0 && rA_addr == 0)
sewardj87e651f2005-09-09 08:31:18 +00003994 return False;
ceriond953ebb2005-11-29 13:27:20 +00003995 DIP("lswx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
sewardj87e651f2005-09-09 08:31:18 +00003996 t_nbytes = newTemp(Ity_I32);
ceriond953ebb2005-11-29 13:27:20 +00003997 assign( t_EA, ea_rAor0_idxd(rA_addr,rB_addr) );
cerionedf7fc52005-11-18 20:57:41 +00003998 assign( t_nbytes, unop( Iop_8Uto32, getXER_BC() ) );
ceriond953ebb2005-11-29 13:27:20 +00003999 generate_lsw_sequence( t_nbytes, t_EA, rD_addr, 128 );
sewardj87e651f2005-09-09 08:31:18 +00004000 *stopHere = True;
4001 return True;
4002
sewardj5876fa12005-09-09 09:35:29 +00004003 case 0x2D5: // stswi (Store String Word Immediate, PPC32 p528)
ceriond953ebb2005-11-29 13:27:20 +00004004 DIP("stswi r%u,r%u,%d\n", rS_addr, rA_addr, NumBytes);
4005 assign( t_EA, ea_rAor0(rA_addr) );
sewardj5876fa12005-09-09 09:35:29 +00004006 if (NumBytes == 8) {
4007 /* Special case hack */
cerion76de5cf2005-11-18 18:25:12 +00004008 /* Mem[EA] = rD; Mem[EA+4] = (rD+1)%32 */
4009 storeBE( mkexpr(t_EA),
4010 getIReg(rD_addr) );
4011 storeBE( binop(Iop_Add32, mkexpr(t_EA), mkU32(4)),
4012 getIReg((rD_addr+1) % 32) );
sewardj5876fa12005-09-09 09:35:29 +00004013 } else {
4014 t_nbytes = newTemp(Ity_I32);
cerion76de5cf2005-11-18 18:25:12 +00004015 assign( t_nbytes, mkU32(NumBytes==0 ? 32 : NumBytes) );
ceriond953ebb2005-11-29 13:27:20 +00004016 generate_stsw_sequence( t_nbytes, t_EA, rD_addr, 32 );
sewardj5876fa12005-09-09 09:35:29 +00004017 *stopHere = True;
4018 }
4019 return True;
4020
sewardj5876fa12005-09-09 09:35:29 +00004021 case 0x295: // stswx (Store String Word Indexed, PPC32 p529)
ceriond953ebb2005-11-29 13:27:20 +00004022 DIP("stswx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
sewardj5876fa12005-09-09 09:35:29 +00004023 t_nbytes = newTemp(Ity_I32);
ceriond953ebb2005-11-29 13:27:20 +00004024 assign( t_EA, ea_rAor0_idxd(rA_addr,rB_addr) );
cerionedf7fc52005-11-18 20:57:41 +00004025 assign( t_nbytes, unop( Iop_8Uto32, getXER_BC() ) );
ceriond953ebb2005-11-29 13:27:20 +00004026 generate_stsw_sequence( t_nbytes, t_EA, rS_addr, 128 );
sewardj5876fa12005-09-09 09:35:29 +00004027 *stopHere = True;
4028 return True;
sewardj87e651f2005-09-09 08:31:18 +00004029
4030 default:
cerion5b2325f2005-12-23 00:55:09 +00004031 vex_printf("dis_int_ldst_str(ppc)(opc2)\n");
sewardj87e651f2005-09-09 08:31:18 +00004032 return False;
4033 }
4034 return True;
4035}
4036
cerion094d1392005-06-20 13:45:57 +00004037
sewardjb51f0f42005-07-18 11:38:02 +00004038/* ------------------------------------------------------------------
4039 Integer Branch Instructions
4040 ------------------------------------------------------------------ */
cerion645c9302005-01-31 10:09:59 +00004041
cerion45552a92005-02-03 18:20:22 +00004042/*
4043 Branch helper function
4044 ok = BO[2] | ((CTR[0] != 0) ^ BO[1])
sewardjb51f0f42005-07-18 11:38:02 +00004045 Returns an I32 which is 0x00000000 if the ctr condition failed
4046 and 0xFFFFFFFF otherwise.
cerion45552a92005-02-03 18:20:22 +00004047*/
sewardjb51f0f42005-07-18 11:38:02 +00004048static IRExpr* /* :: Ity_I32 */ branch_ctr_ok( UInt BO )
cerion45552a92005-02-03 18:20:22 +00004049{
ceriond953ebb2005-11-29 13:27:20 +00004050 IRType ty = mode64 ? Ity_I64 : Ity_I32;
sewardjb51f0f42005-07-18 11:38:02 +00004051 IRTemp ok = newTemp(Ity_I32);
cerionf0de28c2005-12-13 20:21:11 +00004052 IRExpr* ctr_lo32;
cerioned623db2005-06-20 12:42:04 +00004053
cerionf0de28c2005-12-13 20:21:11 +00004054 if ((BO >> 2) & 1) { // independent of ctr
sewardjb51f0f42005-07-18 11:38:02 +00004055 assign( ok, mkU32(0xFFFFFFFF) );
cerionb85e8bb2005-02-16 08:54:33 +00004056 } else {
cerionf0de28c2005-12-13 20:21:11 +00004057 ctr_lo32 = mkSzNarrow32(ty, getGST( PPC_GST_CTR ));
4058 if ((BO >> 1) & 1) { // ctr == 0 ?
ceriond953ebb2005-11-29 13:27:20 +00004059 assign( ok, unop( Iop_1Sto32,
cerionf0de28c2005-12-13 20:21:11 +00004060 binop( Iop_CmpEQ32, ctr_lo32, mkU32(0))) );
4061 } else { // ctr != 0 ?
sewardjb51f0f42005-07-18 11:38:02 +00004062 assign( ok, unop( Iop_1Sto32,
cerionf0de28c2005-12-13 20:21:11 +00004063 binop( Iop_CmpNE32, ctr_lo32, mkU32(0))) );
cerionb85e8bb2005-02-16 08:54:33 +00004064 }
4065 }
4066 return mkexpr(ok);
cerion45552a92005-02-03 18:20:22 +00004067}
4068
sewardjb51f0f42005-07-18 11:38:02 +00004069
cerion45552a92005-02-03 18:20:22 +00004070/*
sewardjb51f0f42005-07-18 11:38:02 +00004071 Branch helper function cond_ok = BO[4] | (CR[BI] == BO[3])
4072 Returns an I32 which is either 0 if the condition failed or
4073 some arbitrary nonzero value otherwise. */
4074
4075static IRExpr* /* :: Ity_I32 */ branch_cond_ok( UInt BO, UInt BI )
cerion45552a92005-02-03 18:20:22 +00004076{
sewardjb51f0f42005-07-18 11:38:02 +00004077 Int where;
4078 IRTemp res = newTemp(Ity_I32);
cerionb85e8bb2005-02-16 08:54:33 +00004079 IRTemp cr_bi = newTemp(Ity_I32);
4080
sewardjb51f0f42005-07-18 11:38:02 +00004081 if ((BO >> 4) & 1) {
4082 assign( res, mkU32(1) );
cerionb85e8bb2005-02-16 08:54:33 +00004083 } else {
sewardjb51f0f42005-07-18 11:38:02 +00004084 // ok = (CR[BI] == BO[3]) Note, the following relies on
4085 // getCRbit_anywhere returning a value which
4086 // is either zero or has exactly 1 bit set.
4087 assign( cr_bi, getCRbit_anywhere( BI, &where ) );
cerione9d361a2005-03-04 17:35:29 +00004088
4089 if ((BO >> 3) & 1) {
sewardjb51f0f42005-07-18 11:38:02 +00004090 /* We can use cr_bi as-is. */
4091 assign( res, mkexpr(cr_bi) );
cerione9d361a2005-03-04 17:35:29 +00004092 } else {
sewardjb51f0f42005-07-18 11:38:02 +00004093 /* We have to invert the sense of the information held in
4094 cr_bi. For that we need to know which bit
cerion76de5cf2005-11-18 18:25:12 +00004095 getCRbit_anywhere regards as significant. */
cerion5b2325f2005-12-23 00:55:09 +00004096 assign( res, binop(Iop_Xor32, mkexpr(cr_bi),
4097 mkU32(1<<where)) );
cerionb85e8bb2005-02-16 08:54:33 +00004098 }
4099 }
sewardjb51f0f42005-07-18 11:38:02 +00004100 return mkexpr(res);
cerion45552a92005-02-03 18:20:22 +00004101}
4102
4103
cerion3d870a32005-03-18 12:23:33 +00004104/*
4105 Integer Branch Instructions
4106*/
sewardj9d540e52005-10-08 11:28:16 +00004107static Bool dis_branch ( UInt theInstr,
4108 /*OUT*/DisResult* dres,
4109 Bool (*resteerOkFn)(Addr64) )
cerion91ad5362005-01-27 23:02:41 +00004110{
ceriond953ebb2005-11-29 13:27:20 +00004111 UChar opc1 = ifieldOPC(theInstr);
4112 UChar BO = ifieldRegDS(theInstr);
4113 UChar BI = ifieldRegA(theInstr);
4114 UInt BD_u16 = ifieldUIMM16(theInstr) & 0xFFFFFFFC; /* mask off */
4115 UChar b11to15 = ifieldRegB(theInstr);
4116 UInt opc2 = ifieldOPClo10(theInstr);
4117 UInt LI_u26 = ifieldUIMM26(theInstr) & 0xFFFFFFFC; /* mask off */
4118 UChar flag_AA = ifieldBIT1(theInstr);
4119 UChar flag_LK = ifieldBIT0(theInstr);
4120
cerion2831b002005-11-30 19:55:22 +00004121 IRType ty = mode64 ? Ity_I64 : Ity_I32;
ceriond953ebb2005-11-29 13:27:20 +00004122 Addr64 tgt = 0;
4123 Int BD = extend_s_16to32(BD_u16);
cerion2831b002005-11-30 19:55:22 +00004124 IRTemp do_branch = newTemp(Ity_I32);
4125 IRTemp ctr_ok = newTemp(Ity_I32);
4126 IRTemp cond_ok = newTemp(Ity_I32);
4127 IRExpr* e_nia = mkSzImm(ty, nextInsnAddr());
4128 IRConst* c_nia = mkSzConst(ty, nextInsnAddr());
sewardjdf07b882005-11-29 18:19:11 +00004129 IRTemp lr_old = newTemp(ty);
ceriond953ebb2005-11-29 13:27:20 +00004130
cerionb85e8bb2005-02-16 08:54:33 +00004131 /* Hack to pass through code that just wants to read the PC */
4132 if (theInstr == 0x429F0005) {
sewardjb51f0f42005-07-18 11:38:02 +00004133 DIP("bcl 0x%x, 0x%x (a.k.a mr lr,cia+4)\n", BO, BI);
ceriond953ebb2005-11-29 13:27:20 +00004134 putGST( PPC_GST_LR, e_nia );
cerionb85e8bb2005-02-16 08:54:33 +00004135 return True;
sewardjb51f0f42005-07-18 11:38:02 +00004136 }
sewardj9d540e52005-10-08 11:28:16 +00004137
4138 /* The default what-next. Individual cases can override it. */
4139 dres->whatNext = Dis_StopHere;
4140
cerionb85e8bb2005-02-16 08:54:33 +00004141 switch (opc1) {
cerione9d361a2005-03-04 17:35:29 +00004142 case 0x12: // b (Branch, PPC32 p360)
cerion4561acb2005-02-21 14:07:48 +00004143 if (flag_AA) {
cerion2831b002005-11-30 19:55:22 +00004144 tgt = mkSzAddr( ty, extend_s_26to64(LI_u26) );
cerion4561acb2005-02-21 14:07:48 +00004145 } else {
cerion2831b002005-11-30 19:55:22 +00004146 tgt = mkSzAddr( ty, guest_CIA_curr_instr +
4147 (Long)extend_s_26to64(LI_u26) );
cerionb85e8bb2005-02-16 08:54:33 +00004148 }
ceriond953ebb2005-11-29 13:27:20 +00004149 if (mode64) {
4150 DIP("b%s%s 0x%llx\n",
4151 flag_LK ? "l" : "", flag_AA ? "a" : "", tgt);
4152 } else {
4153 DIP("b%s%s 0x%x\n",
4154 flag_LK ? "l" : "", flag_AA ? "a" : "", (Addr32)tgt);
sewardj9d540e52005-10-08 11:28:16 +00004155 }
4156
ceriond953ebb2005-11-29 13:27:20 +00004157 if (flag_LK)
4158 putGST( PPC_GST_LR, e_nia );
4159
4160 if (resteerOkFn(tgt)) {
4161 dres->whatNext = Dis_Resteer;
4162 dres->continueAt = tgt;
sewardj9d540e52005-10-08 11:28:16 +00004163 } else {
4164 irbb->jumpkind = flag_LK ? Ijk_Call : Ijk_Boring;
cerion2831b002005-11-30 19:55:22 +00004165 irbb->next = mkSzImm(ty, tgt);
sewardj9d540e52005-10-08 11:28:16 +00004166 }
cerionb85e8bb2005-02-16 08:54:33 +00004167 break;
4168
cerione9d361a2005-03-04 17:35:29 +00004169 case 0x10: // bc (Branch Conditional, PPC32 p361)
cerionb85e8bb2005-02-16 08:54:33 +00004170 DIP("bc%s%s 0x%x, 0x%x, 0x%x\n",
ceriond953ebb2005-11-29 13:27:20 +00004171 flag_LK ? "l" : "", flag_AA ? "a" : "", BO, BI, BD);
cerionb85e8bb2005-02-16 08:54:33 +00004172
4173 if (!(BO & 0x4)) {
ceriond953ebb2005-11-29 13:27:20 +00004174 putGST( PPC_GST_CTR,
cerion2831b002005-11-30 19:55:22 +00004175 binop(mkSzOp(ty, Iop_Sub8),
4176 getGST( PPC_GST_CTR ), mkSzImm(ty, 1)) );
cerionb85e8bb2005-02-16 08:54:33 +00004177 }
sewardjb51f0f42005-07-18 11:38:02 +00004178
4179 /* This is a bit subtle. ctr_ok is either all 0s or all 1s.
cerion76de5cf2005-11-18 18:25:12 +00004180 cond_ok is either zero or nonzero, since that's the cheapest
4181 way to compute it. Anding them together gives a value which
4182 is either zero or non zero and so that's what we must test
4183 for in the IRStmt_Exit. */
sewardjb51f0f42005-07-18 11:38:02 +00004184 assign( ctr_ok, branch_ctr_ok( BO ) );
cerionb85e8bb2005-02-16 08:54:33 +00004185 assign( cond_ok, branch_cond_ok( BO, BI ) );
sewardjb51f0f42005-07-18 11:38:02 +00004186 assign( do_branch,
4187 binop(Iop_And32, mkexpr(cond_ok), mkexpr(ctr_ok)) );
4188
cerion4561acb2005-02-21 14:07:48 +00004189 if (flag_AA) {
cerion2831b002005-11-30 19:55:22 +00004190 tgt = mkSzAddr(ty, extend_s_16to64(BD_u16));
cerion4561acb2005-02-21 14:07:48 +00004191 } else {
cerion2831b002005-11-30 19:55:22 +00004192 tgt = mkSzAddr(ty, guest_CIA_curr_instr +
4193 (Long)extend_s_16to64(BD_u16));
cerionb85e8bb2005-02-16 08:54:33 +00004194 }
ceriond953ebb2005-11-29 13:27:20 +00004195 if (flag_LK)
4196 putGST( PPC_GST_LR, e_nia );
cerionb85e8bb2005-02-16 08:54:33 +00004197
ceriond953ebb2005-11-29 13:27:20 +00004198 stmt( IRStmt_Exit(
4199 binop(Iop_CmpNE32, mkexpr(do_branch), mkU32(0)),
4200 flag_LK ? Ijk_Call : Ijk_Boring,
cerion2831b002005-11-30 19:55:22 +00004201 mkSzConst(ty, tgt) ) );
cerionb85e8bb2005-02-16 08:54:33 +00004202
4203 irbb->jumpkind = Ijk_Boring;
ceriond953ebb2005-11-29 13:27:20 +00004204 irbb->next = e_nia;
cerionb85e8bb2005-02-16 08:54:33 +00004205 break;
4206
4207 case 0x13:
4208 if (b11to15!=0) {
cerion5b2325f2005-12-23 00:55:09 +00004209 vex_printf("dis_int_branch(ppc)(0x13,b11to15)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004210 return False;
4211 }
cerion91ad5362005-01-27 23:02:41 +00004212
cerionb85e8bb2005-02-16 08:54:33 +00004213 switch (opc2) {
cerione9d361a2005-03-04 17:35:29 +00004214 case 0x210: // bcctr (Branch Cond. to Count Register, PPC32 p363)
cerion5b2325f2005-12-23 00:55:09 +00004215 if ((BO & 0x4) == 0) { // "decr and test CTR" option invalid
4216 vex_printf("dis_int_branch(ppc)(bcctr,BO)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004217 return False;
4218 }
ceriona31e8b52005-02-21 16:30:45 +00004219 DIP("bcctr%s 0x%x, 0x%x\n", flag_LK ? "l" : "", BO, BI);
cerionb85e8bb2005-02-16 08:54:33 +00004220
4221 assign( cond_ok, branch_cond_ok( BO, BI ) );
ceriond953ebb2005-11-29 13:27:20 +00004222
cerion2831b002005-11-30 19:55:22 +00004223 assign( lr_old, addr_align( getGST( PPC_GST_CTR ), 4 ));
sewardjdf07b882005-11-29 18:19:11 +00004224
ceriond953ebb2005-11-29 13:27:20 +00004225 if (flag_LK)
4226 putGST( PPC_GST_LR, e_nia );
cerionb85e8bb2005-02-16 08:54:33 +00004227
sewardjb51f0f42005-07-18 11:38:02 +00004228 stmt( IRStmt_Exit(
4229 binop(Iop_CmpEQ32, mkexpr(cond_ok), mkU32(0)),
4230 Ijk_Boring,
ceriond953ebb2005-11-29 13:27:20 +00004231 c_nia ));
cerionb85e8bb2005-02-16 08:54:33 +00004232
4233 irbb->jumpkind = flag_LK ? Ijk_Call : Ijk_Boring;
sewardjdf07b882005-11-29 18:19:11 +00004234 irbb->next = mkexpr(lr_old);
cerionb85e8bb2005-02-16 08:54:33 +00004235 break;
4236
cerione9d361a2005-03-04 17:35:29 +00004237 case 0x010: // bclr (Branch Cond. to Link Register, PPC32 p365)
sewardjb51f0f42005-07-18 11:38:02 +00004238
4239 if ((BO & 0x14 /* 1z1zz */) == 0x14 && flag_LK == 0) {
cerion225a0342005-09-12 20:49:09 +00004240 DIP("blr\n");
sewardjb51f0f42005-07-18 11:38:02 +00004241 } else {
4242 DIP("bclr%s 0x%x, 0x%x\n", flag_LK ? "l" : "", BO, BI);
4243 }
cerion91ad5362005-01-27 23:02:41 +00004244
cerionb85e8bb2005-02-16 08:54:33 +00004245 if (!(BO & 0x4)) {
ceriond953ebb2005-11-29 13:27:20 +00004246 putGST( PPC_GST_CTR,
cerion2831b002005-11-30 19:55:22 +00004247 binop(mkSzOp(ty, Iop_Sub8),
4248 getGST( PPC_GST_CTR ), mkSzImm(ty, 1)) );
cerionb85e8bb2005-02-16 08:54:33 +00004249 }
4250
sewardjb51f0f42005-07-18 11:38:02 +00004251 /* See comments above for 'bc' about this */
4252 assign( ctr_ok, branch_ctr_ok( BO ) );
4253 assign( cond_ok, branch_cond_ok( BO, BI ) );
4254 assign( do_branch,
4255 binop(Iop_And32, mkexpr(cond_ok), mkexpr(ctr_ok)) );
cerion2831b002005-11-30 19:55:22 +00004256
sewardjdf07b882005-11-29 18:19:11 +00004257 assign( lr_old, addr_align( getGST( PPC_GST_LR ), 4 ));
4258
ceriond953ebb2005-11-29 13:27:20 +00004259 if (flag_LK)
4260 putGST( PPC_GST_LR, e_nia );
sewardjb51f0f42005-07-18 11:38:02 +00004261
4262 stmt( IRStmt_Exit(
4263 binop(Iop_CmpEQ32, mkexpr(do_branch), mkU32(0)),
4264 Ijk_Boring,
ceriond953ebb2005-11-29 13:27:20 +00004265 c_nia ));
sewardjb51f0f42005-07-18 11:38:02 +00004266
sewardjd37be032005-11-12 12:56:31 +00004267 /* blrl is pretty strange; it's like a return that sets the
4268 return address of its caller to the insn following this
4269 one. Mark it as a return. */
4270 irbb->jumpkind = Ijk_Ret; /* was flag_LK ? Ijk_Call : Ijk_Ret; */
sewardjdf07b882005-11-29 18:19:11 +00004271 irbb->next = mkexpr(lr_old);
cerionb85e8bb2005-02-16 08:54:33 +00004272 break;
4273
4274 default:
cerion5b2325f2005-12-23 00:55:09 +00004275 vex_printf("dis_int_branch(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004276 return False;
4277 }
4278 break;
cerion2831b002005-11-30 19:55:22 +00004279
cerionb85e8bb2005-02-16 08:54:33 +00004280 default:
cerion5b2325f2005-12-23 00:55:09 +00004281 vex_printf("dis_int_branch(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004282 return False;
4283 }
cerion2831b002005-11-30 19:55:22 +00004284
cerionb85e8bb2005-02-16 08:54:33 +00004285 return True;
cerion91ad5362005-01-27 23:02:41 +00004286}
4287
4288
4289
cerion3d870a32005-03-18 12:23:33 +00004290/*
4291 Condition Register Logical Instructions
4292*/
cerion3007c7f2005-02-23 23:13:29 +00004293static Bool dis_cond_logic ( UInt theInstr )
4294{
4295 /* XL-Form */
cerion76de5cf2005-11-18 18:25:12 +00004296 UChar opc1 = ifieldOPC(theInstr);
4297 UChar crbD_addr = ifieldRegDS(theInstr);
4298 UChar crfD_addr = toUChar( IFIELD(theInstr, 23, 3) );
4299 UChar crbA_addr = ifieldRegA(theInstr);
4300 UChar crfS_addr = toUChar( IFIELD(theInstr, 18, 3) );
4301 UChar crbB_addr = ifieldRegB(theInstr);
4302 UInt opc2 = ifieldOPClo10(theInstr);
4303 UChar b0 = ifieldBIT0(theInstr);
cerion3007c7f2005-02-23 23:13:29 +00004304
cerionf0de28c2005-12-13 20:21:11 +00004305 IRTemp crbD = newTemp(Ity_I32);
4306 IRTemp crbA = newTemp(Ity_I32);
4307 IRTemp crbB = newTemp(Ity_I32);
cerion3007c7f2005-02-23 23:13:29 +00004308
4309 if (opc1 != 19 || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00004310 vex_printf("dis_cond_logic(ppc)(opc1)\n");
cerion3007c7f2005-02-23 23:13:29 +00004311 return False;
4312 }
4313
cerione9d361a2005-03-04 17:35:29 +00004314 if (opc2 == 0) { // mcrf (Move Cond Reg Field, PPC32 p464)
cerion3007c7f2005-02-23 23:13:29 +00004315 if (((crbD_addr & 0x3) != 0) ||
cerion76de5cf2005-11-18 18:25:12 +00004316 ((crbA_addr & 0x3) != 0) || (crbB_addr != 0)) {
cerion5b2325f2005-12-23 00:55:09 +00004317 vex_printf("dis_cond_logic(ppc)(crbD|crbA|crbB != 0)\n");
cerion3007c7f2005-02-23 23:13:29 +00004318 return False;
cerion76de5cf2005-11-18 18:25:12 +00004319 }
ceriond953ebb2005-11-29 13:27:20 +00004320 DIP("mcrf cr%u,cr%u\n", crfD_addr, crfS_addr);
sewardjb51f0f42005-07-18 11:38:02 +00004321 putCR0( crfD_addr, getCR0( crfS_addr) );
4322 putCR321( crfD_addr, getCR321(crfS_addr) );
cerion3007c7f2005-02-23 23:13:29 +00004323 } else {
sewardjb51f0f42005-07-18 11:38:02 +00004324 assign( crbA, getCRbit(crbA_addr) );
ceriona50fde52005-07-01 21:16:10 +00004325 if (crbA_addr == crbB_addr)
sewardjb51f0f42005-07-18 11:38:02 +00004326 crbB = crbA;
ceriona50fde52005-07-01 21:16:10 +00004327 else
sewardjb51f0f42005-07-18 11:38:02 +00004328 assign( crbB, getCRbit(crbB_addr) );
cerion3007c7f2005-02-23 23:13:29 +00004329
4330 switch (opc2) {
sewardj7c2dc712005-09-08 17:33:27 +00004331 case 0x101: // crand (Cond Reg AND, PPC32 p372)
4332 DIP("crand crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4333 assign( crbD, binop(Iop_And32, mkexpr(crbA), mkexpr(crbB)) );
4334 break;
sewardj7787af42005-08-04 18:32:19 +00004335 case 0x081: // crandc (Cond Reg AND w. Complement, PPC32 p373)
4336 DIP("crandc crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4337 assign( crbD, binop(Iop_And32,
4338 mkexpr(crbA),
4339 unop(Iop_Not32, mkexpr(crbB))) );
4340 break;
sewardje14bb9f2005-07-22 09:39:02 +00004341 case 0x121: // creqv (Cond Reg Equivalent, PPC32 p374)
4342 DIP("creqv crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4343 assign( crbD, unop(Iop_Not32,
4344 binop(Iop_Xor32, mkexpr(crbA), mkexpr(crbB))) );
4345 break;
sewardj7c2dc712005-09-08 17:33:27 +00004346 case 0x0E1: // crnand (Cond Reg NAND, PPC32 p375)
4347 DIP("crnand crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4348 assign( crbD, unop(Iop_Not32,
4349 binop(Iop_And32, mkexpr(crbA), mkexpr(crbB))) );
4350 break;
cerione9d361a2005-03-04 17:35:29 +00004351 case 0x021: // crnor (Cond Reg NOR, PPC32 p376)
cerion3007c7f2005-02-23 23:13:29 +00004352 DIP("crnor crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4353 assign( crbD, unop(Iop_Not32,
4354 binop(Iop_Or32, mkexpr(crbA), mkexpr(crbB))) );
4355 break;
cerione9d361a2005-03-04 17:35:29 +00004356 case 0x1C1: // cror (Cond Reg OR, PPC32 p377)
cerion3007c7f2005-02-23 23:13:29 +00004357 DIP("cror crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4358 assign( crbD, binop(Iop_Or32, mkexpr(crbA), mkexpr(crbB)) );
4359 break;
sewardj7c2dc712005-09-08 17:33:27 +00004360 case 0x1A1: // crorc (Cond Reg OR w. Complement, PPC32 p378)
4361 DIP("crorc crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4362 assign( crbD, binop(Iop_Or32,
4363 mkexpr(crbA),
4364 unop(Iop_Not32, mkexpr(crbB))) );
4365 break;
cerione9d361a2005-03-04 17:35:29 +00004366 case 0x0C1: // crxor (Cond Reg XOR, PPC32 p379)
cerion3007c7f2005-02-23 23:13:29 +00004367 DIP("crxor crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4368 assign( crbD, binop(Iop_Xor32, mkexpr(crbA), mkexpr(crbB)) );
4369 break;
cerion3007c7f2005-02-23 23:13:29 +00004370 default:
cerion5b2325f2005-12-23 00:55:09 +00004371 vex_printf("dis_cond_logic(ppc)(opc2)\n");
cerion3007c7f2005-02-23 23:13:29 +00004372 return False;
4373 }
4374
sewardjb51f0f42005-07-18 11:38:02 +00004375 putCRbit( crbD_addr, mkexpr(crbD) );
cerion3007c7f2005-02-23 23:13:29 +00004376 }
4377 return True;
4378}
4379
4380
cerion3d870a32005-03-18 12:23:33 +00004381/*
4382 System Linkage Instructions
4383*/
sewardj9e6491a2005-07-02 19:24:10 +00004384static Bool dis_syslink ( UInt theInstr, DisResult* dres )
cerion8c3adda2005-01-31 11:54:05 +00004385{
ceriond953ebb2005-11-29 13:27:20 +00004386 IRType ty = mode64 ? Ity_I64 : Ity_I32;
4387
cerionb85e8bb2005-02-16 08:54:33 +00004388 if (theInstr != 0x44000002) {
cerion5b2325f2005-12-23 00:55:09 +00004389 vex_printf("dis_syslink(ppc)(theInstr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004390 return False;
4391 }
cerione1d857b2005-02-04 18:29:05 +00004392
cerione9d361a2005-03-04 17:35:29 +00004393 // sc (System Call, PPC32 p504)
cerionb85e8bb2005-02-16 08:54:33 +00004394 DIP("sc\n");
4395
4396 /* It's important that all ArchRegs carry their up-to-date value
4397 at this point. So we declare an end-of-block here, which
4398 forces any TempRegs caching ArchRegs to be flushed. */
cerion2831b002005-11-30 19:55:22 +00004399 irbb->next = mkSzImm( ty, nextInsnAddr() );
sewardj4fa325a2005-11-03 13:27:24 +00004400 irbb->jumpkind = Ijk_Sys_syscall;
ceriond953ebb2005-11-29 13:27:20 +00004401
sewardj9e6491a2005-07-02 19:24:10 +00004402 dres->whatNext = Dis_StopHere;
cerionb85e8bb2005-02-16 08:54:33 +00004403 return True;
cerion8c3adda2005-01-31 11:54:05 +00004404}
4405
cerion3d870a32005-03-18 12:23:33 +00004406
4407/*
4408 Memory Synchronization Instructions
cerion07b07a92005-12-22 14:32:35 +00004409
4410 Note on Reservations:
4411 We rely on the assumption that V will in fact only allow one thread at
4412 once to run. In effect, a thread can make a reservation, but we don't
4413 check any stores it does. Instead, the reservation is cancelled when
4414 the scheduler switches to another thread (run_thread_for_a_while()).
cerion3d870a32005-03-18 12:23:33 +00004415*/
cerion8c3adda2005-01-31 11:54:05 +00004416static Bool dis_memsync ( UInt theInstr )
4417{
cerionb85e8bb2005-02-16 08:54:33 +00004418 /* X-Form, XL-Form */
ceriond953ebb2005-11-29 13:27:20 +00004419 UChar opc1 = ifieldOPC(theInstr);
4420 UInt b11to25 = IFIELD(theInstr, 11, 15);
4421 UChar rD_addr = ifieldRegDS(theInstr);
4422 UChar rS_addr = rD_addr;
4423 UChar rA_addr = ifieldRegA(theInstr);
4424 UChar rB_addr = ifieldRegB(theInstr);
4425 UInt opc2 = ifieldOPClo10(theInstr);
4426 UChar b0 = ifieldBIT0(theInstr);
cerionedf7fc52005-11-18 20:57:41 +00004427
ceriond953ebb2005-11-29 13:27:20 +00004428 IRType ty = mode64 ? Ity_I64 : Ity_I32;
4429 IRTemp EA = newTemp(ty);
4430 IRTemp rS = newTemp(ty);
4431
4432 assign( EA, ea_rAor0_idxd( rA_addr, rB_addr ) );
4433
cerionb85e8bb2005-02-16 08:54:33 +00004434 switch (opc1) {
sewardjafe85832005-09-09 10:25:39 +00004435 /* XL-Form */
cerione9d361a2005-03-04 17:35:29 +00004436 case 0x13: // isync (Instruction Synchronize, PPC32 p432)
cerionb85e8bb2005-02-16 08:54:33 +00004437 if (opc2 != 0x096) {
cerion5b2325f2005-12-23 00:55:09 +00004438 vex_printf("dis_memsync(ppc)(0x13,opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004439 return False;
4440 }
4441 if (b11to25 != 0 || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00004442 vex_printf("dis_memsync(ppc)(0x13,b11to25|b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004443 return False;
4444 }
4445 DIP("isync\n");
cerionb85e8bb2005-02-16 08:54:33 +00004446 stmt( IRStmt_MFence() );
4447 break;
cerion8c3adda2005-01-31 11:54:05 +00004448
cerionb85e8bb2005-02-16 08:54:33 +00004449 /* X-Form */
4450 case 0x1F:
4451 switch (opc2) {
cerion5b2325f2005-12-23 00:55:09 +00004452 case 0x356: // eieio (Enforce In-Order Exec of I/O, PPC32 p394)
sewardj7787af42005-08-04 18:32:19 +00004453 if (b11to25 != 0 || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00004454 vex_printf("dis_memsync(ppc)(eiei0,b11to25|b0)\n");
sewardj7787af42005-08-04 18:32:19 +00004455 return False;
4456 }
4457 DIP("eieio\n");
4458 /* Insert a memory fence, just to be on the safe side. */
4459 stmt( IRStmt_MFence() );
4460 break;
cerion8c3adda2005-01-31 11:54:05 +00004461
cerione9d361a2005-03-04 17:35:29 +00004462 case 0x014: // lwarx (Load Word and Reserve Indexed, PPC32 p458)
cerionb85e8bb2005-02-16 08:54:33 +00004463 if (b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00004464 vex_printf("dis_memsync(ppc)(lwarx,b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004465 return False;
4466 }
ceriond953ebb2005-11-29 13:27:20 +00004467 DIP("lwarx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
cerion5b2325f2005-12-23 00:55:09 +00004468 putIReg( rD_addr, mkSzWiden32(ty, loadBE(Ity_I32, mkexpr(EA)),
4469 False) );
cerion76de5cf2005-11-18 18:25:12 +00004470 /* Take a reservation */
ceriond953ebb2005-11-29 13:27:20 +00004471 putGST( PPC_GST_RESVN, mkexpr(EA) );
cerionb85e8bb2005-02-16 08:54:33 +00004472 break;
4473
sewardj7787af42005-08-04 18:32:19 +00004474 case 0x096: {
4475 // stwcx. (Store Word Conditional Indexed, PPC32 p532)
cerionf0de28c2005-12-13 20:21:11 +00004476 IRTemp resaddr = newTemp(ty);
cerionb85e8bb2005-02-16 08:54:33 +00004477 if (b0 != 1) {
cerion5b2325f2005-12-23 00:55:09 +00004478 vex_printf("dis_memsync(ppc)(stwcx.,b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004479 return False;
4480 }
ceriond953ebb2005-11-29 13:27:20 +00004481 DIP("stwcx. r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
cerion76de5cf2005-11-18 18:25:12 +00004482 assign( rS, getIReg(rS_addr) );
sewardjafe85832005-09-09 10:25:39 +00004483
cerion76de5cf2005-11-18 18:25:12 +00004484 /* First set up as if the reservation failed */
sewardj7787af42005-08-04 18:32:19 +00004485 // Set CR0[LT GT EQ S0] = 0b000 || XER[SO]
4486 putCR321(0, mkU8(0<<1));
4487 putCR0(0, getXER_SO());
4488
cerion76de5cf2005-11-18 18:25:12 +00004489 /* Get the reservation address into a temporary, then
4490 clear it. */
ceriond953ebb2005-11-29 13:27:20 +00004491 assign( resaddr, getGST(PPC_GST_RESVN) );
cerion2831b002005-11-30 19:55:22 +00004492 putGST( PPC_GST_RESVN, mkSzImm(ty, 0) );
sewardj7787af42005-08-04 18:32:19 +00004493
cerion76de5cf2005-11-18 18:25:12 +00004494 /* Skip the rest if the reservation really did fail. */
sewardj7787af42005-08-04 18:32:19 +00004495 stmt( IRStmt_Exit(
ceriond953ebb2005-11-29 13:27:20 +00004496 ( mode64 ?
4497 binop(Iop_CmpNE64, mkexpr(resaddr), mkexpr(EA)) :
4498 binop(Iop_CmpNE32, mkexpr(resaddr), mkexpr(EA)) ),
sewardj7787af42005-08-04 18:32:19 +00004499 Ijk_Boring,
cerion2831b002005-11-30 19:55:22 +00004500 mkSzConst( ty, nextInsnAddr()) ));
ceriond953ebb2005-11-29 13:27:20 +00004501
4502 /* Note for mode64:
4503 If resaddr != lwarx_resaddr, CR0[EQ] is undefined, and
4504 whether rS is stored is dependent on that value. */
sewardj7787af42005-08-04 18:32:19 +00004505
cerion76de5cf2005-11-18 18:25:12 +00004506 /* Success? Do the store */
4507 storeBE( mkexpr(EA), mkexpr(rS) );
cerionb85e8bb2005-02-16 08:54:33 +00004508
sewardjb51f0f42005-07-18 11:38:02 +00004509 // Set CR0[LT GT EQ S0] = 0b001 || XER[SO]
4510 putCR321(0, mkU8(1<<1));
cerionb85e8bb2005-02-16 08:54:33 +00004511 break;
sewardj7787af42005-08-04 18:32:19 +00004512 }
4513
cerione9d361a2005-03-04 17:35:29 +00004514 case 0x256: // sync (Synchronize, PPC32 p543)
cerionb85e8bb2005-02-16 08:54:33 +00004515 if (b11to25 != 0 || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00004516 vex_printf("dis_memsync(ppc)(sync,b11to25|b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004517 return False;
4518 }
4519 DIP("sync\n");
4520 /* Insert a memory fence. It's sometimes important that these
4521 are carried through to the generated code. */
4522 stmt( IRStmt_MFence() );
4523 break;
cerionf0de28c2005-12-13 20:21:11 +00004524
4525
4526 /* 64bit Memsync */
cerion5b2325f2005-12-23 00:55:09 +00004527 case 0x054: // ldarx (Load DWord and Reserve Indexed, PPC64 p473)
cerionf0de28c2005-12-13 20:21:11 +00004528 if (b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00004529 vex_printf("dis_memsync(ppc)(ldarx,b0)\n");
cerionf0de28c2005-12-13 20:21:11 +00004530 return False;
4531 }
4532 DIP("ldarx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
cerion07b07a92005-12-22 14:32:35 +00004533 putIReg( rD_addr, loadBE(Ity_I64, mkexpr(EA)) );
4534 // Take a reservation
4535 putGST( PPC_GST_RESVN, mkexpr(EA) );
4536 break;
4537
cerion5b2325f2005-12-23 00:55:09 +00004538 case 0x0D6: { // stdcx. (Store DWord Condition Indexd, PPC64 p581)
cerion07b07a92005-12-22 14:32:35 +00004539 IRTemp resaddr = newTemp(ty);
cerionf0de28c2005-12-13 20:21:11 +00004540 if (b0 != 1) {
cerion5b2325f2005-12-23 00:55:09 +00004541 vex_printf("dis_memsync(ppc)(stdcx.,b0)\n");
cerionf0de28c2005-12-13 20:21:11 +00004542 return False;
4543 }
4544 DIP("stdcx. r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
cerion07b07a92005-12-22 14:32:35 +00004545 assign( rS, getIReg(rS_addr) );
cerionf0de28c2005-12-13 20:21:11 +00004546
cerion07b07a92005-12-22 14:32:35 +00004547 // First set up as if the reservation failed
4548 // Set CR0[LT GT EQ S0] = 0b000 || XER[SO]
4549 putCR321(0, mkU8(0<<1));
4550 putCR0(0, getXER_SO());
cerionf0de28c2005-12-13 20:21:11 +00004551
cerion07b07a92005-12-22 14:32:35 +00004552 // Get the reservation address into a temporary, then clear it.
4553 assign( resaddr, getGST(PPC_GST_RESVN) );
4554 putGST( PPC_GST_RESVN, mkSzImm(ty, 0) );
cerionf0de28c2005-12-13 20:21:11 +00004555
cerion07b07a92005-12-22 14:32:35 +00004556 // Skip the rest if the reservation really did fail.
4557 stmt( IRStmt_Exit( binop(Iop_CmpNE64, mkexpr(resaddr),
4558 mkexpr(EA)),
4559 Ijk_Boring,
4560 IRConst_U64(nextInsnAddr())) );
cerionf0de28c2005-12-13 20:21:11 +00004561
cerion07b07a92005-12-22 14:32:35 +00004562 // Success? Do the store
4563 storeBE( mkexpr(EA), mkexpr(rS) );
cerionb85e8bb2005-02-16 08:54:33 +00004564
cerion07b07a92005-12-22 14:32:35 +00004565 // Set CR0[LT GT EQ S0] = 0b001 || XER[SO]
4566 putCR321(0, mkU8(1<<1));
4567 break;
4568 }
4569
cerionb85e8bb2005-02-16 08:54:33 +00004570 default:
cerion5b2325f2005-12-23 00:55:09 +00004571 vex_printf("dis_memsync(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004572 return False;
4573 }
4574 break;
cerion8c3adda2005-01-31 11:54:05 +00004575
cerionb85e8bb2005-02-16 08:54:33 +00004576 default:
cerion5b2325f2005-12-23 00:55:09 +00004577 vex_printf("dis_memsync(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004578 return False;
4579 }
4580 return True;
cerion8c3adda2005-01-31 11:54:05 +00004581}
4582
4583
4584
cerion3d870a32005-03-18 12:23:33 +00004585/*
4586 Integer Shift Instructions
4587*/
cerion645c9302005-01-31 10:09:59 +00004588static Bool dis_int_shift ( UInt theInstr )
4589{
cerionf0de28c2005-12-13 20:21:11 +00004590 /* X-Form, XS-Form */
cerion76de5cf2005-11-18 18:25:12 +00004591 UChar opc1 = ifieldOPC(theInstr);
4592 UChar rS_addr = ifieldRegDS(theInstr);
4593 UChar rA_addr = ifieldRegA(theInstr);
4594 UChar rB_addr = ifieldRegB(theInstr);
4595 UChar sh_imm = rB_addr;
4596 UInt opc2 = ifieldOPClo10(theInstr);
cerionf0de28c2005-12-13 20:21:11 +00004597 UChar b1 = ifieldBIT1(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00004598 UChar flag_rC = ifieldBIT0(theInstr);
4599
ceriond953ebb2005-11-29 13:27:20 +00004600 IRType ty = mode64 ? Ity_I64 : Ity_I32;
4601 IRTemp rA = newTemp(ty);
cerion07b07a92005-12-22 14:32:35 +00004602 IRTemp rS = newTemp(ty);
4603 IRTemp rB = newTemp(ty);
ceriond953ebb2005-11-29 13:27:20 +00004604 IRTemp outofrange = newTemp(Ity_I8);
ceriond953ebb2005-11-29 13:27:20 +00004605 IRTemp rS_lo32 = newTemp(Ity_I32);
4606 IRTemp rB_lo32 = newTemp(Ity_I32);
4607 IRExpr* e_tmp;
4608
cerion07b07a92005-12-22 14:32:35 +00004609 assign( rS, getIReg(rS_addr) );
4610 assign( rB, getIReg(rB_addr) );
4611 assign( rS_lo32, mkSzNarrow32(ty, mkexpr(rS)) );
4612 assign( rB_lo32, mkSzNarrow32(ty, mkexpr(rB)) );
cerionb85e8bb2005-02-16 08:54:33 +00004613
4614 if (opc1 == 0x1F) {
4615 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00004616 case 0x018: { // slw (Shift Left Word, PPC32 p505)
cerion5b2325f2005-12-23 00:55:09 +00004617 DIP("slw%s r%u,r%u,r%u\n", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00004618 rA_addr, rS_addr, rB_addr);
4619 /* rA = rS << rB */
4620 /* ppc32 semantics are:
sewardjdfb11442005-10-08 19:58:48 +00004621 slw(x,y) = (x << (y & 31)) -- primary result
4622 & ~((y << 26) >>s 31) -- make result 0
4623 for y in 32 .. 63
4624 */
ceriond953ebb2005-11-29 13:27:20 +00004625 e_tmp =
4626 binop( Iop_And32,
4627 binop( Iop_Shl32,
4628 mkexpr(rS_lo32),
4629 unop( Iop_32to8,
4630 binop(Iop_And32,
4631 mkexpr(rB_lo32), mkU32(31)))),
4632 unop( Iop_Not32,
4633 binop( Iop_Sar32,
4634 binop(Iop_Shl32, mkexpr(rB_lo32), mkU8(26)),
4635 mkU8(31))) );
cerion2831b002005-11-30 19:55:22 +00004636 assign( rA, mkSzWiden32(ty, e_tmp, /* Signed */False) );
cerionb85e8bb2005-02-16 08:54:33 +00004637 break;
ceriond953ebb2005-11-29 13:27:20 +00004638 }
4639
cerion5b2325f2005-12-23 00:55:09 +00004640 case 0x318: { // sraw (Shift Right Alg Word, PPC32 p506)
cerion07b07a92005-12-22 14:32:35 +00004641 IRTemp sh_amt = newTemp(Ity_I32);
cerion5b2325f2005-12-23 00:55:09 +00004642 DIP("sraw%s r%u,r%u,r%u\n", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00004643 rA_addr, rS_addr, rB_addr);
cerion76de5cf2005-11-18 18:25:12 +00004644 /* JRS: my reading of the (poorly worded) PPC32 doc p506 is:
4645 amt = rB & 63
4646 rA = Sar32( rS, amt > 31 ? 31 : amt )
4647 XER.CA = amt > 31 ? sign-of-rS : (computation as per srawi)
4648 */
cerion5b2325f2005-12-23 00:55:09 +00004649 assign( sh_amt, binop(Iop_And32, mkU32(0x3F),
4650 mkexpr(rB_lo32)) );
cerion76de5cf2005-11-18 18:25:12 +00004651 assign( outofrange,
sewardj20ef5472005-07-21 14:48:31 +00004652 unop( Iop_1Uto8,
cerion5b2325f2005-12-23 00:55:09 +00004653 binop(Iop_CmpLT32U, mkU32(31),
4654 mkexpr(sh_amt)) ));
ceriond953ebb2005-11-29 13:27:20 +00004655 e_tmp = binop( Iop_Sar32,
4656 mkexpr(rS_lo32),
sewardj20ef5472005-07-21 14:48:31 +00004657 unop( Iop_32to8,
4658 IRExpr_Mux0X( mkexpr(outofrange),
cerion07b07a92005-12-22 14:32:35 +00004659 mkexpr(sh_amt),
ceriond953ebb2005-11-29 13:27:20 +00004660 mkU32(31)) ) );
cerion2831b002005-11-30 19:55:22 +00004661 assign( rA, mkSzWiden32(ty, e_tmp, /* Signed */True) );
4662
cerion5b2325f2005-12-23 00:55:09 +00004663 set_XER_CA( ty, PPCG_FLAG_OP_SRAW,
cerionf0de28c2005-12-13 20:21:11 +00004664 mkexpr(rA),
4665 mkSzWiden32(ty, mkexpr(rS_lo32), True),
cerion07b07a92005-12-22 14:32:35 +00004666 mkSzWiden32(ty, mkexpr(sh_amt), True ),
cerionf0de28c2005-12-13 20:21:11 +00004667 mkSzWiden32(ty, getXER_CA32(), True) );
cerionb85e8bb2005-02-16 08:54:33 +00004668 break;
cerion07b07a92005-12-22 14:32:35 +00004669 }
cerionb85e8bb2005-02-16 08:54:33 +00004670
cerion5b2325f2005-12-23 00:55:09 +00004671 case 0x338: // srawi (Shift Right Alg Word Immediate, PPC32 p507)
4672 DIP("srawi%s r%u,r%u,%d\n", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00004673 rA_addr, rS_addr, sh_imm);
4674 vassert(sh_imm < 32);
cerionf0de28c2005-12-13 20:21:11 +00004675 if (mode64) {
4676 assign( rA, binop(Iop_Sar64,
cerion5b2325f2005-12-23 00:55:09 +00004677 binop(Iop_Shl64, getIReg(rS_addr),
4678 mkU8(32)),
cerionf0de28c2005-12-13 20:21:11 +00004679 mkU8(32 + sh_imm)) );
4680 } else {
cerion5b2325f2005-12-23 00:55:09 +00004681 assign( rA, binop(Iop_Sar32, mkexpr(rS_lo32),
4682 mkU8(sh_imm)) );
cerionf0de28c2005-12-13 20:21:11 +00004683 }
cerion2831b002005-11-30 19:55:22 +00004684
cerion5b2325f2005-12-23 00:55:09 +00004685 set_XER_CA( ty, PPCG_FLAG_OP_SRAWI,
cerionf0de28c2005-12-13 20:21:11 +00004686 mkexpr(rA),
cerion5b2325f2005-12-23 00:55:09 +00004687 mkSzWiden32(ty, mkexpr(rS_lo32), /* Syned */True),
cerionf0de28c2005-12-13 20:21:11 +00004688 mkSzImm(ty, sh_imm),
cerion5b2325f2005-12-23 00:55:09 +00004689 mkSzWiden32(ty, getXER_CA32(), /* Syned */False) );
cerionb85e8bb2005-02-16 08:54:33 +00004690 break;
4691
cerione9d361a2005-03-04 17:35:29 +00004692 case 0x218: // srw (Shift Right Word, PPC32 p508)
cerion5b2325f2005-12-23 00:55:09 +00004693 DIP("srw%s r%u,r%u,r%u\n", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00004694 rA_addr, rS_addr, rB_addr);
4695 /* rA = rS >>u rB */
4696 /* ppc32 semantics are:
cerionf0de28c2005-12-13 20:21:11 +00004697 srw(x,y) = (x >>u (y & 31)) -- primary result
sewardjdfb11442005-10-08 19:58:48 +00004698 & ~((y << 26) >>s 31) -- make result 0
4699 for y in 32 .. 63
4700 */
ceriond953ebb2005-11-29 13:27:20 +00004701 e_tmp =
sewardjdfb11442005-10-08 19:58:48 +00004702 binop(
4703 Iop_And32,
4704 binop( Iop_Shr32,
ceriond953ebb2005-11-29 13:27:20 +00004705 mkexpr(rS_lo32),
sewardjdfb11442005-10-08 19:58:48 +00004706 unop( Iop_32to8,
cerion5b2325f2005-12-23 00:55:09 +00004707 binop(Iop_And32, mkexpr(rB_lo32),
4708 mkU32(31)))),
sewardjdfb11442005-10-08 19:58:48 +00004709 unop( Iop_Not32,
4710 binop( Iop_Sar32,
cerion5b2325f2005-12-23 00:55:09 +00004711 binop(Iop_Shl32, mkexpr(rB_lo32),
4712 mkU8(26)),
ceriond953ebb2005-11-29 13:27:20 +00004713 mkU8(31))));
cerion2831b002005-11-30 19:55:22 +00004714 assign( rA, mkSzWiden32(ty, e_tmp, /* Signed */False) );
cerionb85e8bb2005-02-16 08:54:33 +00004715 break;
cerionf0de28c2005-12-13 20:21:11 +00004716
4717
4718 /* 64bit Shifts */
cerion5b2325f2005-12-23 00:55:09 +00004719 case 0x01B: // sld (Shift Left DWord, PPC64 p568)
4720 DIP("sld%s r%u,r%u,r%u\n",
4721 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerionf0de28c2005-12-13 20:21:11 +00004722 /* rA = rS << rB */
cerion07b07a92005-12-22 14:32:35 +00004723 /* ppc64 semantics are:
cerionf0de28c2005-12-13 20:21:11 +00004724 slw(x,y) = (x << (y & 63)) -- primary result
4725 & ~((y << 57) >>s 63) -- make result 0
4726 for y in 64 ..
4727 */
cerionf0de28c2005-12-13 20:21:11 +00004728 assign( rA,
4729 binop(
4730 Iop_And64,
4731 binop( Iop_Shl64,
4732 mkexpr(rS),
4733 unop( Iop_64to8,
4734 binop(Iop_And64, mkexpr(rB), mkU64(63)))),
4735 unop( Iop_Not64,
4736 binop( Iop_Sar64,
4737 binop(Iop_Shl64, mkexpr(rB), mkU8(57)),
4738 mkU8(63)))) );
cerion07b07a92005-12-22 14:32:35 +00004739 break;
cerionf0de28c2005-12-13 20:21:11 +00004740
cerion5b2325f2005-12-23 00:55:09 +00004741 case 0x31A: { // srad (Shift Right Alg DWord, PPC64 p570)
cerion07b07a92005-12-22 14:32:35 +00004742 IRTemp sh_amt = newTemp(Ity_I64);
cerion5b2325f2005-12-23 00:55:09 +00004743 DIP("srad%s r%u,r%u,r%u\n",
4744 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerionf0de28c2005-12-13 20:21:11 +00004745 /* amt = rB & 127
4746 rA = Sar64( rS, amt > 63 ? 63 : amt )
4747 XER.CA = amt > 63 ? sign-of-rS : (computation as per srawi)
4748 */
cerion07b07a92005-12-22 14:32:35 +00004749 assign( sh_amt, binop(Iop_And64, mkU64(0x7F), mkexpr(rB)) );
cerionf0de28c2005-12-13 20:21:11 +00004750 assign( outofrange,
4751 unop( Iop_1Uto8,
cerion5b2325f2005-12-23 00:55:09 +00004752 binop(Iop_CmpLT64U, mkU64(63),
4753 mkexpr(sh_amt)) ));
cerionf0de28c2005-12-13 20:21:11 +00004754 assign( rA,
4755 binop( Iop_Sar64,
4756 mkexpr(rS),
4757 unop( Iop_64to8,
4758 IRExpr_Mux0X( mkexpr(outofrange),
cerion07b07a92005-12-22 14:32:35 +00004759 mkexpr(sh_amt),
4760 mkU64(63)) ))
cerionf0de28c2005-12-13 20:21:11 +00004761 );
cerion5b2325f2005-12-23 00:55:09 +00004762 set_XER_CA( ty, PPCG_FLAG_OP_SRAD,
cerion07b07a92005-12-22 14:32:35 +00004763 mkexpr(rA), mkexpr(rS), mkexpr(sh_amt),
cerion5b2325f2005-12-23 00:55:09 +00004764 mkSzWiden32(ty, getXER_CA32(), /* Syned */False) );
cerion07b07a92005-12-22 14:32:35 +00004765 break;
4766 }
4767
cerion5b2325f2005-12-23 00:55:09 +00004768 case 0x33A: case 0x33B: // sradi (Shr Alg DWord Imm, PPC64 p571)
cerionf0de28c2005-12-13 20:21:11 +00004769 sh_imm |= b1<<5;
4770 vassert(sh_imm < 64);
cerion5b2325f2005-12-23 00:55:09 +00004771 DIP("sradi%s r%u,r%u,%u\n",
4772 flag_rC ? ".":"", rA_addr, rS_addr, sh_imm);
cerionf0de28c2005-12-13 20:21:11 +00004773 assign( rA, binop(Iop_Sar64, getIReg(rS_addr), mkU8(sh_imm)) );
4774
cerion5b2325f2005-12-23 00:55:09 +00004775 set_XER_CA( ty, PPCG_FLAG_OP_SRADI,
cerionf0de28c2005-12-13 20:21:11 +00004776 mkexpr(rA),
4777 getIReg(rS_addr),
4778 mkU64(sh_imm),
cerion5b2325f2005-12-23 00:55:09 +00004779 mkSzWiden32(ty, getXER_CA32(), /* Syned */False) );
cerionf0de28c2005-12-13 20:21:11 +00004780 break;
4781
cerion5b2325f2005-12-23 00:55:09 +00004782 case 0x21B: // srd (Shift Right DWord, PPC64 p574)
4783 DIP("srd%s r%u,r%u,r%u\n",
4784 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerionf0de28c2005-12-13 20:21:11 +00004785 /* rA = rS >>u rB */
cerion07b07a92005-12-22 14:32:35 +00004786 /* ppc semantics are:
cerionf0de28c2005-12-13 20:21:11 +00004787 srw(x,y) = (x >>u (y & 63)) -- primary result
4788 & ~((y << 57) >>s 63) -- make result 0
4789 for y in 64 .. 127
4790 */
cerionf0de28c2005-12-13 20:21:11 +00004791 assign( rA,
4792 binop(
4793 Iop_And64,
4794 binop( Iop_Shr64,
4795 mkexpr(rS),
4796 unop( Iop_64to8,
4797 binop(Iop_And64, mkexpr(rB), mkU64(63)))),
4798 unop( Iop_Not64,
4799 binop( Iop_Sar64,
4800 binop(Iop_Shl64, mkexpr(rB), mkU8(57)),
4801 mkU8(63)))) );
cerion07b07a92005-12-22 14:32:35 +00004802 break;
cerionf0de28c2005-12-13 20:21:11 +00004803
cerionb85e8bb2005-02-16 08:54:33 +00004804 default:
cerion5b2325f2005-12-23 00:55:09 +00004805 vex_printf("dis_int_shift(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004806 return False;
4807 }
4808 } else {
cerion5b2325f2005-12-23 00:55:09 +00004809 vex_printf("dis_int_shift(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004810 return False;
4811 }
cerion0d330c52005-02-28 16:43:16 +00004812
cerion76de5cf2005-11-18 18:25:12 +00004813 putIReg( rA_addr, mkexpr(rA) );
cerionb85e8bb2005-02-16 08:54:33 +00004814
cerion76de5cf2005-11-18 18:25:12 +00004815 if (flag_rC) {
4816 set_CR0( mkexpr(rA) );
cerionb85e8bb2005-02-16 08:54:33 +00004817 }
4818 return True;
cerion645c9302005-01-31 10:09:59 +00004819}
4820
4821
4822
sewardj602857d2005-09-06 09:10:09 +00004823/*
4824 Integer Load/Store Reverse Instructions
4825*/
sewardjfb957972005-09-08 17:53:03 +00004826static IRExpr* /* :: Ity_I32 */ gen_byterev32 ( IRTemp t )
4827{
4828 vassert(typeOfIRTemp(irbb->tyenv, t) == Ity_I32);
4829 return
4830 binop(Iop_Or32,
4831 binop(Iop_Shl32, mkexpr(t), mkU8(24)),
4832 binop(Iop_Or32,
4833 binop(Iop_And32, binop(Iop_Shl32, mkexpr(t), mkU8(8)),
4834 mkU32(0x00FF0000)),
4835 binop(Iop_Or32,
4836 binop(Iop_And32, binop(Iop_Shr32, mkexpr(t), mkU8(8)),
4837 mkU32(0x0000FF00)),
4838 binop(Iop_And32, binop(Iop_Shr32, mkexpr(t), mkU8(24)),
4839 mkU32(0x000000FF) )
4840 )));
4841}
4842
sewardj602857d2005-09-06 09:10:09 +00004843static Bool dis_int_ldst_rev ( UInt theInstr )
4844{
4845 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00004846 UChar opc1 = ifieldOPC(theInstr);
4847 UChar rD_addr = ifieldRegDS(theInstr);
4848 UChar rS_addr = rD_addr;
4849 UChar rA_addr = ifieldRegA(theInstr);
4850 UChar rB_addr = ifieldRegB(theInstr);
4851 UInt opc2 = ifieldOPClo10(theInstr);
4852 UChar b0 = ifieldBIT0(theInstr);
cerionedf7fc52005-11-18 20:57:41 +00004853
ceriond953ebb2005-11-29 13:27:20 +00004854 IRType ty = mode64 ? Ity_I64 : Ity_I32;
4855 IRTemp EA = newTemp(ty);
cerionedf7fc52005-11-18 20:57:41 +00004856 IRTemp w1 = newTemp(Ity_I32);
4857 IRTemp w2 = newTemp(Ity_I32);
sewardj602857d2005-09-06 09:10:09 +00004858
4859 if (opc1 != 0x1F || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00004860 vex_printf("dis_int_ldst_rev(ppc)(opc1|b0)\n");
sewardj602857d2005-09-06 09:10:09 +00004861 return False;
4862 }
sewardjafe85832005-09-09 10:25:39 +00004863
ceriond953ebb2005-11-29 13:27:20 +00004864 assign( EA, ea_rAor0_idxd( rA_addr, rB_addr ) );
sewardj602857d2005-09-06 09:10:09 +00004865
4866 switch (opc2) {
sewardjb51f0f42005-07-18 11:38:02 +00004867//zz case 0x316: // lhbrx (Load Half Word Byte-Reverse Indexed, PPC32 p449)
4868//zz vassert(0);
4869//zz
ceriond953ebb2005-11-29 13:27:20 +00004870//zz DIP("lhbrx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
sewardjb51f0f42005-07-18 11:38:02 +00004871//zz assign( byte0, loadBE(Ity_I8, mkexpr(EA)) );
4872//zz assign( byte1, loadBE(Ity_I8, binop(Iop_Add32, mkexpr(EA),mkU32(1))) );
cerion76de5cf2005-11-18 18:25:12 +00004873//zz assign( rD, binop(Iop_Or32,
sewardjb51f0f42005-07-18 11:38:02 +00004874//zz binop(Iop_Shl32, mkexpr(byte1), mkU8(8)),
4875//zz mkexpr(byte0)) );
cerion76de5cf2005-11-18 18:25:12 +00004876//zz putIReg( rD_addr, mkexpr(rD));
sewardjb51f0f42005-07-18 11:38:02 +00004877//zz break;
sewardj602857d2005-09-06 09:10:09 +00004878
sewardjfb957972005-09-08 17:53:03 +00004879 case 0x216: // lwbrx (Load Word Byte-Reverse Indexed, PPC32 p459)
ceriond953ebb2005-11-29 13:27:20 +00004880 DIP("lwbrx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
sewardjfb957972005-09-08 17:53:03 +00004881 assign( w1, loadBE(Ity_I32, mkexpr(EA)) );
4882 assign( w2, gen_byterev32(w1) );
cerion5b2325f2005-12-23 00:55:09 +00004883 putIReg( rD_addr, mkSzWiden32(ty, mkexpr(w2),
4884 /* Signed */False) );
sewardjfb957972005-09-08 17:53:03 +00004885 break;
sewardj602857d2005-09-06 09:10:09 +00004886
sewardjb51f0f42005-07-18 11:38:02 +00004887//zz case 0x396: // sthbrx (Store Half Word Byte-Reverse Indexed, PPC32 p523)
4888//zz vassert(0);
4889//zz
ceriond953ebb2005-11-29 13:27:20 +00004890//zz DIP("sthbrx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
cerion76de5cf2005-11-18 18:25:12 +00004891//zz assign( rS, getIReg(rS_addr) );
4892//zz assign( byte0, binop(Iop_And32, mkexpr(rS), mkU32(0x00FF)) );
4893//zz assign( byte1, binop(Iop_And32, mkexpr(rS), mkU32(0xFF00)) );
sewardjb51f0f42005-07-18 11:38:02 +00004894//zz
4895//zz assign( tmp16,
4896//zz unop(Iop_32to16,
4897//zz binop(Iop_Or32,
4898//zz binop(Iop_Shl32, mkexpr(byte0), mkU8(8)),
4899//zz binop(Iop_Shr32, mkexpr(byte1), mkU8(8)))) );
4900//zz storeBE( mkexpr(EA), getIReg(tmp16) );
4901//zz break;
sewardj602857d2005-09-06 09:10:09 +00004902
cerion5b2325f2005-12-23 00:55:09 +00004903 case 0x296: // stwbrx (Store Word Byte-Reverse Indxd, PPC32 p531)
ceriond953ebb2005-11-29 13:27:20 +00004904 DIP("stwbrx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00004905 assign( w1, mkSzNarrow32(ty, getIReg(rS_addr)) );
sewardjfb957972005-09-08 17:53:03 +00004906 storeBE( mkexpr(EA), gen_byterev32(w1) );
4907 break;
4908
4909 default:
cerion5b2325f2005-12-23 00:55:09 +00004910 vex_printf("dis_int_ldst_rev(ppc)(opc2)\n");
sewardjfb957972005-09-08 17:53:03 +00004911 return False;
sewardj602857d2005-09-06 09:10:09 +00004912 }
4913 return True;
4914}
cerion645c9302005-01-31 10:09:59 +00004915
4916
4917
cerion3d870a32005-03-18 12:23:33 +00004918/*
4919 Processor Control Instructions
4920*/
cerion8c3adda2005-01-31 11:54:05 +00004921static Bool dis_proc_ctl ( UInt theInstr )
4922{
cerion76de5cf2005-11-18 18:25:12 +00004923 UChar opc1 = ifieldOPC(theInstr);
cerionb85e8bb2005-02-16 08:54:33 +00004924
4925 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00004926 UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) );
4927 UChar b21to22 = toUChar( IFIELD( theInstr, 21, 2 ) );
4928 UChar rD_addr = ifieldRegDS(theInstr);
4929 UInt b11to20 = IFIELD( theInstr, 11, 10 );
cerione9d361a2005-03-04 17:35:29 +00004930
cerion76de5cf2005-11-18 18:25:12 +00004931 /* XFX-Form */
4932 UChar rS_addr = rD_addr;
4933 UInt SPR = b11to20;
cerionedf7fc52005-11-18 20:57:41 +00004934 UInt TBR = b11to20;
cerion76de5cf2005-11-18 18:25:12 +00004935 UChar b20 = toUChar( IFIELD( theInstr, 20, 1 ) );
4936 UInt CRM = IFIELD( theInstr, 12, 8 );
4937 UChar b11 = toUChar( IFIELD( theInstr, 11, 1 ) );
4938
4939 UInt opc2 = ifieldOPClo10(theInstr);
4940 UChar b0 = ifieldBIT0(theInstr);
4941
cerion2831b002005-11-30 19:55:22 +00004942 IRType ty = mode64 ? Ity_I64 : Ity_I32;
cerionf0de28c2005-12-13 20:21:11 +00004943 IRTemp rS = newTemp(ty);
ceriond953ebb2005-11-29 13:27:20 +00004944 assign( rS, getIReg(rS_addr) );
sewardj41a7b702005-11-18 22:18:23 +00004945
cerion76de5cf2005-11-18 18:25:12 +00004946 /* Reorder SPR field as per PPC32 p470 */
4947 SPR = ((SPR & 0x1F) << 5) | ((SPR >> 5) & 0x1F);
sewardj73a91972005-09-06 10:25:46 +00004948 /* Reorder TBR field as per PPC32 p475 */
4949 TBR = ((TBR & 31) << 5) | ((TBR >> 5) & 31);
cerionb85e8bb2005-02-16 08:54:33 +00004950
4951 if (opc1 != 0x1F || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00004952 vex_printf("dis_proc_ctl(ppc)(opc1|b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004953 return False;
4954 }
4955
4956 switch (opc2) {
cerioncb14e732005-09-09 16:38:19 +00004957 /* X-Form */
cerion5b2325f2005-12-23 00:55:09 +00004958 case 0x200: { // mcrxr (Move to Cond Register from XER, PPC32 p466)
cerioncb14e732005-09-09 16:38:19 +00004959 if (b21to22 != 0 || b11to20 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00004960 vex_printf("dis_proc_ctl(ppc)(mcrxr,b21to22|b11to20)\n");
cerioncb14e732005-09-09 16:38:19 +00004961 return False;
4962 }
4963 DIP("mcrxr crf%d\n", crfD);
cerionedf7fc52005-11-18 20:57:41 +00004964 /* Move XER[0-3] (the top 4 bits of XER) to CR[crfD] */
ceriond953ebb2005-11-29 13:27:20 +00004965 putGST_field( PPC_GST_CR,
4966 getGST_field( PPC_GST_XER, 7 ),
cerionedf7fc52005-11-18 20:57:41 +00004967 crfD );
sewardj55ccc3e2005-09-09 19:45:02 +00004968
4969 // Clear XER[0-3]
cerionedf7fc52005-11-18 20:57:41 +00004970 putXER_SO( mkU8(0) );
4971 putXER_OV( mkU8(0) );
4972 putXER_CA( mkU8(0) );
cerioncb14e732005-09-09 16:38:19 +00004973 break;
sewardj55ccc3e2005-09-09 19:45:02 +00004974 }
cerionb85e8bb2005-02-16 08:54:33 +00004975
cerion5b2325f2005-12-23 00:55:09 +00004976 case 0x013: // mfcr (Move from Cond Register, PPC32 p467)
cerionb85e8bb2005-02-16 08:54:33 +00004977 if (b11to20 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00004978 vex_printf("dis_proc_ctl(ppc)(mfcr,b11to20)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004979 return False;
4980 }
ceriond953ebb2005-11-29 13:27:20 +00004981 DIP("mfcr r%u\n", rD_addr);
cerion2831b002005-11-30 19:55:22 +00004982 putIReg( rD_addr, mkSzWiden32(ty, getGST( PPC_GST_CR ),
4983 /* Signed */False) );
cerionb85e8bb2005-02-16 08:54:33 +00004984 break;
4985
4986 /* XFX-Form */
cerione9d361a2005-03-04 17:35:29 +00004987 case 0x153: // mfspr (Move from Special-Purpose Register, PPC32 p470)
cerionb85e8bb2005-02-16 08:54:33 +00004988
cerion76de5cf2005-11-18 18:25:12 +00004989 switch (SPR) { // Choose a register...
ceriond953ebb2005-11-29 13:27:20 +00004990 case 0x1:
4991 DIP("mfxer r%u\n", rD_addr);
cerion2831b002005-11-30 19:55:22 +00004992 putIReg( rD_addr, mkSzWiden32(ty, getGST( PPC_GST_XER ),
4993 /* Signed */False) );
ceriond953ebb2005-11-29 13:27:20 +00004994 break;
4995 case 0x8:
4996 DIP("mflr r%u\n", rD_addr);
4997 putIReg( rD_addr, getGST( PPC_GST_LR ) );
4998 break;
4999 case 0x9:
5000 DIP("mfctr r%u\n", rD_addr);
5001 putIReg( rD_addr, getGST( PPC_GST_CTR ) );
5002 break;
5003 case 0x100:
5004 DIP("mfvrsave r%u\n", rD_addr);
cerion2831b002005-11-30 19:55:22 +00005005 putIReg( rD_addr, mkSzWiden32(ty, getGST( PPC_GST_VRSAVE ),
5006 /* Signed */False) );
ceriond953ebb2005-11-29 13:27:20 +00005007 break;
5008
5009 default:
cerion5b2325f2005-12-23 00:55:09 +00005010 vex_printf("dis_proc_ctl(ppc)(mfspr,SPR)(0x%x)\n", SPR);
ceriond953ebb2005-11-29 13:27:20 +00005011 return False;
cerionb85e8bb2005-02-16 08:54:33 +00005012 }
5013 break;
5014
sewardj73a91972005-09-06 10:25:46 +00005015 case 0x173: { // mftb (Move from Time Base, PPC32 p475)
5016 IRTemp val = newTemp(Ity_I64);
5017 IRExpr** args = mkIRExprVec_0();
cerion5b2325f2005-12-23 00:55:09 +00005018 IRDirty* d = unsafeIRDirty_1_N( val,
5019 0/*regparms*/,
5020 "ppcg_dirtyhelper_MFTB",
5021 &ppcg_dirtyhelper_MFTB,
5022 args );
sewardj73a91972005-09-06 10:25:46 +00005023 /* execute the dirty call, dumping the result in val. */
5024 stmt( IRStmt_Dirty(d) );
5025
5026 switch (TBR) {
ceriond953ebb2005-11-29 13:27:20 +00005027 case 269:
ceriond953ebb2005-11-29 13:27:20 +00005028 DIP("mftbu r%u", rD_addr);
cerion2831b002005-11-30 19:55:22 +00005029 putIReg( rD_addr,
5030 mkSzWiden32(ty, unop(Iop_64HIto32, mkexpr(val)),
5031 /* Signed */False) );
ceriond953ebb2005-11-29 13:27:20 +00005032 break;
5033 case 268:
ceriond953ebb2005-11-29 13:27:20 +00005034 DIP("mftb r%u", rD_addr);
cerion2831b002005-11-30 19:55:22 +00005035 putIReg( rD_addr, (mode64) ? mkexpr(val) :
5036 unop(Iop_64to32, mkexpr(val)) );
ceriond953ebb2005-11-29 13:27:20 +00005037 break;
5038 default:
5039 return False; /* illegal instruction */
sewardj73a91972005-09-06 10:25:46 +00005040 }
5041 break;
5042 }
5043
cerion5b2325f2005-12-23 00:55:09 +00005044 case 0x090: { // mtcrf (Move to Cond Register Fields, PPC32 p477)
cerionedf7fc52005-11-18 20:57:41 +00005045 Int cr;
5046 UChar shft;
cerionb85e8bb2005-02-16 08:54:33 +00005047 if (b11 != 0 || b20 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00005048 vex_printf("dis_proc_ctl(ppc)(mtcrf,b11|b20)\n");
cerionb85e8bb2005-02-16 08:54:33 +00005049 return False;
5050 }
ceriond953ebb2005-11-29 13:27:20 +00005051 DIP("mtcrf 0x%x,r%u\n", CRM, rS_addr);
cerionedf7fc52005-11-18 20:57:41 +00005052 /* Write to each field specified by CRM */
5053 for (cr = 0; cr < 8; cr++) {
5054 if ((CRM & (1 << (7-cr))) == 0)
5055 continue;
5056 shft = 4*(7-cr);
ceriond953ebb2005-11-29 13:27:20 +00005057 putGST_field( PPC_GST_CR,
cerion2831b002005-11-30 19:55:22 +00005058 binop(Iop_Shr32,
5059 mkSzNarrow32(ty, mkexpr(rS)),
5060 mkU8(shft)), cr );
cerionedf7fc52005-11-18 20:57:41 +00005061 }
cerionb85e8bb2005-02-16 08:54:33 +00005062 break;
cerionedf7fc52005-11-18 20:57:41 +00005063 }
cerione9d361a2005-03-04 17:35:29 +00005064
5065 case 0x1D3: // mtspr (Move to Special-Purpose Register, PPC32 p483)
cerionb85e8bb2005-02-16 08:54:33 +00005066
cerion76de5cf2005-11-18 18:25:12 +00005067 switch (SPR) { // Choose a register...
ceriond953ebb2005-11-29 13:27:20 +00005068 case 0x1:
5069 DIP("mtxer r%u\n", rS_addr);
cerion2831b002005-11-30 19:55:22 +00005070 putGST( PPC_GST_XER, mkSzNarrow32(ty, mkexpr(rS)) );
ceriond953ebb2005-11-29 13:27:20 +00005071 break;
5072 case 0x8:
5073 DIP("mtlr r%u\n", rS_addr);
5074 putGST( PPC_GST_LR, mkexpr(rS) );
5075 break;
5076 case 0x9:
5077 DIP("mtctr r%u\n", rS_addr);
5078 putGST( PPC_GST_CTR, mkexpr(rS) );
5079 break;
5080 case 0x100:
5081 DIP("mtvrsave r%u\n", rS_addr);
cerion2831b002005-11-30 19:55:22 +00005082 putGST( PPC_GST_VRSAVE, mkSzNarrow32(ty, mkexpr(rS)) );
ceriond953ebb2005-11-29 13:27:20 +00005083 break;
5084
5085 default:
cerion5b2325f2005-12-23 00:55:09 +00005086 vex_printf("dis_proc_ctl(ppc)(mtspr,SPR)(%u)\n", SPR);
ceriond953ebb2005-11-29 13:27:20 +00005087 return False;
cerionb85e8bb2005-02-16 08:54:33 +00005088 }
5089 break;
5090
5091 default:
cerion5b2325f2005-12-23 00:55:09 +00005092 vex_printf("dis_proc_ctl(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00005093 return False;
5094 }
5095 return True;
cerion8c3adda2005-01-31 11:54:05 +00005096}
5097
5098
cerion3d870a32005-03-18 12:23:33 +00005099/*
5100 Cache Management Instructions
5101*/
sewardjd94b73a2005-06-30 12:08:48 +00005102static Bool dis_cache_manage ( UInt theInstr,
sewardj9e6491a2005-07-02 19:24:10 +00005103 DisResult* dres,
sewardjd94b73a2005-06-30 12:08:48 +00005104 VexArchInfo* guest_archinfo )
cerion8c3adda2005-01-31 11:54:05 +00005105{
cerionb85e8bb2005-02-16 08:54:33 +00005106 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00005107 UChar opc1 = ifieldOPC(theInstr);
5108 UChar b21to25 = ifieldRegDS(theInstr);
5109 UChar rA_addr = ifieldRegA(theInstr);
5110 UChar rB_addr = ifieldRegB(theInstr);
5111 UInt opc2 = ifieldOPClo10(theInstr);
5112 UChar b0 = ifieldBIT0(theInstr);
cerion5b2325f2005-12-23 00:55:09 +00005113 UInt lineszB = guest_archinfo->ppc_cache_line_szB;
ceriond953ebb2005-11-29 13:27:20 +00005114
5115 IRType ty = mode64 ? Ity_I64 : Ity_I32;
cerion094d1392005-06-20 13:45:57 +00005116
cerionb85e8bb2005-02-16 08:54:33 +00005117 if (opc1 != 0x1F || b21to25 != 0 || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00005118 vex_printf("dis_cache_manage(ppc)(opc1|b21to25|b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00005119 return False;
5120 }
sewardjd94b73a2005-06-30 12:08:48 +00005121
5122 /* stay sane .. */
5123 vassert(lineszB == 32 || lineszB == 128);
cerionb85e8bb2005-02-16 08:54:33 +00005124
5125 switch (opc2) {
sewardjb51f0f42005-07-18 11:38:02 +00005126//zz case 0x2F6: // dcba (Data Cache Block Allocate, PPC32 p380)
5127//zz vassert(0); /* AWAITING TEST CASE */
ceriond953ebb2005-11-29 13:27:20 +00005128//zz DIP("dcba r%u,r%u\n", rA_addr, rB_addr);
cerion5b2325f2005-12-23 00:55:09 +00005129//zz if (0) vex_printf("vex ppc->IR: kludged dcba\n");
sewardjb51f0f42005-07-18 11:38:02 +00005130//zz break;
sewardj20ef5472005-07-21 14:48:31 +00005131
5132 case 0x056: // dcbf (Data Cache Block Flush, PPC32 p382)
ceriond953ebb2005-11-29 13:27:20 +00005133 DIP("dcbf r%u,r%u\n", rA_addr, rB_addr);
sewardj20ef5472005-07-21 14:48:31 +00005134 /* nop as far as vex is concerned */
cerion5b2325f2005-12-23 00:55:09 +00005135 if (0) vex_printf("vex ppc->IR: kludged dcbf\n");
sewardj20ef5472005-07-21 14:48:31 +00005136 break;
cerionb85e8bb2005-02-16 08:54:33 +00005137
cerione9d361a2005-03-04 17:35:29 +00005138 case 0x036: // dcbst (Data Cache Block Store, PPC32 p384)
ceriond953ebb2005-11-29 13:27:20 +00005139 DIP("dcbst r%u,r%u\n", rA_addr, rB_addr);
sewardjb51f0f42005-07-18 11:38:02 +00005140 /* nop as far as vex is concerned */
cerionb85e8bb2005-02-16 08:54:33 +00005141 break;
cerion8c3adda2005-01-31 11:54:05 +00005142
cerione9d361a2005-03-04 17:35:29 +00005143 case 0x116: // dcbt (Data Cache Block Touch, PPC32 p385)
ceriond953ebb2005-11-29 13:27:20 +00005144 DIP("dcbt r%u,r%u\n", rA_addr, rB_addr);
sewardjb51f0f42005-07-18 11:38:02 +00005145 /* nop as far as vex is concerned */
cerionb85e8bb2005-02-16 08:54:33 +00005146 break;
5147
cerione9d361a2005-03-04 17:35:29 +00005148 case 0x0F6: // dcbtst (Data Cache Block Touch for Store, PPC32 p386)
ceriond953ebb2005-11-29 13:27:20 +00005149 DIP("dcbtst r%u,r%u\n", rA_addr, rB_addr);
sewardjb51f0f42005-07-18 11:38:02 +00005150 /* nop as far as vex is concerned */
cerionb85e8bb2005-02-16 08:54:33 +00005151 break;
5152
cerion094d1392005-06-20 13:45:57 +00005153 case 0x3F6: { // dcbz (Data Cache Block Clear to Zero, PPC32 p387)
sewardjd94b73a2005-06-30 12:08:48 +00005154 /* Clear all bytes in cache block at (rA|0) + rB. */
cerion2831b002005-11-30 19:55:22 +00005155 IRTemp EA = newTemp(ty);
5156 IRTemp addr = newTemp(ty);
cerion094d1392005-06-20 13:45:57 +00005157 IRExpr* irx_addr;
cerion094d1392005-06-20 13:45:57 +00005158 UInt i;
ceriond953ebb2005-11-29 13:27:20 +00005159 DIP("dcbz r%u,r%u\n", rA_addr, rB_addr);
sewardjcb1f68e2005-12-30 03:39:14 +00005160
ceriond953ebb2005-11-29 13:27:20 +00005161 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
cerion094d1392005-06-20 13:45:57 +00005162
cerion2831b002005-11-30 19:55:22 +00005163 if (mode64) {
5164 /* Round EA down to the start of the containing block. */
5165 assign( addr, binop( Iop_And64,
5166 mkexpr(EA),
5167 mkU64( ~((ULong)lineszB-1) )) );
5168
5169 for (i = 0; i < lineszB / 8; i++) {
5170 irx_addr = binop( Iop_Add64, mkexpr(addr), mkU64(i*8) );
5171 storeBE( irx_addr, mkU64(0) );
5172 }
5173 } else {
5174 /* Round EA down to the start of the containing block. */
5175 assign( addr, binop( Iop_And32,
5176 mkexpr(EA),
5177 mkU32( ~(lineszB-1) )) );
5178
5179 for (i = 0; i < lineszB / 4; i++) {
5180 irx_addr = binop( Iop_Add32, mkexpr(addr), mkU32(i*4) );
5181 storeBE( irx_addr, mkU32(0) );
5182 }
cerion094d1392005-06-20 13:45:57 +00005183 }
cerionb85e8bb2005-02-16 08:54:33 +00005184 break;
cerion094d1392005-06-20 13:45:57 +00005185 }
cerion8c3adda2005-01-31 11:54:05 +00005186
sewardj7ce9d152005-03-15 16:54:13 +00005187 case 0x3D6: {
5188 // icbi (Instruction Cache Block Invalidate, PPC32 p431)
5189 /* Invalidate all translations containing code from the cache
sewardjd94b73a2005-06-30 12:08:48 +00005190 block at (rA|0) + rB. */
ceriond953ebb2005-11-29 13:27:20 +00005191 IRTemp EA = newTemp(ty);
5192 IRTemp addr = newTemp(ty);
5193 DIP("icbi r%u,r%u\n", rA_addr, rB_addr);
5194 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
sewardj7ce9d152005-03-15 16:54:13 +00005195
cerion2831b002005-11-30 19:55:22 +00005196 /* Round EA down to the start of the containing block. */
5197 assign( addr, binop( mkSzOp(ty, Iop_And8),
ceriond953ebb2005-11-29 13:27:20 +00005198 mkexpr(EA),
cerion2831b002005-11-30 19:55:22 +00005199 mkSzImm(ty, ~(((ULong)lineszB)-1) )) );
ceriond953ebb2005-11-29 13:27:20 +00005200 putGST( PPC_GST_TISTART, mkexpr(addr) );
cerion2831b002005-11-30 19:55:22 +00005201 putGST( PPC_GST_TILEN, mkSzImm(ty, lineszB) );
sewardj7ce9d152005-03-15 16:54:13 +00005202
sewardja8078f62005-03-15 18:27:40 +00005203 /* be paranoid ... */
5204 stmt( IRStmt_MFence() );
5205
sewardj7ce9d152005-03-15 16:54:13 +00005206 irbb->jumpkind = Ijk_TInval;
cerion2831b002005-11-30 19:55:22 +00005207 irbb->next = mkSzImm(ty, nextInsnAddr());
sewardj9e6491a2005-07-02 19:24:10 +00005208 dres->whatNext = Dis_StopHere;
cerionb85e8bb2005-02-16 08:54:33 +00005209 break;
sewardj7ce9d152005-03-15 16:54:13 +00005210 }
cerion8c3adda2005-01-31 11:54:05 +00005211
cerionb85e8bb2005-02-16 08:54:33 +00005212 default:
cerion5b2325f2005-12-23 00:55:09 +00005213 vex_printf("dis_cache_manage(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00005214 return False;
5215 }
5216 return True;
cerion8c3adda2005-01-31 11:54:05 +00005217}
5218
5219
sewardje14bb9f2005-07-22 09:39:02 +00005220/*------------------------------------------------------------*/
5221/*--- Floating Point Helpers ---*/
5222/*------------------------------------------------------------*/
5223
sewardje14bb9f2005-07-22 09:39:02 +00005224/* --------- Synthesise a 2-bit FPU rounding mode. --------- */
5225/* Produces a value in 0 .. 3, which is encoded as per the type
cerion5b2325f2005-12-23 00:55:09 +00005226 IRRoundingMode. PPCRoundingMode encoding is different to
sewardje14bb9f2005-07-22 09:39:02 +00005227 IRRoundingMode, so need to map it.
5228*/
5229static IRExpr* /* :: Ity_I32 */ get_roundingmode ( void )
5230{
5231/*
5232 rounding mode | PPC | IR
5233 ------------------------
5234 to nearest | 00 | 00
5235 to zero | 01 | 11
5236 to +infinity | 10 | 10
5237 to -infinity | 11 | 01
5238*/
5239 IRTemp rm_PPC32 = newTemp(Ity_I32);
ceriond953ebb2005-11-29 13:27:20 +00005240 assign( rm_PPC32, getGST_masked( PPC_GST_FPSCR, MASK_FPSCR_RN ) );
sewardje14bb9f2005-07-22 09:39:02 +00005241
5242 // rm_IR = XOR( rm_PPC32, (rm_PPC32 << 1) & 2)
5243 return binop(Iop_Xor32, mkexpr(rm_PPC32),
5244 binop(Iop_And32, mkU32(2),
5245 binop(Iop_Shl32, mkexpr(rm_PPC32), mkU8(1))));
5246}
5247
5248/* Round float to single precision
5249 - returns type Ity_F64 */
5250static IRExpr* roundToSgl ( IRExpr* src )
5251{
cerion5b2325f2005-12-23 00:55:09 +00005252 return unop(Iop_F32toF64,
5253 binop(Iop_F64toF32, get_roundingmode(), src));
sewardje14bb9f2005-07-22 09:39:02 +00005254}
cerion094d1392005-06-20 13:45:57 +00005255
5256
cerion3d870a32005-03-18 12:23:33 +00005257/*------------------------------------------------------------*/
5258/*--- Floating Point Instruction Translation ---*/
5259/*------------------------------------------------------------*/
5260
5261/*
5262 Floating Point Load Instructions
5263*/
5264static Bool dis_fp_load ( UInt theInstr )
5265{
cerion76de5cf2005-11-18 18:25:12 +00005266 /* X-Form, D-Form */
5267 UChar opc1 = ifieldOPC(theInstr);
5268 UChar frD_addr = ifieldRegDS(theInstr);
5269 UChar rA_addr = ifieldRegA(theInstr);
5270 UChar rB_addr = ifieldRegB(theInstr);
5271 UInt opc2 = ifieldOPClo10(theInstr);
5272 UChar b0 = ifieldBIT0(theInstr);
cerion2831b002005-11-30 19:55:22 +00005273 UInt uimm16 = ifieldUIMM16(theInstr);
cerion094d1392005-06-20 13:45:57 +00005274
cerion2831b002005-11-30 19:55:22 +00005275 Int simm16 = extend_s_16to32(uimm16);
5276 IRType ty = mode64 ? Ity_I64 : Ity_I32;
5277 IRTemp EA = newTemp(ty);
5278 IRTemp rA = newTemp(ty);
5279 IRTemp rB = newTemp(ty);
cerion094d1392005-06-20 13:45:57 +00005280
5281 assign( rA, getIReg(rA_addr) );
5282 assign( rB, getIReg(rB_addr) );
ceriond953ebb2005-11-29 13:27:20 +00005283
cerion3d870a32005-03-18 12:23:33 +00005284
5285 switch(opc1) {
sewardje14bb9f2005-07-22 09:39:02 +00005286 case 0x30: // lfs (Load Float Single, PPC32 p441)
ceriond953ebb2005-11-29 13:27:20 +00005287 DIP("lfs fr%u,%d(r%u)\n", frD_addr, simm16, rA_addr);
5288 assign( EA, ea_rAor0_simm(rA_addr, simm16) );
cerion5b2325f2005-12-23 00:55:09 +00005289 putFReg( frD_addr,
5290 unop(Iop_F32toF64, loadBE(Ity_F32, mkexpr(EA))) );
sewardje14bb9f2005-07-22 09:39:02 +00005291 break;
5292
cerion5b2325f2005-12-23 00:55:09 +00005293 case 0x31: // lfsu (Load Float Single, Update, PPC32 p442)
cerion729edb72005-12-02 16:03:46 +00005294 if (rA_addr == 0) {
cerion5b2325f2005-12-23 00:55:09 +00005295 vex_printf("dis_fp_load(ppc)(instr,lfsu)\n");
cerion729edb72005-12-02 16:03:46 +00005296 return False;
5297 }
5298 DIP("lfsu fr%u,%d(r%u)\n", frD_addr, simm16, rA_addr);
5299 assign( EA, ea_rA_simm(rA_addr, simm16) );
cerion5b2325f2005-12-23 00:55:09 +00005300 putFReg( frD_addr,
5301 unop(Iop_F32toF64, loadBE(Ity_F32, mkexpr(EA))) );
cerion729edb72005-12-02 16:03:46 +00005302 putIReg( rA_addr, mkexpr(EA) );
5303 break;
cerion094d1392005-06-20 13:45:57 +00005304
5305 case 0x32: // lfd (Load Float Double, PPC32 p437)
ceriond953ebb2005-11-29 13:27:20 +00005306 DIP("lfd fr%u,%d(r%u)\n", frD_addr, simm16, rA_addr);
5307 assign( EA, ea_rAor0_simm(rA_addr, simm16) );
cerion094d1392005-06-20 13:45:57 +00005308 putFReg( frD_addr, loadBE(Ity_F64, mkexpr(EA)) );
5309 break;
cerion3d870a32005-03-18 12:23:33 +00005310
cerion5b2325f2005-12-23 00:55:09 +00005311 case 0x33: // lfdu (Load Float Double, Update, PPC32 p438)
sewardj0e2cc672005-07-29 21:58:51 +00005312 if (rA_addr == 0) {
cerion5b2325f2005-12-23 00:55:09 +00005313 vex_printf("dis_fp_load(ppc)(instr,lfdu)\n");
sewardj0e2cc672005-07-29 21:58:51 +00005314 return False;
5315 }
ceriond953ebb2005-11-29 13:27:20 +00005316 DIP("lfdu fr%u,%d(r%u)\n", frD_addr, simm16, rA_addr);
5317 assign( EA, ea_rA_simm(rA_addr, simm16) );
sewardj0e2cc672005-07-29 21:58:51 +00005318 putFReg( frD_addr, loadBE(Ity_F64, mkexpr(EA)) );
5319 putIReg( rA_addr, mkexpr(EA) );
5320 break;
sewardje14bb9f2005-07-22 09:39:02 +00005321
5322 case 0x1F:
5323 if (b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00005324 vex_printf("dis_fp_load(ppc)(instr,b0)\n");
sewardje14bb9f2005-07-22 09:39:02 +00005325 return False;
5326 }
5327
5328 switch(opc2) {
ceriond953ebb2005-11-29 13:27:20 +00005329 case 0x217: // lfsx (Load Float Single Indexed, PPC32 p444)
5330 DIP("lfsx fr%u,r%u,r%u\n", frD_addr, rA_addr, rB_addr);
5331 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
5332 putFReg( frD_addr, unop( Iop_F32toF64,
5333 loadBE(Ity_F32, mkexpr(EA))) );
5334 break;
5335
cerion5b2325f2005-12-23 00:55:09 +00005336 case 0x237: // lfsux (Load Float Single, Update Indxd, PPC32 p443)
ceriond953ebb2005-11-29 13:27:20 +00005337 if (rA_addr == 0) {
cerion5b2325f2005-12-23 00:55:09 +00005338 vex_printf("dis_fp_load(ppc)(instr,lfsux)\n");
sewardje14bb9f2005-07-22 09:39:02 +00005339 return False;
ceriond953ebb2005-11-29 13:27:20 +00005340 }
5341 DIP("lfsux fr%u,r%u,r%u\n", frD_addr, rA_addr, rB_addr);
5342 assign( EA, ea_rA_idxd(rA_addr, rB_addr) );
cerion5b2325f2005-12-23 00:55:09 +00005343 putFReg( frD_addr,
5344 unop(Iop_F32toF64, loadBE(Ity_F32, mkexpr(EA))) );
ceriond953ebb2005-11-29 13:27:20 +00005345 putIReg( rA_addr, mkexpr(EA) );
5346 break;
5347
5348 case 0x257: // lfdx (Load Float Double Indexed, PPC32 p440)
5349 DIP("lfdx fr%u,r%u,r%u\n", frD_addr, rA_addr, rB_addr);
5350 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
5351 putFReg( frD_addr, loadBE(Ity_F64, mkexpr(EA)) );
5352 break;
5353
cerion5b2325f2005-12-23 00:55:09 +00005354 case 0x277: // lfdux (Load Float Double, Update Indxd, PPC32 p439)
ceriond953ebb2005-11-29 13:27:20 +00005355 if (rA_addr == 0) {
cerion5b2325f2005-12-23 00:55:09 +00005356 vex_printf("dis_fp_load(ppc)(instr,lfdux)\n");
ceriond953ebb2005-11-29 13:27:20 +00005357 return False;
5358 }
5359 DIP("lfdux fr%u,r%u,r%u\n", frD_addr, rA_addr, rB_addr);
5360 assign( EA, ea_rA_idxd(rA_addr, rB_addr) );
5361 putFReg( frD_addr, loadBE(Ity_F64, mkexpr(EA)) );
5362 putIReg( rA_addr, mkexpr(EA) );
5363 break;
5364
5365 default:
cerion5b2325f2005-12-23 00:55:09 +00005366 vex_printf("dis_fp_load(ppc)(opc2)\n");
ceriond953ebb2005-11-29 13:27:20 +00005367 return False;
sewardje14bb9f2005-07-22 09:39:02 +00005368 }
5369 break;
cerion3d870a32005-03-18 12:23:33 +00005370
5371 default:
cerion5b2325f2005-12-23 00:55:09 +00005372 vex_printf("dis_fp_load(ppc)(opc1)\n");
cerion3d870a32005-03-18 12:23:33 +00005373 return False;
5374 }
5375 return True;
5376}
5377
5378
5379
5380/*
5381 Floating Point Store Instructions
5382*/
5383static Bool dis_fp_store ( UInt theInstr )
5384{
cerion76de5cf2005-11-18 18:25:12 +00005385 /* X-Form, D-Form */
5386 UChar opc1 = ifieldOPC(theInstr);
5387 UChar frS_addr = ifieldRegDS(theInstr);
5388 UChar rA_addr = ifieldRegA(theInstr);
5389 UChar rB_addr = ifieldRegB(theInstr);
5390 UInt opc2 = ifieldOPClo10(theInstr);
5391 UChar b0 = ifieldBIT0(theInstr);
ceriond953ebb2005-11-29 13:27:20 +00005392 Int uimm16 = ifieldUIMM16(theInstr);
cerion094d1392005-06-20 13:45:57 +00005393
cerion2831b002005-11-30 19:55:22 +00005394 Int simm16 = extend_s_16to32(uimm16);
5395 IRTemp frS = newTemp(Ity_F64);
5396 IRType ty = mode64 ? Ity_I64 : Ity_I32;
5397 IRTemp EA = newTemp(ty);
5398 IRTemp rA = newTemp(ty);
5399 IRTemp rB = newTemp(ty);
cerion094d1392005-06-20 13:45:57 +00005400
5401 assign( frS, getFReg(frS_addr) );
cerionedf7fc52005-11-18 20:57:41 +00005402 assign( rA, getIReg(rA_addr) );
5403 assign( rB, getIReg(rB_addr) );
cerion3d870a32005-03-18 12:23:33 +00005404
5405 switch(opc1) {
sewardje14bb9f2005-07-22 09:39:02 +00005406
5407 case 0x34: // stfs (Store Float Single, PPC32 p518)
ceriond953ebb2005-11-29 13:27:20 +00005408 DIP("stfs fr%u,%d(r%u)\n", frS_addr, simm16, rA_addr);
5409 assign( EA, ea_rAor0_simm(rA_addr, simm16) );
cerion876ef412005-12-14 22:00:53 +00005410 /* TODO
5411 This implementation ends up rounding twice, losing accuracy.
5412 - first via F64toF32, and then by the backend fp store (stfs)
5413 */
sewardje14bb9f2005-07-22 09:39:02 +00005414 storeBE( mkexpr(EA),
5415 binop(Iop_F64toF32, get_roundingmode(), mkexpr(frS)) );
5416 break;
5417
cerion5b2325f2005-12-23 00:55:09 +00005418 case 0x35: // stfsu (Store Float Single, Update, PPC32 p519)
cerion729edb72005-12-02 16:03:46 +00005419 if (rA_addr == 0) {
cerion5b2325f2005-12-23 00:55:09 +00005420 vex_printf("dis_fp_store(ppc)(instr,stfsu)\n");
cerion729edb72005-12-02 16:03:46 +00005421 return False;
5422 }
5423 DIP("stfsu fr%u,%d(r%u)\n", frS_addr, simm16, rA_addr);
5424 assign( EA, ea_rA_simm(rA_addr, simm16) );
cerion876ef412005-12-14 22:00:53 +00005425 /* This implementation loses accuracy - see note for stfs */
cerion729edb72005-12-02 16:03:46 +00005426 storeBE( mkexpr(EA),
5427 binop(Iop_F64toF32, get_roundingmode(), mkexpr(frS)) );
5428 putIReg( rA_addr, mkexpr(EA) );
5429 break;
cerion3d870a32005-03-18 12:23:33 +00005430
cerion094d1392005-06-20 13:45:57 +00005431 case 0x36: // stfd (Store Float Double, PPC32 p513)
ceriond953ebb2005-11-29 13:27:20 +00005432 DIP("stfd fr%u,%d(r%u)\n", frS_addr, simm16, rA_addr);
5433 assign( EA, ea_rAor0_simm(rA_addr, simm16) );
cerion094d1392005-06-20 13:45:57 +00005434 storeBE( mkexpr(EA), mkexpr(frS) );
5435 break;
cerion3d870a32005-03-18 12:23:33 +00005436
cerion5b2325f2005-12-23 00:55:09 +00005437 case 0x37: // stfdu (Store Float Double, Update, PPC32 p514)
sewardje14bb9f2005-07-22 09:39:02 +00005438 if (rA_addr == 0) {
cerion5b2325f2005-12-23 00:55:09 +00005439 vex_printf("dis_fp_store(ppc)(instr,stfdu)\n");
sewardje14bb9f2005-07-22 09:39:02 +00005440 return False;
5441 }
ceriond953ebb2005-11-29 13:27:20 +00005442 DIP("stfdu fr%u,%d(r%u)\n", frS_addr, simm16, rA_addr);
5443 assign( EA, ea_rA_simm(rA_addr, simm16) );
sewardje14bb9f2005-07-22 09:39:02 +00005444 storeBE( mkexpr(EA), mkexpr(frS) );
5445 putIReg( rA_addr, mkexpr(EA) );
5446 break;
5447
5448 case 0x1F:
5449 if (b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00005450 vex_printf("dis_fp_store(ppc)(instr,b0)\n");
sewardje14bb9f2005-07-22 09:39:02 +00005451 return False;
5452 }
5453
5454 switch(opc2) {
ceriond953ebb2005-11-29 13:27:20 +00005455 case 0x297: // stfsx (Store Float Single Indexed, PPC32 p521)
5456 DIP("stfsx fr%u,r%u,r%u\n", frS_addr, rA_addr, rB_addr);
5457 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
cerion876ef412005-12-14 22:00:53 +00005458 /* This implementation loses accuracy - see note for stfs */
cerion5b2325f2005-12-23 00:55:09 +00005459 storeBE( mkexpr(EA), binop(Iop_F64toF32,
5460 get_roundingmode(), mkexpr(frS)) );
ceriond953ebb2005-11-29 13:27:20 +00005461 break;
5462
cerion5b2325f2005-12-23 00:55:09 +00005463 case 0x2B7: // stfsux (Store Float Sgl, Update Indxd, PPC32 p520)
cerion729edb72005-12-02 16:03:46 +00005464 if (rA_addr == 0) {
cerion5b2325f2005-12-23 00:55:09 +00005465 vex_printf("dis_fp_store(ppc)(instr,stfsux)\n");
cerion729edb72005-12-02 16:03:46 +00005466 return False;
5467 }
5468 DIP("stfsux fr%u,r%u,r%u\n", frS_addr, rA_addr, rB_addr);
5469 assign( EA, ea_rA_idxd(rA_addr, rB_addr) );
cerion876ef412005-12-14 22:00:53 +00005470 /* This implementation loses accuracy - see note for stfs */
cerion5b2325f2005-12-23 00:55:09 +00005471 storeBE( mkexpr(EA), binop(Iop_F64toF32,
5472 get_roundingmode(), mkexpr(frS)) );
cerion729edb72005-12-02 16:03:46 +00005473 putIReg( rA_addr, mkexpr(EA) );
5474 break;
sewardje14bb9f2005-07-22 09:39:02 +00005475
ceriond953ebb2005-11-29 13:27:20 +00005476 case 0x2D7: // stfdx (Store Float Double Indexed, PPC32 p516)
5477 DIP("stfdx fr%u,r%u,r%u\n", frS_addr, rA_addr, rB_addr);
5478 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
5479 storeBE( mkexpr(EA), mkexpr(frS) );
5480 break;
sewardje14bb9f2005-07-22 09:39:02 +00005481
cerion5b2325f2005-12-23 00:55:09 +00005482 case 0x2F7: // stfdux (Store Float Dbl, Update Indxd, PPC32 p515)
ceriond953ebb2005-11-29 13:27:20 +00005483 if (rA_addr == 0) {
cerion5b2325f2005-12-23 00:55:09 +00005484 vex_printf("dis_fp_store(ppc)(instr,stfdux)\n");
ceriond953ebb2005-11-29 13:27:20 +00005485 return False;
5486 }
5487 DIP("stfdux fr%u,r%u,r%u\n", frS_addr, rA_addr, rB_addr);
5488 assign( EA, ea_rA_idxd(rA_addr, rB_addr) );
5489 storeBE( mkexpr(EA), mkexpr(frS) );
5490 putIReg( rA_addr, mkexpr(EA) );
5491 break;
sewardj5f63c0c2005-09-09 10:36:55 +00005492
cerion2831b002005-11-30 19:55:22 +00005493//zz case 0x3D7: // stfiwx (Store Float as Int, Indexed, PPC32 p517)
5494//zz DIP("stfiwx fr%u,r%u,r%u\n", frS_addr, rA_addr, rB_addr);
5495//zz assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
5496//zz storeBE( mkexpr(EA),
5497//zz unop(Iop_64to32, unop(Iop_ReinterpF64asI64, mkexpr(frS))) );
5498//zz break;
sewardje14bb9f2005-07-22 09:39:02 +00005499
ceriond953ebb2005-11-29 13:27:20 +00005500 default:
cerion5b2325f2005-12-23 00:55:09 +00005501 vex_printf("dis_fp_store(ppc)(opc2)\n");
ceriond953ebb2005-11-29 13:27:20 +00005502 return False;
sewardje14bb9f2005-07-22 09:39:02 +00005503 }
5504 break;
cerion3d870a32005-03-18 12:23:33 +00005505
5506 default:
cerion5b2325f2005-12-23 00:55:09 +00005507 vex_printf("dis_fp_store(ppc)(opc1)\n");
cerion3d870a32005-03-18 12:23:33 +00005508 return False;
5509 }
5510 return True;
5511}
5512
5513
5514
5515/*
5516 Floating Point Arith Instructions
5517*/
5518static Bool dis_fp_arith ( UInt theInstr )
5519{
5520 /* A-Form */
cerion76de5cf2005-11-18 18:25:12 +00005521 UChar opc1 = ifieldOPC(theInstr);
5522 UChar frD_addr = ifieldRegDS(theInstr);
5523 UChar frA_addr = ifieldRegA(theInstr);
5524 UChar frB_addr = ifieldRegB(theInstr);
5525 UChar frC_addr = ifieldRegC(theInstr);
5526 UChar opc2 = ifieldOPClo5(theInstr);
5527 UChar flag_rC = ifieldBIT0(theInstr);
5528 // Note: flag_rC ignored as fp exceptions not supported.
cerion094d1392005-06-20 13:45:57 +00005529
5530 IRTemp frD = newTemp(Ity_F64);
5531 IRTemp frA = newTemp(Ity_F64);
5532 IRTemp frB = newTemp(Ity_F64);
5533 IRTemp frC = newTemp(Ity_F64);
5534
5535 assign( frA, getFReg(frA_addr));
5536 assign( frB, getFReg(frB_addr));
5537 assign( frC, getFReg(frC_addr));
cerion3d870a32005-03-18 12:23:33 +00005538
5539 switch (opc1) {
sewardje14bb9f2005-07-22 09:39:02 +00005540 case 0x3B:
5541 switch (opc2) {
5542 case 0x12: // fdivs (Floating Divide Single, PPC32 p407)
5543 if (frC_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00005544 vex_printf("dis_fp_arith(ppc)(instr,fdivs)\n");
sewardje14bb9f2005-07-22 09:39:02 +00005545 return False;
5546 }
cerion5b2325f2005-12-23 00:55:09 +00005547 DIP("fdivs%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00005548 frD_addr, frA_addr, frB_addr);
cerion5b2325f2005-12-23 00:55:09 +00005549 assign( frD, roundToSgl( binop(Iop_DivF64,
5550 mkexpr(frA), mkexpr(frB)) ));
sewardje14bb9f2005-07-22 09:39:02 +00005551 break;
5552
5553 case 0x14: // fsubs (Floating Subtract Single, PPC32 p430)
5554 if (frC_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00005555 vex_printf("dis_fp_arith(ppc)(instr,fsubs)\n");
sewardje14bb9f2005-07-22 09:39:02 +00005556 return False;
5557 }
cerion5b2325f2005-12-23 00:55:09 +00005558 DIP("fsubs%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00005559 frD_addr, frA_addr, frB_addr);
5560 assign( frD, roundToSgl(
5561 binop(Iop_SubF64, mkexpr(frA), mkexpr(frB)) ));
5562 break;
5563
5564 case 0x15: // fadds (Floating Add Single, PPC32 p401)
5565 if (frC_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00005566 vex_printf("dis_fp_arith(ppc)(instr,fadds)\n");
sewardje14bb9f2005-07-22 09:39:02 +00005567 return False;
5568 }
cerion5b2325f2005-12-23 00:55:09 +00005569 DIP("fadds%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00005570 frD_addr, frA_addr, frB_addr);
5571 assign( frD, roundToSgl(
5572 binop(Iop_AddF64, mkexpr(frA), mkexpr(frB)) ));
5573 break;
5574
sewardjb51f0f42005-07-18 11:38:02 +00005575//zz case 0x16: // fsqrts (Floating SqRt (Single-Precision), PPC32 p428)
5576//zz if (frA_addr != 0 || frC_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00005577//zz vex_printf("dis_fp_arith(ppc)(instr,fsqrts)\n");
sewardjb51f0f42005-07-18 11:38:02 +00005578//zz return False;
5579//zz }
cerion5b2325f2005-12-23 00:55:09 +00005580//zz DIP("fsqrts%s fr%u,fr%u\n", flag_rC ? ".":"",
sewardjb51f0f42005-07-18 11:38:02 +00005581//zz frD_addr, frB_addr);
5582//zz assign( frD, roundToSgl( unop(Iop_SqrtF64, mkexpr(frB)) ));
5583//zz break;
sewardje14bb9f2005-07-22 09:39:02 +00005584
sewardjb51f0f42005-07-18 11:38:02 +00005585//zz case 0x18: // fres (Floating Reciprocal Estimate Single, PPC32 p421)
5586//zz if (frA_addr != 0 || frC_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00005587//zz vex_printf("dis_fp_arith(ppc)(instr,fres)\n");
sewardjb51f0f42005-07-18 11:38:02 +00005588//zz return False;
5589//zz }
cerion5b2325f2005-12-23 00:55:09 +00005590//zz DIP("fres%s fr%u,fr%u\n", flag_rC ? ".":"",
sewardjb51f0f42005-07-18 11:38:02 +00005591//zz frD_addr, frB_addr);
5592//zz DIP(" => not implemented\n");
5593//zz // CAB: Can we use one of the 128 bit SIMD Iop_Recip32F ops?
5594//zz return False;
sewardje14bb9f2005-07-22 09:39:02 +00005595
5596 case 0x19: // fmuls (Floating Multiply Single, PPC32 p414)
5597 if (frB_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00005598 vex_printf("dis_fp_arith(ppc)(instr,fmuls)\n");
sewardje14bb9f2005-07-22 09:39:02 +00005599 return False;
5600 }
cerion5b2325f2005-12-23 00:55:09 +00005601 DIP("fmuls%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00005602 frD_addr, frA_addr, frC_addr);
cerion5b2325f2005-12-23 00:55:09 +00005603 assign( frD, roundToSgl( binop(Iop_MulF64,
5604 mkexpr(frA), mkexpr(frC)) ));
sewardje14bb9f2005-07-22 09:39:02 +00005605 break;
5606
5607 default:
cerion5b2325f2005-12-23 00:55:09 +00005608 vex_printf("dis_fp_arith(ppc)(3B: opc2)\n");
sewardje14bb9f2005-07-22 09:39:02 +00005609 return False;
5610 }
5611 break;
cerion094d1392005-06-20 13:45:57 +00005612
cerion3d870a32005-03-18 12:23:33 +00005613 case 0x3F:
5614 switch (opc2) {
cerion5b2325f2005-12-23 00:55:09 +00005615 case 0x12: // fdiv (Floating Div (Double-Precision), PPC32 p406)
sewardje14bb9f2005-07-22 09:39:02 +00005616 if (frC_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00005617 vex_printf("dis_fp_arith(ppc)(instr,fdiv)\n");
sewardje14bb9f2005-07-22 09:39:02 +00005618 return False;
5619 }
cerion5b2325f2005-12-23 00:55:09 +00005620 DIP("fdiv%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00005621 frD_addr, frA_addr, frB_addr);
5622 assign( frD, binop( Iop_DivF64, mkexpr(frA), mkexpr(frB) ) );
5623 break;
5624
cerion5b2325f2005-12-23 00:55:09 +00005625 case 0x14: // fsub (Floating Sub (Double-Precision), PPC32 p429)
sewardje14bb9f2005-07-22 09:39:02 +00005626 if (frC_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00005627 vex_printf("dis_fp_arith(ppc)(instr,fsub)\n");
sewardje14bb9f2005-07-22 09:39:02 +00005628 return False;
5629 }
cerion5b2325f2005-12-23 00:55:09 +00005630 DIP("fsub%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00005631 frD_addr, frA_addr, frB_addr);
5632 assign( frD, binop( Iop_SubF64, mkexpr(frA), mkexpr(frB) ) );
5633 break;
cerion3d870a32005-03-18 12:23:33 +00005634
5635 case 0x15: // fadd (Floating Add (Double-Precision), PPC32 p400)
5636 if (frC_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00005637 vex_printf("dis_fp_arith(ppc)(instr,fadd)\n");
cerion3d870a32005-03-18 12:23:33 +00005638 return False;
5639 }
cerion5b2325f2005-12-23 00:55:09 +00005640 DIP("fadd%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
cerion3d870a32005-03-18 12:23:33 +00005641 frD_addr, frA_addr, frB_addr);
cerion094d1392005-06-20 13:45:57 +00005642 assign( frD, binop( Iop_AddF64, mkexpr(frA), mkexpr(frB) ) );
5643 break;
cerion3d870a32005-03-18 12:23:33 +00005644
cerion876ef412005-12-14 22:00:53 +00005645 case 0x16: // fsqrt (Floating SqRt (Double-Precision), PPC32 p427)
5646 if (frA_addr != 0 || frC_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00005647 vex_printf("dis_fp_arith(ppc)(instr,fsqrt)\n");
cerion876ef412005-12-14 22:00:53 +00005648 return False;
5649 }
cerion5b2325f2005-12-23 00:55:09 +00005650 DIP("fsqrt%s fr%u,fr%u\n", flag_rC ? ".":"",
cerion876ef412005-12-14 22:00:53 +00005651 frD_addr, frB_addr);
5652 assign( frD, unop( Iop_SqrtF64, mkexpr(frB) ) );
5653 break;
sewardje14bb9f2005-07-22 09:39:02 +00005654
5655 case 0x17: { // fsel (Floating Select, PPC32 p426)
5656 IRTemp cc = newTemp(Ity_I32);
5657 IRTemp cc_b0 = newTemp(Ity_I32);
5658
cerion5b2325f2005-12-23 00:55:09 +00005659 DIP("fsel%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00005660 frD_addr, frA_addr, frC_addr, frB_addr);
5661
5662 // cc: UN == 0x41, LT == 0x01, GT == 0x00, EQ == 0x40
5663 // => GT|EQ == (cc & 0x1 == 0)
cerion5b2325f2005-12-23 00:55:09 +00005664 assign( cc, binop(Iop_CmpF64, mkexpr(frA),
5665 IRExpr_Const(IRConst_F64(0))) );
sewardje14bb9f2005-07-22 09:39:02 +00005666 assign( cc_b0, binop(Iop_And32, mkexpr(cc), mkU32(1)) );
5667
5668 // frD = (frA >= 0.0) ? frC : frB
5669 // = (cc_b0 == 0) ? frC : frB
5670 assign( frD,
5671 IRExpr_Mux0X(
5672 unop(Iop_1Uto8,
5673 binop(Iop_CmpEQ32, mkexpr(cc_b0), mkU32(0))),
5674 mkexpr(frB),
5675 mkexpr(frC) ));
5676 break;
5677 }
5678
cerion5b2325f2005-12-23 00:55:09 +00005679 case 0x19: // fmul (Floating Mult (Double Precision), PPC32 p413)
sewardje14bb9f2005-07-22 09:39:02 +00005680 if (frB_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00005681 vex_printf("dis_fp_arith(ppc)(instr,fmul)\n");
sewardje14bb9f2005-07-22 09:39:02 +00005682 return False;
5683 }
cerion5b2325f2005-12-23 00:55:09 +00005684 DIP("fmul%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00005685 frD_addr, frA_addr, frC_addr);
5686 assign( frD, binop( Iop_MulF64, mkexpr(frA), mkexpr(frC) ) );
5687 break;
5688
cerion5b2325f2005-12-23 00:55:09 +00005689//zz case 0x1A: // frsqrte (Floating Recip SqRt Est., PPC32 p424)
sewardjb51f0f42005-07-18 11:38:02 +00005690//zz if (frA_addr != 0 || frC_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00005691//zz vex_printf("dis_fp_arith(ppc)(instr,frsqrte)\n");
sewardjb51f0f42005-07-18 11:38:02 +00005692//zz return False;
5693//zz }
cerion5b2325f2005-12-23 00:55:09 +00005694//zz DIP("frsqrte%s fr%u,fr%u\n", flag_rC ? ".":"",
sewardjb51f0f42005-07-18 11:38:02 +00005695//zz frD_addr, frB_addr);
5696//zz DIP(" => not implemented\n");
5697//zz // CAB: Iop_SqrtF64, then one of the 128 bit SIMD Iop_Recip32F ops?
5698//zz return False;
cerion3d870a32005-03-18 12:23:33 +00005699
5700 default:
cerion5b2325f2005-12-23 00:55:09 +00005701 vex_printf("dis_fp_arith(ppc)(3F: opc2)\n");
cerion3d870a32005-03-18 12:23:33 +00005702 return False;
5703 }
cerion094d1392005-06-20 13:45:57 +00005704 break;
5705
cerion3d870a32005-03-18 12:23:33 +00005706 default:
cerion5b2325f2005-12-23 00:55:09 +00005707 vex_printf("dis_fp_arith(ppc)(opc1)\n");
cerion3d870a32005-03-18 12:23:33 +00005708 return False;
5709 }
cerion094d1392005-06-20 13:45:57 +00005710
5711 putFReg( frD_addr, mkexpr(frD) );
cerion3d870a32005-03-18 12:23:33 +00005712 return True;
5713}
5714
5715
5716
sewardje14bb9f2005-07-22 09:39:02 +00005717/*
5718 Floating Point Mult-Add Instructions
5719*/
5720static Bool dis_fp_multadd ( UInt theInstr )
5721{
5722 /* A-Form */
cerion76de5cf2005-11-18 18:25:12 +00005723 UChar opc1 = ifieldOPC(theInstr);
5724 UChar frD_addr = ifieldRegDS(theInstr);
5725 UChar frA_addr = ifieldRegA(theInstr);
5726 UChar frB_addr = ifieldRegB(theInstr);
5727 UChar frC_addr = ifieldRegC(theInstr);
5728 UChar opc2 = ifieldOPClo5(theInstr);
5729 UChar flag_rC = ifieldBIT0(theInstr);
sewardje14bb9f2005-07-22 09:39:02 +00005730
5731 IRTemp frD = newTemp(Ity_F64);
5732 IRTemp frA = newTemp(Ity_F64);
5733 IRTemp frB = newTemp(Ity_F64);
5734 IRTemp frC = newTemp(Ity_F64);
5735
5736 assign( frA, getFReg(frA_addr));
5737 assign( frB, getFReg(frB_addr));
5738 assign( frC, getFReg(frC_addr));
5739
5740 switch (opc1) {
5741 case 0x3B:
5742 switch (opc2) {
5743 case 0x1C: // fmsubs (Floating Mult-Subtr Single, PPC32 p412)
cerion5b2325f2005-12-23 00:55:09 +00005744 DIP("fmsubs%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00005745 frD_addr, frA_addr, frC_addr, frB_addr);
cerion5b2325f2005-12-23 00:55:09 +00005746 assign( frD, roundToSgl( binop( Iop_SubF64,
5747 binop(Iop_MulF64, mkexpr(frA),
5748 mkexpr(frC)),
5749 mkexpr(frB)) ));
sewardje14bb9f2005-07-22 09:39:02 +00005750 break;
5751
5752 case 0x1D: // fmadds (Floating Mult-Add Single, PPC32 p409)
cerion5b2325f2005-12-23 00:55:09 +00005753 DIP("fmadds%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00005754 frD_addr, frA_addr, frC_addr, frB_addr);
cerion5b2325f2005-12-23 00:55:09 +00005755 assign( frD, roundToSgl( binop( Iop_AddF64,
5756 binop(Iop_MulF64, mkexpr(frA),
5757 mkexpr(frC)),
5758 mkexpr(frB)) ));
sewardje14bb9f2005-07-22 09:39:02 +00005759 break;
5760
5761 case 0x1E: // fnmsubs (Float Neg Mult-Subtr Single, PPC32 p420)
cerion5b2325f2005-12-23 00:55:09 +00005762 DIP("fnmsubs%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00005763 frD_addr, frA_addr, frC_addr, frB_addr);
5764 assign( frD, roundToSgl(
cerion5b2325f2005-12-23 00:55:09 +00005765 unop(Iop_NegF64,
5766 binop(Iop_SubF64,
5767 binop(Iop_MulF64, mkexpr(frA),
5768 mkexpr(frC)),
5769 mkexpr(frB))) ));
sewardje14bb9f2005-07-22 09:39:02 +00005770 break;
5771
5772 case 0x1F: // fnmadds (Floating Negative Multiply-Add Single, PPC32 p418)
cerion5b2325f2005-12-23 00:55:09 +00005773 DIP("fnmadds%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00005774 frD_addr, frA_addr, frC_addr, frB_addr);
5775 assign( frD, roundToSgl(
cerion5b2325f2005-12-23 00:55:09 +00005776 unop(Iop_NegF64,
5777 binop(Iop_AddF64,
5778 binop(Iop_MulF64, mkexpr(frA),
5779 mkexpr(frC)),
5780 mkexpr(frB))) ));
sewardje14bb9f2005-07-22 09:39:02 +00005781 break;
5782
5783 default:
cerion5b2325f2005-12-23 00:55:09 +00005784 vex_printf("dis_fp_multadd(ppc)(3B: opc2)\n");
sewardje14bb9f2005-07-22 09:39:02 +00005785 return False;
5786 }
5787 break;
5788
5789 case 0x3F:
5790 switch (opc2) {
cerion5b2325f2005-12-23 00:55:09 +00005791 case 0x1C: // fmsub (Float Mult-Sub (Dbl Precision), PPC32 p411)
5792 DIP("fmsub%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00005793 frD_addr, frA_addr, frC_addr, frB_addr);
5794 assign( frD, binop( Iop_SubF64,
cerion5b2325f2005-12-23 00:55:09 +00005795 binop( Iop_MulF64, mkexpr(frA),
5796 mkexpr(frC) ),
sewardje14bb9f2005-07-22 09:39:02 +00005797 mkexpr(frB) ));
5798 break;
5799
cerion5b2325f2005-12-23 00:55:09 +00005800 case 0x1D: // fmadd (Float Mult-Add (Dbl Precision), PPC32 p408)
5801 DIP("fmadd%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00005802 frD_addr, frA_addr, frC_addr, frB_addr);
5803 assign( frD, binop( Iop_AddF64,
cerion5b2325f2005-12-23 00:55:09 +00005804 binop( Iop_MulF64, mkexpr(frA),
5805 mkexpr(frC) ),
sewardje14bb9f2005-07-22 09:39:02 +00005806 mkexpr(frB) ));
5807 break;
5808
cerion5b2325f2005-12-23 00:55:09 +00005809 case 0x1E: // fnmsub (Float Neg Mult-Subtr (Dbl Precision), PPC32 p419)
5810 DIP("fnmsub%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardj0e2cc672005-07-29 21:58:51 +00005811 frD_addr, frA_addr, frC_addr, frB_addr);
5812 assign( frD, unop( Iop_NegF64,
5813 binop( Iop_SubF64,
cerion5b2325f2005-12-23 00:55:09 +00005814 binop( Iop_MulF64, mkexpr(frA),
5815 mkexpr(frC) ),
sewardj0e2cc672005-07-29 21:58:51 +00005816 mkexpr(frB) )));
5817 break;
5818
cerion5b2325f2005-12-23 00:55:09 +00005819 case 0x1F: // fnmadd (Float Neg Mult-Add (Dbl Precision), PPC32 p417)
5820 DIP("fnmadd%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardj0e2cc672005-07-29 21:58:51 +00005821 frD_addr, frA_addr, frC_addr, frB_addr);
5822 assign( frD, unop( Iop_NegF64,
5823 binop( Iop_AddF64,
cerion5b2325f2005-12-23 00:55:09 +00005824 binop( Iop_MulF64, mkexpr(frA),
5825 mkexpr(frC) ),
sewardj0e2cc672005-07-29 21:58:51 +00005826 mkexpr(frB) )));
5827 break;
sewardje14bb9f2005-07-22 09:39:02 +00005828
5829 default:
cerion5b2325f2005-12-23 00:55:09 +00005830 vex_printf("dis_fp_multadd(ppc)(3F: opc2)\n");
sewardje14bb9f2005-07-22 09:39:02 +00005831 return False;
5832 }
5833 break;
5834
5835 default:
cerion5b2325f2005-12-23 00:55:09 +00005836 vex_printf("dis_fp_multadd(ppc)(opc1)\n");
sewardje14bb9f2005-07-22 09:39:02 +00005837 return False;
5838 }
5839
5840 putFReg( frD_addr, mkexpr(frD) );
5841 return True;
5842}
5843
5844
5845
5846/*
5847 Floating Point Compare Instructions
5848*/
5849static Bool dis_fp_cmp ( UInt theInstr )
5850{
5851 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00005852 UChar opc1 = ifieldOPC(theInstr);
5853 UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) );
5854 UChar b21to22 = toUChar( IFIELD( theInstr, 21, 2 ) );
5855 UChar frA_addr = ifieldRegA(theInstr);
5856 UChar frB_addr = ifieldRegB(theInstr);
5857 UInt opc2 = ifieldOPClo10(theInstr);
5858 UChar b0 = ifieldBIT0(theInstr);
sewardje14bb9f2005-07-22 09:39:02 +00005859
5860 IRTemp ccIR = newTemp(Ity_I32);
5861 IRTemp ccPPC32 = newTemp(Ity_I32);
5862
sewardje14bb9f2005-07-22 09:39:02 +00005863 IRTemp frA = newTemp(Ity_F64);
5864 IRTemp frB = newTemp(Ity_F64);
sewardje14bb9f2005-07-22 09:39:02 +00005865
5866 if (opc1 != 0x3F || b21to22 != 0 || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00005867 vex_printf("dis_fp_cmp(ppc)(instr)\n");
sewardje14bb9f2005-07-22 09:39:02 +00005868 return False;
5869 }
5870
5871 assign( frA, getFReg(frA_addr));
5872 assign( frB, getFReg(frB_addr));
5873
5874 assign( ccIR, binop(Iop_CmpF64, mkexpr(frA), mkexpr(frB)) );
5875
5876 /* Map compare result from IR to PPC32 */
5877 /*
5878 FP cmp result | PPC | IR
5879 --------------------------
5880 UN | 0x1 | 0x45
5881 EQ | 0x2 | 0x40
5882 GT | 0x4 | 0x00
5883 LT | 0x8 | 0x01
5884 */
5885
5886 // ccPPC32 = Shl(1, (0x2 & ~(ccIR>>5)) || (0x1 & (XOR(ccIR, ccIR>>6))))
5887 assign(
5888 ccPPC32,
5889 binop(Iop_Shl32, mkU32(1),
5890 unop(Iop_32to8,
5891 binop(Iop_Or32,
5892 binop(Iop_And32, mkU32(2),
5893 unop(Iop_Not32,
5894 binop(Iop_Shr32, mkexpr(ccIR), mkU8(5)))),
5895 binop(Iop_And32, mkU32(1),
5896 binop(Iop_Xor32, mkexpr(ccIR),
5897 binop(Iop_Shr32, mkexpr(ccIR), mkU8(6)))))))
5898 );
5899
ceriond953ebb2005-11-29 13:27:20 +00005900 putGST_field( PPC_GST_CR, mkexpr(ccPPC32), crfD );
sewardje14bb9f2005-07-22 09:39:02 +00005901
cerionedf7fc52005-11-18 20:57:41 +00005902 /* CAB: TODO?: Support writing cc to FPSCR->FPCC ?
ceriond953ebb2005-11-29 13:27:20 +00005903 putGST_field( PPC_GST_FPSCR, mkexpr(ccPPC32), 4 );
cerionedf7fc52005-11-18 20:57:41 +00005904 */
sewardje14bb9f2005-07-22 09:39:02 +00005905
cerionedf7fc52005-11-18 20:57:41 +00005906 /* Note: Differences between fcmpu and fcmpo are only in exception
5907 flag settings, which aren't supported anyway. */
sewardje14bb9f2005-07-22 09:39:02 +00005908 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00005909 case 0x000: // fcmpu (Floating Compare Unordered, PPC32 p403)
5910 DIP("fcmpu crf%d,fr%u,fr%u\n", crfD, frA_addr, frB_addr);
5911 break;
5912 case 0x020: // fcmpo (Floating Compare Ordered, PPC32 p402)
5913 DIP("fcmpo crf%d,fr%u,fr%u\n", crfD, frA_addr, frB_addr);
5914 break;
5915 default:
cerion5b2325f2005-12-23 00:55:09 +00005916 vex_printf("dis_fp_cmp(ppc)(opc2)\n");
ceriond953ebb2005-11-29 13:27:20 +00005917 return False;
sewardje14bb9f2005-07-22 09:39:02 +00005918 }
5919 return True;
5920}
5921
5922
5923
5924/*
5925 Floating Point Rounding/Conversion Instructions
5926*/
5927static Bool dis_fp_round ( UInt theInstr )
5928{
5929 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00005930 UChar opc1 = ifieldOPC(theInstr);
5931 UChar frD_addr = ifieldRegDS(theInstr);
5932 UChar b16to20 = ifieldRegA(theInstr);
5933 UChar frB_addr = ifieldRegB(theInstr);
5934 UInt opc2 = ifieldOPClo10(theInstr);
5935 UChar flag_rC = ifieldBIT0(theInstr);
sewardje14bb9f2005-07-22 09:39:02 +00005936
5937 IRTemp frD = newTemp(Ity_F64);
5938 IRTemp frB = newTemp(Ity_F64);
cerion07b07a92005-12-22 14:32:35 +00005939 IRTemp r_tmp32 = newTemp(Ity_I32);
5940 IRTemp r_tmp64 = newTemp(Ity_I64);
sewardje14bb9f2005-07-22 09:39:02 +00005941
5942 if (opc1 != 0x3F || b16to20 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00005943 vex_printf("dis_fp_round(ppc)(instr)\n");
sewardje14bb9f2005-07-22 09:39:02 +00005944 return False;
5945 }
5946
5947 assign( frB, getFReg(frB_addr));
5948
sewardje14bb9f2005-07-22 09:39:02 +00005949 switch (opc2) {
cerionf0de28c2005-12-13 20:21:11 +00005950 case 0x00C: // frsp (Float Round to Single, PPC32 p423)
cerion5b2325f2005-12-23 00:55:09 +00005951 DIP("frsp%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
ceriond953ebb2005-11-29 13:27:20 +00005952 assign( frD, roundToSgl( mkexpr(frB) ));
5953 break;
5954
cerionf0de28c2005-12-13 20:21:11 +00005955 case 0x00E: // fctiw (Float Conv to Int, PPC32 p404)
cerion5b2325f2005-12-23 00:55:09 +00005956 DIP("fctiw%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
5957 assign( r_tmp32,
5958 binop(Iop_F64toI32, get_roundingmode(), mkexpr(frB)) );
ceriond953ebb2005-11-29 13:27:20 +00005959 assign( frD, unop( Iop_ReinterpI64asF64,
cerion07b07a92005-12-22 14:32:35 +00005960 unop( Iop_32Uto64, mkexpr(r_tmp32))));
ceriond953ebb2005-11-29 13:27:20 +00005961 break;
5962
cerionf0de28c2005-12-13 20:21:11 +00005963 case 0x00F: // fctiwz (Float Conv to Int, Round to Zero, PPC32 p405)
cerion5b2325f2005-12-23 00:55:09 +00005964 DIP("fctiwz%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
cerion07b07a92005-12-22 14:32:35 +00005965 assign( r_tmp32, binop(Iop_F64toI32, mkU32(0x3), mkexpr(frB)) );
ceriond953ebb2005-11-29 13:27:20 +00005966 assign( frD, unop( Iop_ReinterpI64asF64,
cerion07b07a92005-12-22 14:32:35 +00005967 unop( Iop_32Uto64, mkexpr(r_tmp32))));
ceriond953ebb2005-11-29 13:27:20 +00005968 break;
cerionf0de28c2005-12-13 20:21:11 +00005969
5970
5971 /* 64bit FP conversions */
cerion5b2325f2005-12-23 00:55:09 +00005972 case 0x32E: // fctid (Float Conv to Int DWord, PPC64 p437)
5973 DIP("fctid%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
5974 assign( r_tmp64,
5975 binop(Iop_F64toI64, get_roundingmode(), mkexpr(frB)) );
cerion07b07a92005-12-22 14:32:35 +00005976 assign( frD, unop( Iop_ReinterpI64asF64, mkexpr(r_tmp64)) );
5977 break;
cerionf0de28c2005-12-13 20:21:11 +00005978
cerion5b2325f2005-12-23 00:55:09 +00005979 case 0x32F: // fctidz (Float Conv to Int DWord, Round to Zero, PPC64 p437)
5980 DIP("fctidz%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
cerion07b07a92005-12-22 14:32:35 +00005981 assign( r_tmp64, binop(Iop_F64toI64, mkU32(0x3), mkexpr(frB)) );
5982 assign( frD, unop( Iop_ReinterpI64asF64, mkexpr(r_tmp64)) );
5983 break;
cerionf0de28c2005-12-13 20:21:11 +00005984
cerion5b2325f2005-12-23 00:55:09 +00005985 case 0x34E: // fcfid (Float Conv from Int DWord, PPC64 p434)
5986 DIP("fcfid%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
cerion07b07a92005-12-22 14:32:35 +00005987 assign( r_tmp64, unop( Iop_ReinterpF64asI64, mkexpr(frB)) );
cerion5b2325f2005-12-23 00:55:09 +00005988 assign( frD, binop(Iop_I64toF64, get_roundingmode(),
5989 mkexpr(r_tmp64)) );
cerion07b07a92005-12-22 14:32:35 +00005990 break;
cerionf0de28c2005-12-13 20:21:11 +00005991
ceriond953ebb2005-11-29 13:27:20 +00005992 default:
cerion5b2325f2005-12-23 00:55:09 +00005993 vex_printf("dis_fp_round(ppc)(opc2)\n");
ceriond953ebb2005-11-29 13:27:20 +00005994 return False;
sewardje14bb9f2005-07-22 09:39:02 +00005995 }
5996
5997 putFReg( frD_addr, mkexpr(frD) );
5998 return True;
5999}
6000
6001
6002
6003/*
6004 Floating Point Move Instructions
6005*/
6006static Bool dis_fp_move ( UInt theInstr )
6007{
6008 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00006009 UChar opc1 = ifieldOPC(theInstr);
6010 UChar frD_addr = ifieldRegDS(theInstr);
6011 UChar b16to20 = ifieldRegA(theInstr);
6012 UChar frB_addr = ifieldRegB(theInstr);
6013 UInt opc2 = ifieldOPClo10(theInstr);
6014 UChar flag_rC = ifieldBIT0(theInstr);
sewardje14bb9f2005-07-22 09:39:02 +00006015
6016 IRTemp frD = newTemp(Ity_F64);
6017 IRTemp frB = newTemp(Ity_F64);
6018
6019 if (opc1 != 0x3F || b16to20 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006020 vex_printf("dis_fp_move(ppc)(instr)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006021 return False;
6022 }
6023
6024 assign( frB, getFReg(frB_addr));
6025
sewardje14bb9f2005-07-22 09:39:02 +00006026 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00006027 case 0x028: // fneg (Floating Negate, PPC32 p416)
cerion5b2325f2005-12-23 00:55:09 +00006028 DIP("fneg%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
ceriond953ebb2005-11-29 13:27:20 +00006029 assign( frD, unop( Iop_NegF64, mkexpr(frB) ));
6030 break;
6031
6032 case 0x048: // fmr (Floating Move Register, PPC32 p410)
cerion5b2325f2005-12-23 00:55:09 +00006033 DIP("fmr%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
ceriond953ebb2005-11-29 13:27:20 +00006034 assign( frD, mkexpr(frB) );
6035 break;
6036
6037 case 0x088: // fnabs (Floating Negative Absolute Value, PPC32 p415)
cerion5b2325f2005-12-23 00:55:09 +00006038 DIP("fnabs%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
ceriond953ebb2005-11-29 13:27:20 +00006039 assign( frD, unop( Iop_NegF64, unop( Iop_AbsF64, mkexpr(frB) )));
6040 break;
6041
6042 case 0x108: // fabs (Floating Absolute Value, PPC32 p399)
cerion5b2325f2005-12-23 00:55:09 +00006043 DIP("fabs%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
ceriond953ebb2005-11-29 13:27:20 +00006044 assign( frD, unop( Iop_AbsF64, mkexpr(frB) ));
6045 break;
6046
6047 default:
cerion5b2325f2005-12-23 00:55:09 +00006048 vex_printf("dis_fp_move(ppc)(opc2)\n");
ceriond953ebb2005-11-29 13:27:20 +00006049 return False;
sewardje14bb9f2005-07-22 09:39:02 +00006050 }
6051
6052 putFReg( frD_addr, mkexpr(frD) );
6053 return True;
6054}
6055
6056
6057
6058/*
6059 Floating Point Status/Control Register Instructions
6060*/
6061static Bool dis_fp_scr ( UInt theInstr )
6062{
cerion76de5cf2005-11-18 18:25:12 +00006063 /* Many forms - see each switch case */
6064 UChar opc1 = ifieldOPC(theInstr);
6065 UInt opc2 = ifieldOPClo10(theInstr);
6066 UChar flag_rC = ifieldBIT0(theInstr);
sewardje14bb9f2005-07-22 09:39:02 +00006067
6068 if (opc1 != 0x3F) {
cerion5b2325f2005-12-23 00:55:09 +00006069 vex_printf("dis_fp_scr(ppc)(instr)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006070 return False;
6071 }
6072
6073 switch (opc2) {
sewardjb51f0f42005-07-18 11:38:02 +00006074//zz case 0x026: { // mtfsb1 (Move to FPSCR Bit 1, PPC32 p479)
6075//zz // Bit crbD of the FPSCR is set.
cerion76de5cf2005-11-18 18:25:12 +00006076//zz UChar crbD = ifieldRegDS(theInstr);
6077//zz UInt b11to20 = IFIELD(theInstr, 11, 10);
sewardjb51f0f42005-07-18 11:38:02 +00006078//zz
6079//zz if (b11to20 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006080//zz vex_printf("dis_fp_scr(ppc)(instr,mtfsb1)\n");
sewardjb51f0f42005-07-18 11:38:02 +00006081//zz return False;
6082//zz }
cerion5b2325f2005-12-23 00:55:09 +00006083//zz DIP("mtfsb1%s crb%d \n", flag_rC ? ".":"", crbD);
ceriond953ebb2005-11-29 13:27:20 +00006084//zz putGST_masked( PPC_GST_FPSCR, mkU32(1<<(31-crbD)), 1<<(31-crbD) );
sewardjb51f0f42005-07-18 11:38:02 +00006085//zz break;
6086//zz }
6087//zz
6088//zz case 0x040: { // mcrfs (Move to Condition Register from FPSCR, PPC32 p465)
cerion76de5cf2005-11-18 18:25:12 +00006089//zz UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) );
6090//zz UChar b21to22 = toUChar( IFIELD( theInstr, 21, 2 ) );
6091//zz UChar crfS = toUChar( IFIELD( theInstr, 18, 3 ) );
6092//zz UChar b11to17 = toUChar( IFIELD( theInstr, 11, 7 ) );
sewardjb51f0f42005-07-18 11:38:02 +00006093//zz
6094//zz IRTemp tmp = newTemp(Ity_I32);
6095//zz
cerion76de5cf2005-11-18 18:25:12 +00006096//zz if (b21to22 != 0 || b11to17 != 0 || flag_rC != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006097//zz vex_printf("dis_fp_scr(ppc)(instr,mcrfs)\n");
sewardjb51f0f42005-07-18 11:38:02 +00006098//zz return False;
6099//zz }
6100//zz DIP("mcrfs crf%d,crf%d\n", crfD, crfS);
ceriond953ebb2005-11-29 13:27:20 +00006101//zz assign( tmp, getGST_field( PPC_GST_FPSCR, crfS ) );
6102//zz putGST_field( PPC_GST_CR, mkexpr(tmp), crfD );
sewardjb51f0f42005-07-18 11:38:02 +00006103//zz break;
6104//zz }
sewardj0e2cc672005-07-29 21:58:51 +00006105
6106 case 0x046: { // mtfsb0 (Move to FPSCR Bit 0, PPC32 p478)
6107 // Bit crbD of the FPSCR is cleared.
cerion76de5cf2005-11-18 18:25:12 +00006108 UChar crbD = ifieldRegDS(theInstr);
6109 UInt b11to20 = IFIELD(theInstr, 11, 10);
sewardj0e2cc672005-07-29 21:58:51 +00006110
6111 if (b11to20 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006112 vex_printf("dis_fp_scr(ppc)(instr,mtfsb0)\n");
sewardj0e2cc672005-07-29 21:58:51 +00006113 return False;
6114 }
cerion5b2325f2005-12-23 00:55:09 +00006115 DIP("mtfsb0%s crb%d\n", flag_rC ? ".":"", crbD);
ceriond953ebb2005-11-29 13:27:20 +00006116 putGST_masked( PPC_GST_FPSCR, mkU32(0), 1<<(31-crbD) );
sewardj0e2cc672005-07-29 21:58:51 +00006117 break;
6118 }
6119
6120 case 0x086: { // mtfsfi (Move to FPSCR Field Immediate, PPC32 p481)
cerion76de5cf2005-11-18 18:25:12 +00006121 UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) );
6122 UChar b16to22 = toUChar( IFIELD( theInstr, 16, 7 ) );
6123 UChar IMM = toUChar( IFIELD( theInstr, 12, 4 ) );
6124 UChar b11 = toUChar( IFIELD( theInstr, 11, 1 ) );
sewardj0e2cc672005-07-29 21:58:51 +00006125
6126 if (b16to22 != 0 || b11 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006127 vex_printf("dis_fp_scr(ppc)(instr,mtfsfi)\n");
sewardj0e2cc672005-07-29 21:58:51 +00006128 return False;
6129 }
cerion5b2325f2005-12-23 00:55:09 +00006130 DIP("mtfsfi%s crf%d,%d\n", flag_rC ? ".":"", crfD, IMM);
ceriond953ebb2005-11-29 13:27:20 +00006131 putGST_field( PPC_GST_FPSCR, mkU32(IMM), crfD );
sewardj0e2cc672005-07-29 21:58:51 +00006132 break;
6133 }
sewardje14bb9f2005-07-22 09:39:02 +00006134
6135 case 0x247: { // mffs (Move from FPSCR, PPC32 p468)
cerion76de5cf2005-11-18 18:25:12 +00006136 UChar frD_addr = ifieldRegDS(theInstr);
6137 UInt b11to20 = IFIELD(theInstr, 11, 10);
sewardje14bb9f2005-07-22 09:39:02 +00006138
6139 if (b11to20 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006140 vex_printf("dis_fp_scr(ppc)(instr,mffs)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006141 return False;
6142 }
cerion5b2325f2005-12-23 00:55:09 +00006143 DIP("mffs%s fr%u\n", flag_rC ? ".":"", frD_addr);
6144 putFReg( frD_addr,
6145 unop( Iop_ReinterpI64asF64,
6146 unop( Iop_32Uto64,
6147 getGST_masked( PPC_GST_FPSCR, MASK_FPSCR_RN ) )));
sewardje14bb9f2005-07-22 09:39:02 +00006148 break;
6149 }
6150
6151 case 0x2C7: { // mtfsf (Move to FPSCR Fields, PPC32 p480)
cerion76de5cf2005-11-18 18:25:12 +00006152 UChar b25 = toUChar( IFIELD(theInstr, 25, 1) );
6153 UChar FM = toUChar( IFIELD(theInstr, 17, 8) );
6154 UChar b16 = toUChar( IFIELD(theInstr, 16, 1) );
6155 UChar frB_addr = ifieldRegB(theInstr);
6156 IRTemp frB = newTemp(Ity_F64);
sewardje14bb9f2005-07-22 09:39:02 +00006157 IRTemp rB_32 = newTemp(Ity_I32);
cerion76de5cf2005-11-18 18:25:12 +00006158 Int i, mask;
sewardje14bb9f2005-07-22 09:39:02 +00006159
6160 if (b25 != 0 || b16 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006161 vex_printf("dis_fp_scr(ppc)(instr,mtfsf)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006162 return False;
6163 }
cerion5b2325f2005-12-23 00:55:09 +00006164 DIP("mtfsf%s %d,fr%u\n", flag_rC ? ".":"", FM, frB_addr);
sewardje14bb9f2005-07-22 09:39:02 +00006165 assign( frB, getFReg(frB_addr));
6166 assign( rB_32, unop( Iop_64to32,
6167 unop( Iop_ReinterpF64asI64, mkexpr(frB) )));
6168 // Build 32bit mask from FM:
cerion76de5cf2005-11-18 18:25:12 +00006169 mask = 0;
sewardje14bb9f2005-07-22 09:39:02 +00006170 for (i=0; i<8; i++) {
6171 if ((FM & (1<<(7-i))) == 1) {
6172 mask |= 0xF << (7-i);
6173 }
6174 }
ceriond953ebb2005-11-29 13:27:20 +00006175 putGST_masked( PPC_GST_FPSCR, mkexpr(rB_32), mask );
sewardje14bb9f2005-07-22 09:39:02 +00006176 break;
6177 }
6178
6179 default:
cerion5b2325f2005-12-23 00:55:09 +00006180 vex_printf("dis_fp_scr(ppc)(opc2)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006181 return False;
6182 }
6183 return True;
6184}
6185
6186
6187
cerion32aad402005-09-10 12:02:24 +00006188/*------------------------------------------------------------*/
6189/*--- AltiVec Instruction Translation ---*/
6190/*------------------------------------------------------------*/
6191
6192/*
6193 Altivec Cache Control Instructions (Data Streams)
6194*/
6195static Bool dis_av_datastream ( UInt theInstr )
6196{
cerion76de5cf2005-11-18 18:25:12 +00006197 /* X-Form */
6198 UChar opc1 = ifieldOPC(theInstr);
6199 UChar flag_T = toUChar( IFIELD( theInstr, 25, 1 ) );
6200 UChar flag_A = flag_T;
6201 UChar b23to24 = toUChar( IFIELD( theInstr, 23, 2 ) );
6202 UChar STRM = toUChar( IFIELD( theInstr, 21, 2 ) );
6203 UChar rA_addr = ifieldRegA(theInstr);
6204 UChar rB_addr = ifieldRegB(theInstr);
6205 UInt opc2 = ifieldOPClo10(theInstr);
6206 UChar b0 = ifieldBIT0(theInstr);
cerion32aad402005-09-10 12:02:24 +00006207
6208 if (opc1 != 0x1F || b23to24 != 0 || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006209 vex_printf("dis_av_datastream(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00006210 return False;
6211 }
6212
6213 switch (opc2) {
6214 case 0x156: // dst (Data Stream Touch, AV p115)
cerion5b2325f2005-12-23 00:55:09 +00006215 DIP("dst%s r%u,r%u,%d\n", flag_T ? "t" : "",
6216 rA_addr, rB_addr, STRM);
cerion32aad402005-09-10 12:02:24 +00006217 DIP(" => not implemented\n");
6218 return False;
6219
6220 case 0x176: // dstst (Data Stream Touch for Store, AV p117)
cerion5b2325f2005-12-23 00:55:09 +00006221 DIP("dstst%s r%u,r%u,%d\n", flag_T ? "t" : "",
6222 rA_addr, rB_addr, STRM);
cerion32aad402005-09-10 12:02:24 +00006223 DIP(" => not implemented\n");
6224 return False;
6225
6226 case 0x336: // dss (Data Stream Stop, AV p114)
6227 if (rA_addr != 0 || rB_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006228 vex_printf("dis_av_datastream(ppc)(opc2,dst)\n");
cerion32aad402005-09-10 12:02:24 +00006229 return False;
6230 }
6231 if (flag_A == 0) {
6232 DIP("dss %d\n", STRM);
6233 DIP(" => not implemented\n");
6234 } else {
6235 DIP("dssall\n");
6236 DIP(" => not implemented\n");
6237 }
6238 return False;
6239
6240 default:
cerion5b2325f2005-12-23 00:55:09 +00006241 vex_printf("dis_av_datastream(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00006242 return False;
6243 }
6244 return True;
6245}
6246
6247/*
6248 AltiVec Processor Control Instructions
6249*/
6250static Bool dis_av_procctl ( UInt theInstr )
6251{
cerion76de5cf2005-11-18 18:25:12 +00006252 /* VX-Form */
6253 UChar opc1 = ifieldOPC(theInstr);
6254 UChar vD_addr = ifieldRegDS(theInstr);
6255 UChar vA_addr = ifieldRegA(theInstr);
6256 UChar vB_addr = ifieldRegB(theInstr);
6257 UInt opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00006258
6259 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00006260 vex_printf("dis_av_procctl(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00006261 return False;
6262 }
6263
6264 switch (opc2) {
6265 case 0x604: // mfvscr (Move from VSCR, AV p129)
6266 if (vA_addr != 0 || vB_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006267 vex_printf("dis_av_procctl(ppc)(opc2,dst)\n");
cerion32aad402005-09-10 12:02:24 +00006268 return False;
6269 }
6270 DIP("mfvscr v%d\n", vD_addr);
ceriond953ebb2005-11-29 13:27:20 +00006271 putVReg( vD_addr, unop(Iop_32UtoV128, getGST( PPC_GST_VSCR )) );
cerion225a0342005-09-12 20:49:09 +00006272 break;
cerion32aad402005-09-10 12:02:24 +00006273
cerion225a0342005-09-12 20:49:09 +00006274 case 0x644: { // mtvscr (Move to VSCR, AV p130)
sewardj197bd172005-10-12 11:34:33 +00006275 IRTemp vB = newTemp(Ity_V128);
cerion32aad402005-09-10 12:02:24 +00006276 if (vD_addr != 0 || vA_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006277 vex_printf("dis_av_procctl(ppc)(opc2,dst)\n");
cerion32aad402005-09-10 12:02:24 +00006278 return False;
6279 }
6280 DIP("mtvscr v%d\n", vB_addr);
cerion225a0342005-09-12 20:49:09 +00006281 assign( vB, getVReg(vB_addr));
ceriond953ebb2005-11-29 13:27:20 +00006282 putGST( PPC_GST_VSCR, unop(Iop_V128to32, mkexpr(vB)) );
cerion225a0342005-09-12 20:49:09 +00006283 break;
6284 }
cerion32aad402005-09-10 12:02:24 +00006285 default:
cerion5b2325f2005-12-23 00:55:09 +00006286 vex_printf("dis_av_procctl(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00006287 return False;
6288 }
6289 return True;
6290}
ceriona982c052005-06-28 17:23:09 +00006291
6292/*
6293 AltiVec Load Instructions
6294*/
6295static Bool dis_av_load ( UInt theInstr )
6296{
cerion76de5cf2005-11-18 18:25:12 +00006297 /* X-Form */
6298 UChar opc1 = ifieldOPC(theInstr);
6299 UChar vD_addr = ifieldRegDS(theInstr);
6300 UChar rA_addr = ifieldRegA(theInstr);
6301 UChar rB_addr = ifieldRegB(theInstr);
6302 UInt opc2 = ifieldOPClo10(theInstr);
6303 UChar b0 = ifieldBIT0(theInstr);
ceriona982c052005-06-28 17:23:09 +00006304
cerionfb197c42005-12-24 12:32:10 +00006305 IRType ty = mode64 ? Ity_I64 : Ity_I32;
6306 IRTemp EA = newTemp(ty);
6307 IRTemp EA_align16 = newTemp(ty);
cerion2831b002005-11-30 19:55:22 +00006308
ceriona982c052005-06-28 17:23:09 +00006309 if (opc1 != 0x1F || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006310 vex_printf("dis_av_load(ppc)(instr)\n");
ceriona982c052005-06-28 17:23:09 +00006311 return False;
6312 }
6313
cerionfb197c42005-12-24 12:32:10 +00006314 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
6315 assign( EA_align16, addr_align( mkexpr(EA), 16 ) );
ceriona50fde52005-07-01 21:16:10 +00006316
ceriona982c052005-06-28 17:23:09 +00006317 switch (opc2) {
6318
cerion6f6c6a02005-09-13 18:41:09 +00006319 case 0x006: { // lvsl (Load Vector for Shift Left, AV p123)
cerionfb197c42005-12-24 12:32:10 +00006320 IRDirty* d;
sewardjd1470942005-10-22 02:01:16 +00006321 UInt vD_off = vectorGuestRegOffset(vD_addr);
6322 IRExpr** args = mkIRExprVec_3(
6323 mkU32(vD_off),
cerionfb197c42005-12-24 12:32:10 +00006324 binop(Iop_And32, mkSzNarrow32(ty, mkexpr(EA)),
6325 mkU32(0xF)),
sewardjd1470942005-10-22 02:01:16 +00006326 mkU32(0)/*left*/ );
cerion5b2325f2005-12-23 00:55:09 +00006327 if (!mode64) {
6328 d = unsafeIRDirty_0_N ( 0/*regparms*/,
6329 "ppc32g_dirtyhelper_LVS",
6330 &ppc32g_dirtyhelper_LVS,
6331 args );
6332 } else {
6333 d = unsafeIRDirty_0_N ( 0/*regparms*/,
6334 "ppc64g_dirtyhelper_LVS",
6335 &ppc64g_dirtyhelper_LVS,
6336 args );
6337 }
ceriond953ebb2005-11-29 13:27:20 +00006338 DIP("lvsl v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr);
cerion6f6c6a02005-09-13 18:41:09 +00006339 /* declare guest state effects */
6340 d->needsBBP = True;
6341 d->nFxState = 1;
6342 d->fxState[0].fx = Ifx_Write;
sewardjd1470942005-10-22 02:01:16 +00006343 d->fxState[0].offset = vD_off;
cerion6f6c6a02005-09-13 18:41:09 +00006344 d->fxState[0].size = sizeof(U128);
cerion32aad402005-09-10 12:02:24 +00006345
cerion6f6c6a02005-09-13 18:41:09 +00006346 /* execute the dirty call, side-effecting guest state */
6347 stmt( IRStmt_Dirty(d) );
6348 break;
6349 }
6350 case 0x026: { // lvsr (Load Vector for Shift Right, AV p125)
cerionfb197c42005-12-24 12:32:10 +00006351 IRDirty* d;
sewardjd1470942005-10-22 02:01:16 +00006352 UInt vD_off = vectorGuestRegOffset(vD_addr);
6353 IRExpr** args = mkIRExprVec_3(
6354 mkU32(vD_off),
cerionfb197c42005-12-24 12:32:10 +00006355 binop(Iop_And32, mkSzNarrow32(ty, mkexpr(EA)),
6356 mkU32(0xF)),
sewardjd1470942005-10-22 02:01:16 +00006357 mkU32(1)/*right*/ );
cerion5b2325f2005-12-23 00:55:09 +00006358 if (!mode64) {
6359 d = unsafeIRDirty_0_N ( 0/*regparms*/,
6360 "ppc32g_dirtyhelper_LVS",
6361 &ppc32g_dirtyhelper_LVS,
6362 args );
6363 } else {
6364 d = unsafeIRDirty_0_N ( 0/*regparms*/,
6365 "ppc64g_dirtyhelper_LVS",
6366 &ppc64g_dirtyhelper_LVS,
6367 args );
6368 }
ceriond953ebb2005-11-29 13:27:20 +00006369 DIP("lvsr v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr);
cerion6f6c6a02005-09-13 18:41:09 +00006370 /* declare guest state effects */
6371 d->needsBBP = True;
6372 d->nFxState = 1;
6373 d->fxState[0].fx = Ifx_Write;
sewardjd1470942005-10-22 02:01:16 +00006374 d->fxState[0].offset = vD_off;
cerion6f6c6a02005-09-13 18:41:09 +00006375 d->fxState[0].size = sizeof(U128);
cerion32aad402005-09-10 12:02:24 +00006376
cerion6f6c6a02005-09-13 18:41:09 +00006377 /* execute the dirty call, side-effecting guest state */
6378 stmt( IRStmt_Dirty(d) );
6379 break;
6380 }
cerion32aad402005-09-10 12:02:24 +00006381 case 0x007: // lvebx (Load Vector Element Byte Indexed, AV p119)
ceriond953ebb2005-11-29 13:27:20 +00006382 DIP("lvebx v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr);
cerion61c92742005-09-14 22:59:26 +00006383 /* loads addressed byte into vector[EA[0:3]
6384 since all other destination bytes are undefined,
6385 can simply load entire vector from 16-aligned EA */
cerionfb197c42005-12-24 12:32:10 +00006386 putVReg( vD_addr, loadBE(Ity_V128, mkexpr(EA_align16)) );
cerion61c92742005-09-14 22:59:26 +00006387 break;
cerion32aad402005-09-10 12:02:24 +00006388
6389 case 0x027: // lvehx (Load Vector Element Half Word Indexed, AV p121)
ceriond953ebb2005-11-29 13:27:20 +00006390 DIP("lvehx v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr);
cerion61c92742005-09-14 22:59:26 +00006391 /* see note for lvebx */
cerionfb197c42005-12-24 12:32:10 +00006392 putVReg( vD_addr, loadBE(Ity_V128, mkexpr(EA_align16)) );
cerion61c92742005-09-14 22:59:26 +00006393 break;
cerion32aad402005-09-10 12:02:24 +00006394
6395 case 0x047: // lvewx (Load Vector Element Word Indexed, AV p122)
ceriond953ebb2005-11-29 13:27:20 +00006396 DIP("lvewx v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr);
cerion61c92742005-09-14 22:59:26 +00006397 /* see note for lvebx */
cerionfb197c42005-12-24 12:32:10 +00006398 putVReg( vD_addr, loadBE(Ity_V128, mkexpr(EA_align16)) );
cerion61c92742005-09-14 22:59:26 +00006399 break;
ceriona982c052005-06-28 17:23:09 +00006400
6401 case 0x067: // lvx (Load Vector Indexed, AV p127)
ceriond953ebb2005-11-29 13:27:20 +00006402 DIP("lvx v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr);
cerionfb197c42005-12-24 12:32:10 +00006403 putVReg( vD_addr, loadBE(Ity_V128, mkexpr(EA_align16)) );
ceriona50fde52005-07-01 21:16:10 +00006404 break;
ceriona982c052005-06-28 17:23:09 +00006405
cerion32aad402005-09-10 12:02:24 +00006406 case 0x167: // lvxl (Load Vector Indexed LRU, AV p128)
6407 // XXX: lvxl gives explicit control over cache block replacement
ceriond953ebb2005-11-29 13:27:20 +00006408 DIP("lvxl v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr);
cerion32aad402005-09-10 12:02:24 +00006409 DIP(" => not implemented\n");
6410 return False;
ceriona982c052005-06-28 17:23:09 +00006411
6412 default:
cerion5b2325f2005-12-23 00:55:09 +00006413 vex_printf("dis_av_load(ppc)(opc2)\n");
ceriona982c052005-06-28 17:23:09 +00006414 return False;
6415 }
6416 return True;
6417}
6418
cerion2831b002005-11-30 19:55:22 +00006419
ceriona982c052005-06-28 17:23:09 +00006420/*
6421 AltiVec Store Instructions
6422*/
6423static Bool dis_av_store ( UInt theInstr )
6424{
cerion76de5cf2005-11-18 18:25:12 +00006425 /* X-Form */
6426 UChar opc1 = ifieldOPC(theInstr);
6427 UChar vS_addr = ifieldRegDS(theInstr);
6428 UChar rA_addr = ifieldRegA(theInstr);
6429 UChar rB_addr = ifieldRegB(theInstr);
6430 UInt opc2 = ifieldOPClo10(theInstr);
6431 UChar b0 = ifieldBIT0(theInstr);
ceriona982c052005-06-28 17:23:09 +00006432
cerion2831b002005-11-30 19:55:22 +00006433 IRType ty = mode64 ? Ity_I64 : Ity_I32;
6434 IRTemp EA = newTemp(ty);
6435 IRTemp addr_aligned = newTemp(Ity_I32);
6436 IRTemp vS = newTemp(Ity_V128);
6437 IRTemp eb = newTemp(Ity_I8);
6438 IRTemp idx = newTemp(Ity_I8);
ceriona982c052005-06-28 17:23:09 +00006439
6440 if (opc1 != 0x1F || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006441 vex_printf("dis_av_store(ppc)(instr)\n");
ceriona982c052005-06-28 17:23:09 +00006442 return False;
6443 }
6444
ceriond953ebb2005-11-29 13:27:20 +00006445 assign( vS, getVReg(vS_addr));
cerion2831b002005-11-30 19:55:22 +00006446 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
ceriond953ebb2005-11-29 13:27:20 +00006447
ceriona982c052005-06-28 17:23:09 +00006448 switch (opc2) {
cerion61c92742005-09-14 22:59:26 +00006449 case 0x087: { // stvebx (Store Vector Byte Indexed, AV p131)
ceriond953ebb2005-11-29 13:27:20 +00006450 DIP("stvebx v%d,r%u,r%u\n", vS_addr, rA_addr, rB_addr);
cerion61c92742005-09-14 22:59:26 +00006451 assign( eb, binop(Iop_And8, mkU8(0xF),
cerion2831b002005-11-30 19:55:22 +00006452 unop(Iop_32to8,
6453 mkSzNarrow32(ty, mkexpr(EA)) )) );
cerion5b2325f2005-12-23 00:55:09 +00006454 assign( idx, binop(Iop_Shl8,
6455 binop(Iop_Sub8, mkU8(15), mkexpr(eb)),
cerion61c92742005-09-14 22:59:26 +00006456 mkU8(3)) );
6457 storeBE( mkexpr(EA),
6458 unop(Iop_32to8, unop(Iop_V128to32,
6459 binop(Iop_ShrV128, mkexpr(vS), mkexpr(idx)))) );
6460 break;
6461 }
6462 case 0x0A7: { // stvehx (Store Vector Half Word Indexed, AV p132)
ceriond953ebb2005-11-29 13:27:20 +00006463 DIP("stvehx v%d,r%u,r%u\n", vS_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00006464 assign( addr_aligned,
6465 mkSzNarrow32(ty, addr_align(mkexpr(EA), 2)) );
cerion61c92742005-09-14 22:59:26 +00006466 assign( eb, binop(Iop_And8, mkU8(0xF),
ceriond953ebb2005-11-29 13:27:20 +00006467 unop(Iop_32to8, mkexpr(addr_aligned) )) );
cerion5b2325f2005-12-23 00:55:09 +00006468 assign( idx, binop(Iop_Shl8,
6469 binop(Iop_Sub8, mkU8(14), mkexpr(eb)),
cerion61c92742005-09-14 22:59:26 +00006470 mkU8(3)) );
ceriond953ebb2005-11-29 13:27:20 +00006471 storeBE( mkexpr(addr_aligned),
cerion61c92742005-09-14 22:59:26 +00006472 unop(Iop_32to16, unop(Iop_V128to32,
6473 binop(Iop_ShrV128, mkexpr(vS), mkexpr(idx)))) );
6474 break;
6475 }
6476 case 0x0C7: { // stvewx (Store Vector Word Indexed, AV p133)
ceriond953ebb2005-11-29 13:27:20 +00006477 DIP("stvewx v%d,r%u,r%u\n", vS_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00006478 assign( addr_aligned,
6479 mkSzNarrow32(ty, addr_align(mkexpr(EA), 4)) );
cerion61c92742005-09-14 22:59:26 +00006480 assign( eb, binop(Iop_And8, mkU8(0xF),
ceriond953ebb2005-11-29 13:27:20 +00006481 unop(Iop_32to8, mkexpr(addr_aligned) )) );
cerion5b2325f2005-12-23 00:55:09 +00006482 assign( idx, binop(Iop_Shl8,
6483 binop(Iop_Sub8, mkU8(12), mkexpr(eb)),
cerion61c92742005-09-14 22:59:26 +00006484 mkU8(3)) );
ceriond953ebb2005-11-29 13:27:20 +00006485 storeBE( mkexpr(addr_aligned),
cerion61c92742005-09-14 22:59:26 +00006486 unop(Iop_V128to32,
6487 binop(Iop_ShrV128, mkexpr(vS), mkexpr(idx))) );
6488 break;
6489 }
cerion32aad402005-09-10 12:02:24 +00006490
ceriona982c052005-06-28 17:23:09 +00006491 case 0x0E7: // stvx (Store Vector Indexed, AV p134)
ceriond953ebb2005-11-29 13:27:20 +00006492 DIP("stvx v%d,r%u,r%u\n", vS_addr, rA_addr, rB_addr);
6493 storeBE( addr_align( mkexpr(EA), 16 ), mkexpr(vS) );
ceriona982c052005-06-28 17:23:09 +00006494 break;
6495
cerion32aad402005-09-10 12:02:24 +00006496 case 0x1E7: // stvxl (Store Vector Indexed LRU, AV p135)
6497 // XXX: stvxl can give explicit control over cache block replacement
ceriond953ebb2005-11-29 13:27:20 +00006498 DIP("stvxl v%d,r%u,r%u\n", vS_addr, rA_addr, rB_addr);
cerion32aad402005-09-10 12:02:24 +00006499 DIP(" => not implemented\n");
6500 return False;
ceriond953ebb2005-11-29 13:27:20 +00006501// STORE(vS, 16, addr_align( mkexpr(EA), 16 ));
cerion5b2325f2005-12-23 00:55:09 +00006502// break;
ceriona982c052005-06-28 17:23:09 +00006503
6504 default:
cerion5b2325f2005-12-23 00:55:09 +00006505 vex_printf("dis_av_store(ppc)(opc2)\n");
ceriona982c052005-06-28 17:23:09 +00006506 return False;
6507 }
6508 return True;
6509}
6510
cerion32aad402005-09-10 12:02:24 +00006511/*
6512 AltiVec Arithmetic Instructions
6513*/
6514static Bool dis_av_arith ( UInt theInstr )
6515{
cerion76de5cf2005-11-18 18:25:12 +00006516 /* VX-Form */
6517 UChar opc1 = ifieldOPC(theInstr);
6518 UChar vD_addr = ifieldRegDS(theInstr);
6519 UChar vA_addr = ifieldRegA(theInstr);
6520 UChar vB_addr = ifieldRegB(theInstr);
6521 UInt opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00006522
ceriond3e52412005-09-14 21:15:40 +00006523 IRTemp vA = newTemp(Ity_V128);
6524 IRTemp vB = newTemp(Ity_V128);
cerion4a49b032005-11-08 16:23:07 +00006525 IRTemp z3 = newTemp(Ity_I64);
6526 IRTemp z2 = newTemp(Ity_I64);
6527 IRTemp z1 = newTemp(Ity_I64);
6528 IRTemp z0 = newTemp(Ity_I64);
6529 IRTemp aEvn, aOdd;
6530 IRTemp a15, a14, a13, a12, a11, a10, a9, a8;
6531 IRTemp a7, a6, a5, a4, a3, a2, a1, a0;
6532 IRTemp b3, b2, b1, b0;
6533
6534 aEvn = aOdd = IRTemp_INVALID;
6535 a15 = a14 = a13 = a12 = a11 = a10 = a9 = a8 = IRTemp_INVALID;
6536 a7 = a6 = a5 = a4 = a3 = a2 = a1 = a0 = IRTemp_INVALID;
6537 b3 = b2 = b1 = b0 = IRTemp_INVALID;
6538
ceriond3e52412005-09-14 21:15:40 +00006539 assign( vA, getVReg(vA_addr));
6540 assign( vB, getVReg(vB_addr));
6541
cerion32aad402005-09-10 12:02:24 +00006542 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00006543 vex_printf("dis_av_arith(ppc)(opc1 != 0x4)\n");
cerion32aad402005-09-10 12:02:24 +00006544 return False;
6545 }
6546
6547 switch (opc2) {
6548 /* Add */
ceriond3e52412005-09-14 21:15:40 +00006549 case 0x180: { // vaddcuw (Add Carryout Unsigned Word, AV p136)
cerion32aad402005-09-10 12:02:24 +00006550 DIP("vaddcuw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006551 /* unsigned_ov(x+y) = (y >u not(x)) */
ceriond3e52412005-09-14 21:15:40 +00006552 putVReg( vD_addr, binop(Iop_ShrN32x4,
cerion36991ef2005-09-15 12:42:16 +00006553 binop(Iop_CmpGT32Ux4, mkexpr(vB),
6554 unop(Iop_NotV128, mkexpr(vA))),
ceriond3e52412005-09-14 21:15:40 +00006555 mkU8(31)) );
6556 break;
6557 }
cerion32aad402005-09-10 12:02:24 +00006558 case 0x000: // vaddubm (Add Unsigned Byte Modulo, AV p141)
6559 DIP("vaddubm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006560 putVReg( vD_addr, binop(Iop_Add8x16, mkexpr(vA), mkexpr(vB)) );
6561 break;
6562
cerion32aad402005-09-10 12:02:24 +00006563 case 0x040: // vadduhm (Add Unsigned Half Word Modulo, AV p143)
6564 DIP("vadduhm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006565 putVReg( vD_addr, binop(Iop_Add16x8, mkexpr(vA), mkexpr(vB)) );
6566 break;
6567
cerion32aad402005-09-10 12:02:24 +00006568 case 0x080: // vadduwm (Add Unsigned Word Modulo, AV p145)
6569 DIP("vadduwm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006570 putVReg( vD_addr, binop(Iop_Add32x4, mkexpr(vA), mkexpr(vB)) );
6571 break;
6572
cerion32aad402005-09-10 12:02:24 +00006573 case 0x200: // vaddubs (Add Unsigned Byte Saturate, AV p142)
6574 DIP("vaddubs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006575 putVReg( vD_addr, binop(Iop_QAdd8Ux16, mkexpr(vA), mkexpr(vB)) );
6576 // TODO: set VSCR[SAT], perhaps via new primop: Iop_SatOfQAdd8Ux16
6577 break;
6578
cerion32aad402005-09-10 12:02:24 +00006579 case 0x240: // vadduhs (Add Unsigned Half Word Saturate, AV p144)
6580 DIP("vadduhs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006581 putVReg( vD_addr, binop(Iop_QAdd16Ux8, mkexpr(vA), mkexpr(vB)) );
6582 // TODO: set VSCR[SAT]
6583 break;
6584
cerion32aad402005-09-10 12:02:24 +00006585 case 0x280: // vadduws (Add Unsigned Word Saturate, AV p146)
6586 DIP("vadduws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006587 putVReg( vD_addr, binop(Iop_QAdd32Ux4, mkexpr(vA), mkexpr(vB)) );
6588 // TODO: set VSCR[SAT]
6589 break;
6590
cerion32aad402005-09-10 12:02:24 +00006591 case 0x300: // vaddsbs (Add Signed Byte Saturate, AV p138)
6592 DIP("vaddsbs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006593 putVReg( vD_addr, binop(Iop_QAdd8Sx16, mkexpr(vA), mkexpr(vB)) );
6594 // TODO: set VSCR[SAT]
6595 break;
6596
cerion32aad402005-09-10 12:02:24 +00006597 case 0x340: // vaddshs (Add Signed Half Word Saturate, AV p139)
6598 DIP("vaddshs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006599 putVReg( vD_addr, binop(Iop_QAdd16Sx8, mkexpr(vA), mkexpr(vB)) );
6600 // TODO: set VSCR[SAT]
6601 break;
6602
cerion32aad402005-09-10 12:02:24 +00006603 case 0x380: // vaddsws (Add Signed Word Saturate, AV p140)
6604 DIP("vaddsws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006605 putVReg( vD_addr, binop(Iop_QAdd32Sx4, mkexpr(vA), mkexpr(vB)) );
6606 // TODO: set VSCR[SAT]
6607 break;
6608
6609
cerion32aad402005-09-10 12:02:24 +00006610 /* Subtract */
cerion36991ef2005-09-15 12:42:16 +00006611 case 0x580: { // vsubcuw (Subtract Carryout Unsigned Word, AV p260)
cerion32aad402005-09-10 12:02:24 +00006612 DIP("vsubcuw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006613 /* unsigned_ov(x-y) = (y >u x) */
6614 putVReg( vD_addr, binop(Iop_ShrN32x4,
6615 unop(Iop_NotV128,
6616 binop(Iop_CmpGT32Ux4, mkexpr(vB),
6617 mkexpr(vA))),
6618 mkU8(31)) );
6619 break;
6620 }
cerion32aad402005-09-10 12:02:24 +00006621 case 0x400: // vsububm (Subtract Unsigned Byte Modulo, AV p265)
6622 DIP("vsububm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006623 putVReg( vD_addr, binop(Iop_Sub8x16, mkexpr(vA), mkexpr(vB)) );
6624 break;
6625
cerion32aad402005-09-10 12:02:24 +00006626 case 0x440: // vsubuhm (Subtract Unsigned Half Word Modulo, AV p267)
6627 DIP("vsubuhm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006628 putVReg( vD_addr, binop(Iop_Sub16x8, mkexpr(vA), mkexpr(vB)) );
6629 break;
6630
cerion32aad402005-09-10 12:02:24 +00006631 case 0x480: // vsubuwm (Subtract Unsigned Word Modulo, AV p269)
6632 DIP("vsubuwm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006633 putVReg( vD_addr, binop(Iop_Sub32x4, mkexpr(vA), mkexpr(vB)) );
6634 break;
6635
cerion32aad402005-09-10 12:02:24 +00006636 case 0x600: // vsububs (Subtract Unsigned Byte Saturate, AV p266)
6637 DIP("vsububs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006638 putVReg( vD_addr, binop(Iop_QSub8Ux16, mkexpr(vA), mkexpr(vB)) );
6639 // TODO: set VSCR[SAT]
6640 break;
6641
cerion5b2325f2005-12-23 00:55:09 +00006642 case 0x640: // vsubuhs (Subtract Unsigned HWord Saturate, AV p268)
cerion32aad402005-09-10 12:02:24 +00006643 DIP("vsubuhs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006644 putVReg( vD_addr, binop(Iop_QSub16Ux8, mkexpr(vA), mkexpr(vB)) );
6645 // TODO: set VSCR[SAT]
6646 break;
6647
cerion32aad402005-09-10 12:02:24 +00006648 case 0x680: // vsubuws (Subtract Unsigned Word Saturate, AV p270)
6649 DIP("vsubuws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006650 putVReg( vD_addr, binop(Iop_QSub32Ux4, mkexpr(vA), mkexpr(vB)) );
6651 // TODO: set VSCR[SAT]
6652 break;
6653
cerion32aad402005-09-10 12:02:24 +00006654 case 0x700: // vsubsbs (Subtract Signed Byte Saturate, AV p262)
6655 DIP("vsubsbs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006656 putVReg( vD_addr, binop(Iop_QSub8Sx16, mkexpr(vA), mkexpr(vB)) );
6657 // TODO: set VSCR[SAT]
6658 break;
6659
cerion32aad402005-09-10 12:02:24 +00006660 case 0x740: // vsubshs (Subtract Signed Half Word Saturate, AV p263)
6661 DIP("vsubshs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006662 putVReg( vD_addr, binop(Iop_QSub16Sx8, mkexpr(vA), mkexpr(vB)) );
6663 // TODO: set VSCR[SAT]
6664 break;
6665
cerion32aad402005-09-10 12:02:24 +00006666 case 0x780: // vsubsws (Subtract Signed Word Saturate, AV p264)
6667 DIP("vsubsws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006668 putVReg( vD_addr, binop(Iop_QSub32Sx4, mkexpr(vA), mkexpr(vB)) );
6669 // TODO: set VSCR[SAT]
6670 break;
cerion32aad402005-09-10 12:02:24 +00006671
6672
6673 /* Maximum */
6674 case 0x002: // vmaxub (Maximum Unsigned Byte, AV p182)
6675 DIP("vmaxub v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006676 putVReg( vD_addr, binop(Iop_Max8Ux16, mkexpr(vA), mkexpr(vB)) );
6677 break;
cerion32aad402005-09-10 12:02:24 +00006678
6679 case 0x042: // vmaxuh (Maximum Unsigned Half Word, AV p183)
6680 DIP("vmaxuh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006681 putVReg( vD_addr, binop(Iop_Max16Ux8, mkexpr(vA), mkexpr(vB)) );
6682 break;
cerion32aad402005-09-10 12:02:24 +00006683
6684 case 0x082: // vmaxuw (Maximum Unsigned Word, AV p184)
6685 DIP("vmaxuw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006686 putVReg( vD_addr, binop(Iop_Max32Ux4, mkexpr(vA), mkexpr(vB)) );
6687 break;
cerion32aad402005-09-10 12:02:24 +00006688
6689 case 0x102: // vmaxsb (Maximum Signed Byte, AV p179)
6690 DIP("vmaxsb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006691 putVReg( vD_addr, binop(Iop_Max8Sx16, mkexpr(vA), mkexpr(vB)) );
6692 break;
cerion32aad402005-09-10 12:02:24 +00006693
6694 case 0x142: // vmaxsh (Maximum Signed Half Word, AV p180)
6695 DIP("vmaxsh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006696 putVReg( vD_addr, binop(Iop_Max16Sx8, mkexpr(vA), mkexpr(vB)) );
6697 break;
cerion32aad402005-09-10 12:02:24 +00006698
6699 case 0x182: // vmaxsw (Maximum Signed Word, AV p181)
6700 DIP("vmaxsw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006701 putVReg( vD_addr, binop(Iop_Max32Sx4, mkexpr(vA), mkexpr(vB)) );
6702 break;
cerion32aad402005-09-10 12:02:24 +00006703
6704
6705 /* Minimum */
6706 case 0x202: // vminub (Minimum Unsigned Byte, AV p191)
6707 DIP("vminub v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006708 putVReg( vD_addr, binop(Iop_Min8Ux16, mkexpr(vA), mkexpr(vB)) );
6709 break;
cerion32aad402005-09-10 12:02:24 +00006710
6711 case 0x242: // vminuh (Minimum Unsigned Half Word, AV p192)
6712 DIP("vminuh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006713 putVReg( vD_addr, binop(Iop_Min16Ux8, mkexpr(vA), mkexpr(vB)) );
6714 break;
cerion32aad402005-09-10 12:02:24 +00006715
6716 case 0x282: // vminuw (Minimum Unsigned Word, AV p193)
6717 DIP("vminuw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006718 putVReg( vD_addr, binop(Iop_Min32Ux4, mkexpr(vA), mkexpr(vB)) );
6719 break;
cerion32aad402005-09-10 12:02:24 +00006720
6721 case 0x302: // vminsb (Minimum Signed Byte, AV p188)
6722 DIP("vminsb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006723 putVReg( vD_addr, binop(Iop_Min8Sx16, mkexpr(vA), mkexpr(vB)) );
6724 break;
cerion32aad402005-09-10 12:02:24 +00006725
6726 case 0x342: // vminsh (Minimum Signed Half Word, AV p189)
6727 DIP("vminsh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006728 putVReg( vD_addr, binop(Iop_Min16Sx8, mkexpr(vA), mkexpr(vB)) );
6729 break;
cerion32aad402005-09-10 12:02:24 +00006730
6731 case 0x382: // vminsw (Minimum Signed Word, AV p190)
6732 DIP("vminsw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006733 putVReg( vD_addr, binop(Iop_Min32Sx4, mkexpr(vA), mkexpr(vB)) );
6734 break;
6735
cerion32aad402005-09-10 12:02:24 +00006736
6737 /* Average */
6738 case 0x402: // vavgub (Average Unsigned Byte, AV p152)
6739 DIP("vavgub v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006740 putVReg( vD_addr, binop(Iop_Avg8Ux16, mkexpr(vA), mkexpr(vB)) );
6741 break;
cerion32aad402005-09-10 12:02:24 +00006742
6743 case 0x442: // vavguh (Average Unsigned Half Word, AV p153)
6744 DIP("vavguh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006745 putVReg( vD_addr, binop(Iop_Avg16Ux8, mkexpr(vA), mkexpr(vB)) );
6746 break;
cerion32aad402005-09-10 12:02:24 +00006747
6748 case 0x482: // vavguw (Average Unsigned Word, AV p154)
6749 DIP("vavguw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006750 putVReg( vD_addr, binop(Iop_Avg32Ux4, mkexpr(vA), mkexpr(vB)) );
6751 break;
cerion32aad402005-09-10 12:02:24 +00006752
6753 case 0x502: // vavgsb (Average Signed Byte, AV p149)
6754 DIP("vavgsb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006755 putVReg( vD_addr, binop(Iop_Avg8Sx16, mkexpr(vA), mkexpr(vB)) );
6756 break;
cerion32aad402005-09-10 12:02:24 +00006757
6758 case 0x542: // vavgsh (Average Signed Half Word, AV p150)
6759 DIP("vavgsh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006760 putVReg( vD_addr, binop(Iop_Avg16Sx8, mkexpr(vA), mkexpr(vB)) );
6761 break;
cerion32aad402005-09-10 12:02:24 +00006762
6763 case 0x582: // vavgsw (Average Signed Word, AV p151)
6764 DIP("vavgsw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006765 putVReg( vD_addr, binop(Iop_Avg32Sx4, mkexpr(vA), mkexpr(vB)) );
6766 break;
cerion32aad402005-09-10 12:02:24 +00006767
6768
6769 /* Multiply */
6770 case 0x008: // vmuloub (Multiply Odd Unsigned Byte, AV p213)
6771 DIP("vmuloub v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00006772 putVReg( vD_addr,
6773 binop(Iop_MullEven8Ux16, mkexpr(vA), mkexpr(vB)));
cerion36991ef2005-09-15 12:42:16 +00006774 break;
cerion32aad402005-09-10 12:02:24 +00006775
6776 case 0x048: // vmulouh (Multiply Odd Unsigned Half Word, AV p214)
6777 DIP("vmulouh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00006778 putVReg( vD_addr,
6779 binop(Iop_MullEven16Ux8, mkexpr(vA), mkexpr(vB)));
cerion36991ef2005-09-15 12:42:16 +00006780 break;
cerion32aad402005-09-10 12:02:24 +00006781
6782 case 0x108: // vmulosb (Multiply Odd Signed Byte, AV p211)
6783 DIP("vmulosb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00006784 putVReg( vD_addr,
6785 binop(Iop_MullEven8Sx16, mkexpr(vA), mkexpr(vB)));
cerion36991ef2005-09-15 12:42:16 +00006786 break;
cerion32aad402005-09-10 12:02:24 +00006787
6788 case 0x148: // vmulosh (Multiply Odd Signed Half Word, AV p212)
6789 DIP("vmulosh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00006790 putVReg( vD_addr,
6791 binop(Iop_MullEven16Sx8, mkexpr(vA), mkexpr(vB)));
cerion36991ef2005-09-15 12:42:16 +00006792 break;
cerion32aad402005-09-10 12:02:24 +00006793
6794 case 0x208: // vmuleub (Multiply Even Unsigned Byte, AV p209)
6795 DIP("vmuleub v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion24d06f12005-11-09 21:34:20 +00006796 putVReg( vD_addr, MK_Iop_MullOdd8Ux16( mkexpr(vA), mkexpr(vB) ));
cerion36991ef2005-09-15 12:42:16 +00006797 break;
cerion32aad402005-09-10 12:02:24 +00006798
6799 case 0x248: // vmuleuh (Multiply Even Unsigned Half Word, AV p210)
6800 DIP("vmuleuh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion24d06f12005-11-09 21:34:20 +00006801 putVReg( vD_addr, MK_Iop_MullOdd16Ux8( mkexpr(vA), mkexpr(vB) ));
cerion36991ef2005-09-15 12:42:16 +00006802 break;
cerion32aad402005-09-10 12:02:24 +00006803
6804 case 0x308: // vmulesb (Multiply Even Signed Byte, AV p207)
6805 DIP("vmulesb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion24d06f12005-11-09 21:34:20 +00006806 putVReg( vD_addr, MK_Iop_MullOdd8Sx16( mkexpr(vA), mkexpr(vB) ));
cerion36991ef2005-09-15 12:42:16 +00006807 break;
cerion32aad402005-09-10 12:02:24 +00006808
6809 case 0x348: // vmulesh (Multiply Even Signed Half Word, AV p208)
6810 DIP("vmulesh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion24d06f12005-11-09 21:34:20 +00006811 putVReg( vD_addr, MK_Iop_MullOdd16Sx8( mkexpr(vA), mkexpr(vB) ));
cerion36991ef2005-09-15 12:42:16 +00006812 break;
cerion32aad402005-09-10 12:02:24 +00006813
6814
6815 /* Sum Across Partial */
cerion4a49b032005-11-08 16:23:07 +00006816 case 0x608: { // vsum4ubs (Sum Partial (1/4) UB Saturate, AV p275)
6817 IRTemp aEE, aEO, aOE, aOO;
6818 aEE = aEO = aOE = aOO = IRTemp_INVALID;
cerion32aad402005-09-10 12:02:24 +00006819 DIP("vsum4ubs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion32aad402005-09-10 12:02:24 +00006820
cerion4a49b032005-11-08 16:23:07 +00006821 /* vA: V128_8Ux16 -> 4 x V128_32Ux4, sign-extended */
6822 expand8Ux16( mkexpr(vA), &aEvn, &aOdd ); // (15,13...),(14,12...)
6823 expand16Ux8( mkexpr(aEvn), &aEE, &aEO ); // (15,11...),(13, 9...)
6824 expand16Ux8( mkexpr(aOdd), &aOE, &aOO ); // (14,10...),(12, 8...)
6825
6826 /* break V128 to 4xI32's, zero-extending to I64's */
6827 breakV128to4x64U( mkexpr(aEE), &a15, &a11, &a7, &a3 );
6828 breakV128to4x64U( mkexpr(aOE), &a14, &a10, &a6, &a2 );
6829 breakV128to4x64U( mkexpr(aEO), &a13, &a9, &a5, &a1 );
6830 breakV128to4x64U( mkexpr(aOO), &a12, &a8, &a4, &a0 );
6831 breakV128to4x64U( mkexpr(vB), &b3, &b2, &b1, &b0 );
6832
6833 /* add lanes */
6834 assign( z3, binop(Iop_Add64, mkexpr(b3),
cerion5b2325f2005-12-23 00:55:09 +00006835 binop(Iop_Add64,
6836 binop(Iop_Add64, mkexpr(a15), mkexpr(a14)),
6837 binop(Iop_Add64, mkexpr(a13), mkexpr(a12)))) );
cerion4a49b032005-11-08 16:23:07 +00006838 assign( z2, binop(Iop_Add64, mkexpr(b2),
cerion5b2325f2005-12-23 00:55:09 +00006839 binop(Iop_Add64,
6840 binop(Iop_Add64, mkexpr(a11), mkexpr(a10)),
6841 binop(Iop_Add64, mkexpr(a9), mkexpr(a8)))) );
cerion4a49b032005-11-08 16:23:07 +00006842 assign( z1, binop(Iop_Add64, mkexpr(b1),
cerion5b2325f2005-12-23 00:55:09 +00006843 binop(Iop_Add64,
6844 binop(Iop_Add64, mkexpr(a7), mkexpr(a6)),
6845 binop(Iop_Add64, mkexpr(a5), mkexpr(a4)))) );
cerion4a49b032005-11-08 16:23:07 +00006846 assign( z0, binop(Iop_Add64, mkexpr(b0),
cerion5b2325f2005-12-23 00:55:09 +00006847 binop(Iop_Add64,
6848 binop(Iop_Add64, mkexpr(a3), mkexpr(a2)),
6849 binop(Iop_Add64, mkexpr(a1), mkexpr(a0)))) );
cerion4a49b032005-11-08 16:23:07 +00006850
6851 /* saturate-narrow to 32bit, and combine to V128 */
6852 putVReg( vD_addr, mkV128from4x64U( mkexpr(z3), mkexpr(z2),
6853 mkexpr(z1), mkexpr(z0)) );
6854 break;
6855 }
6856 case 0x708: { // vsum4sbs (Sum Partial (1/4) SB Saturate, AV p273)
6857 IRTemp aEE, aEO, aOE, aOO;
6858 aEE = aEO = aOE = aOO = IRTemp_INVALID;
cerion32aad402005-09-10 12:02:24 +00006859 DIP("vsum4sbs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion32aad402005-09-10 12:02:24 +00006860
cerion4a49b032005-11-08 16:23:07 +00006861 /* vA: V128_8Sx16 -> 4 x V128_32Sx4, sign-extended */
6862 expand8Sx16( mkexpr(vA), &aEvn, &aOdd ); // (15,13...),(14,12...)
6863 expand16Sx8( mkexpr(aEvn), &aEE, &aEO ); // (15,11...),(13, 9...)
6864 expand16Sx8( mkexpr(aOdd), &aOE, &aOO ); // (14,10...),(12, 8...)
6865
6866 /* break V128 to 4xI32's, sign-extending to I64's */
6867 breakV128to4x64S( mkexpr(aEE), &a15, &a11, &a7, &a3 );
6868 breakV128to4x64S( mkexpr(aOE), &a14, &a10, &a6, &a2 );
6869 breakV128to4x64S( mkexpr(aEO), &a13, &a9, &a5, &a1 );
6870 breakV128to4x64S( mkexpr(aOO), &a12, &a8, &a4, &a0 );
6871 breakV128to4x64S( mkexpr(vB), &b3, &b2, &b1, &b0 );
6872
6873 /* add lanes */
6874 assign( z3, binop(Iop_Add64, mkexpr(b3),
cerion5b2325f2005-12-23 00:55:09 +00006875 binop(Iop_Add64,
6876 binop(Iop_Add64, mkexpr(a15), mkexpr(a14)),
6877 binop(Iop_Add64, mkexpr(a13), mkexpr(a12)))) );
cerion4a49b032005-11-08 16:23:07 +00006878 assign( z2, binop(Iop_Add64, mkexpr(b2),
cerion5b2325f2005-12-23 00:55:09 +00006879 binop(Iop_Add64,
6880 binop(Iop_Add64, mkexpr(a11), mkexpr(a10)),
6881 binop(Iop_Add64, mkexpr(a9), mkexpr(a8)))) );
cerion4a49b032005-11-08 16:23:07 +00006882 assign( z1, binop(Iop_Add64, mkexpr(b1),
cerion5b2325f2005-12-23 00:55:09 +00006883 binop(Iop_Add64,
6884 binop(Iop_Add64, mkexpr(a7), mkexpr(a6)),
6885 binop(Iop_Add64, mkexpr(a5), mkexpr(a4)))) );
cerion4a49b032005-11-08 16:23:07 +00006886 assign( z0, binop(Iop_Add64, mkexpr(b0),
cerion5b2325f2005-12-23 00:55:09 +00006887 binop(Iop_Add64,
6888 binop(Iop_Add64, mkexpr(a3), mkexpr(a2)),
6889 binop(Iop_Add64, mkexpr(a1), mkexpr(a0)))) );
cerion4a49b032005-11-08 16:23:07 +00006890
6891 /* saturate-narrow to 32bit, and combine to V128 */
6892 putVReg( vD_addr, mkV128from4x64S( mkexpr(z3), mkexpr(z2),
6893 mkexpr(z1), mkexpr(z0)) );
6894 break;
6895 }
6896 case 0x648: { // vsum4shs (Sum Partial (1/4) SHW Saturate, AV p274)
cerion32aad402005-09-10 12:02:24 +00006897 DIP("vsum4shs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion32aad402005-09-10 12:02:24 +00006898
cerion4a49b032005-11-08 16:23:07 +00006899 /* vA: V128_16Sx8 -> 2 x V128_32Sx4, sign-extended */
6900 expand16Sx8( mkexpr(vA), &aEvn, &aOdd ); // (7,5...),(6,4...)
6901
6902 /* break V128 to 4xI32's, sign-extending to I64's */
6903 breakV128to4x64S( mkexpr(aEvn), &a7, &a5, &a3, &a1 );
6904 breakV128to4x64S( mkexpr(aOdd), &a6, &a4, &a2, &a0 );
6905 breakV128to4x64S( mkexpr(vB), &b3, &b2, &b1, &b0 );
6906
6907 /* add lanes */
6908 assign( z3, binop(Iop_Add64, mkexpr(b3),
6909 binop(Iop_Add64, mkexpr(a7), mkexpr(a6))));
6910 assign( z2, binop(Iop_Add64, mkexpr(b2),
6911 binop(Iop_Add64, mkexpr(a5), mkexpr(a4))));
6912 assign( z1, binop(Iop_Add64, mkexpr(b1),
6913 binop(Iop_Add64, mkexpr(a3), mkexpr(a2))));
6914 assign( z0, binop(Iop_Add64, mkexpr(b0),
6915 binop(Iop_Add64, mkexpr(a1), mkexpr(a0))));
6916
6917 /* saturate-narrow to 32bit, and combine to V128 */
6918 putVReg( vD_addr, mkV128from4x64S( mkexpr(z3), mkexpr(z2),
6919 mkexpr(z1), mkexpr(z0)) );
6920 break;
6921 }
6922 case 0x688: { // vsum2sws (Sum Partial (1/2) SW Saturate, AV p272)
cerion32aad402005-09-10 12:02:24 +00006923 DIP("vsum2sws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion32aad402005-09-10 12:02:24 +00006924
cerion4a49b032005-11-08 16:23:07 +00006925 /* break V128 to 4xI32's, sign-extending to I64's */
6926 breakV128to4x64S( mkexpr(vA), &a3, &a2, &a1, &a0 );
6927 breakV128to4x64S( mkexpr(vB), &b3, &b2, &b1, &b0 );
6928
6929 /* add lanes */
6930 assign( z2, binop(Iop_Add64, mkexpr(b2),
6931 binop(Iop_Add64, mkexpr(a3), mkexpr(a2))) );
6932 assign( z0, binop(Iop_Add64, mkexpr(b0),
6933 binop(Iop_Add64, mkexpr(a1), mkexpr(a0))) );
6934
6935 /* saturate-narrow to 32bit, and combine to V128 */
6936 putVReg( vD_addr, mkV128from4x64S( mkU64(0), mkexpr(z2),
6937 mkU64(0), mkexpr(z0)) );
6938 break;
6939 }
6940 case 0x788: { // vsumsws (Sum SW Saturate, AV p271)
cerion32aad402005-09-10 12:02:24 +00006941 DIP("vsumsws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion32aad402005-09-10 12:02:24 +00006942
cerion4a49b032005-11-08 16:23:07 +00006943 /* break V128 to 4xI32's, sign-extending to I64's */
6944 breakV128to4x64S( mkexpr(vA), &a3, &a2, &a1, &a0 );
6945 breakV128to4x64S( mkexpr(vB), &b3, &b2, &b1, &b0 );
6946
6947 /* add lanes */
6948 assign( z0, binop(Iop_Add64, mkexpr(b0),
cerion5b2325f2005-12-23 00:55:09 +00006949 binop(Iop_Add64,
6950 binop(Iop_Add64, mkexpr(a3), mkexpr(a2)),
6951 binop(Iop_Add64, mkexpr(a1), mkexpr(a0)))) );
cerion4a49b032005-11-08 16:23:07 +00006952
6953 /* saturate-narrow to 32bit, and combine to V128 */
6954 putVReg( vD_addr, mkV128from4x64S( mkU64(0), mkU64(0),
6955 mkU64(0), mkexpr(z0)) );
6956 break;
6957 }
cerion32aad402005-09-10 12:02:24 +00006958 default:
cerion5b2325f2005-12-23 00:55:09 +00006959 vex_printf("dis_av_arith(ppc)(opc2=0x%x)\n", opc2);
cerion32aad402005-09-10 12:02:24 +00006960 return False;
6961 }
6962 return True;
6963}
6964
6965/*
6966 AltiVec Logic Instructions
6967*/
6968static Bool dis_av_logic ( UInt theInstr )
6969{
cerion76de5cf2005-11-18 18:25:12 +00006970 /* VX-Form */
6971 UChar opc1 = ifieldOPC(theInstr);
6972 UChar vD_addr = ifieldRegDS(theInstr);
6973 UChar vA_addr = ifieldRegA(theInstr);
6974 UChar vB_addr = ifieldRegB(theInstr);
6975 UInt opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00006976
cerion225a0342005-09-12 20:49:09 +00006977 IRTemp vA = newTemp(Ity_V128);
6978 IRTemp vB = newTemp(Ity_V128);
6979 assign( vA, getVReg(vA_addr));
6980 assign( vB, getVReg(vB_addr));
6981
cerion32aad402005-09-10 12:02:24 +00006982 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00006983 vex_printf("dis_av_logic(ppc)(opc1 != 0x4)\n");
cerion32aad402005-09-10 12:02:24 +00006984 return False;
6985 }
6986
6987 switch (opc2) {
6988 case 0x404: // vand (And, AV p147)
6989 DIP("vand v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion225a0342005-09-12 20:49:09 +00006990 putVReg( vD_addr, binop(Iop_AndV128, mkexpr(vA), mkexpr(vB)) );
6991 break;
cerion32aad402005-09-10 12:02:24 +00006992
6993 case 0x444: // vandc (And, AV p148)
6994 DIP("vandc v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion6e7a0ea2005-09-13 13:34:09 +00006995 putVReg( vD_addr, binop(Iop_AndV128, mkexpr(vA),
cerion76de5cf2005-11-18 18:25:12 +00006996 unop(Iop_NotV128, mkexpr(vB))) );
cerion6e7a0ea2005-09-13 13:34:09 +00006997 break;
cerion32aad402005-09-10 12:02:24 +00006998
6999 case 0x484: // vor (Or, AV p217)
7000 DIP("vor v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion225a0342005-09-12 20:49:09 +00007001 putVReg( vD_addr, binop(Iop_OrV128, mkexpr(vA), mkexpr(vB)) );
7002 break;
cerion32aad402005-09-10 12:02:24 +00007003
7004 case 0x4C4: // vxor (Xor, AV p282)
7005 DIP("vxor v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion225a0342005-09-12 20:49:09 +00007006 putVReg( vD_addr, binop(Iop_XorV128, mkexpr(vA), mkexpr(vB)) );
7007 break;
cerion32aad402005-09-10 12:02:24 +00007008
7009 case 0x504: // vnor (Nor, AV p216)
7010 DIP("vnor v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion6e7a0ea2005-09-13 13:34:09 +00007011 putVReg( vD_addr,
7012 unop(Iop_NotV128, binop(Iop_OrV128, mkexpr(vA), mkexpr(vB))) );
7013 break;
cerion32aad402005-09-10 12:02:24 +00007014
7015 default:
cerion5b2325f2005-12-23 00:55:09 +00007016 vex_printf("dis_av_logic(ppc)(opc2=0x%x)\n", opc2);
cerion32aad402005-09-10 12:02:24 +00007017 return False;
7018 }
7019 return True;
7020}
7021
7022/*
7023 AltiVec Compare Instructions
7024*/
7025static Bool dis_av_cmp ( UInt theInstr )
7026{
cerion76de5cf2005-11-18 18:25:12 +00007027 /* VXR-Form */
7028 UChar opc1 = ifieldOPC(theInstr);
7029 UChar vD_addr = ifieldRegDS(theInstr);
7030 UChar vA_addr = ifieldRegA(theInstr);
7031 UChar vB_addr = ifieldRegB(theInstr);
7032 UChar flag_rC = ifieldBIT10(theInstr);
7033 UInt opc2 = IFIELD( theInstr, 0, 10 );
cerion32aad402005-09-10 12:02:24 +00007034
cerion0c439222005-09-15 14:22:58 +00007035 IRTemp vA = newTemp(Ity_V128);
7036 IRTemp vB = newTemp(Ity_V128);
7037 IRTemp vD = newTemp(Ity_V128);
7038 assign( vA, getVReg(vA_addr));
7039 assign( vB, getVReg(vB_addr));
7040
cerion32aad402005-09-10 12:02:24 +00007041 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00007042 vex_printf("dis_av_cmp(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00007043 return False;
7044 }
7045
7046 switch (opc2) {
7047 case 0x006: // vcmpequb (Compare Equal-to Unsigned B, AV p160)
cerion5b2325f2005-12-23 00:55:09 +00007048 DIP("vcmpequb%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7049 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007050 assign( vD, binop(Iop_CmpEQ8x16, mkexpr(vA), mkexpr(vB)) );
7051 break;
cerion32aad402005-09-10 12:02:24 +00007052
7053 case 0x046: // vcmpequh (Compare Equal-to Unsigned HW, AV p161)
cerion5b2325f2005-12-23 00:55:09 +00007054 DIP("vcmpequh%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7055 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007056 assign( vD, binop(Iop_CmpEQ16x8, mkexpr(vA), mkexpr(vB)) );
7057 break;
cerion32aad402005-09-10 12:02:24 +00007058
7059 case 0x086: // vcmpequw (Compare Equal-to Unsigned W, AV p162)
cerion5b2325f2005-12-23 00:55:09 +00007060 DIP("vcmpequw%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7061 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007062 assign( vD, binop(Iop_CmpEQ32x4, mkexpr(vA), mkexpr(vB)) );
7063 break;
cerion32aad402005-09-10 12:02:24 +00007064
7065 case 0x206: // vcmpgtub (Compare Greater-than Unsigned B, AV p168)
cerion5b2325f2005-12-23 00:55:09 +00007066 DIP("vcmpgtub%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7067 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007068 assign( vD, binop(Iop_CmpGT8Ux16, mkexpr(vA), mkexpr(vB)) );
7069 break;
cerion32aad402005-09-10 12:02:24 +00007070
7071 case 0x246: // vcmpgtuh (Compare Greater-than Unsigned HW, AV p169)
cerion5b2325f2005-12-23 00:55:09 +00007072 DIP("vcmpgtuh%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7073 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007074 assign( vD, binop(Iop_CmpGT16Ux8, mkexpr(vA), mkexpr(vB)) );
7075 break;
cerion32aad402005-09-10 12:02:24 +00007076
7077 case 0x286: // vcmpgtuw (Compare Greater-than Unsigned W, AV p170)
cerion5b2325f2005-12-23 00:55:09 +00007078 DIP("vcmpgtuw%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7079 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007080 assign( vD, binop(Iop_CmpGT32Ux4, mkexpr(vA), mkexpr(vB)) );
7081 break;
cerion32aad402005-09-10 12:02:24 +00007082
7083 case 0x306: // vcmpgtsb (Compare Greater-than Signed B, AV p165)
cerion5b2325f2005-12-23 00:55:09 +00007084 DIP("vcmpgtsb%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7085 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007086 assign( vD, binop(Iop_CmpGT8Sx16, mkexpr(vA), mkexpr(vB)) );
7087 break;
cerion32aad402005-09-10 12:02:24 +00007088
7089 case 0x346: // vcmpgtsh (Compare Greater-than Signed HW, AV p166)
cerion5b2325f2005-12-23 00:55:09 +00007090 DIP("vcmpgtsh%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7091 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007092 assign( vD, binop(Iop_CmpGT16Sx8, mkexpr(vA), mkexpr(vB)) );
7093 break;
cerion32aad402005-09-10 12:02:24 +00007094
7095 case 0x386: // vcmpgtsw (Compare Greater-than Signed W, AV p167)
cerion5b2325f2005-12-23 00:55:09 +00007096 DIP("vcmpgtsw%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7097 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007098 assign( vD, binop(Iop_CmpGT32Sx4, mkexpr(vA), mkexpr(vB)) );
7099 break;
cerion32aad402005-09-10 12:02:24 +00007100
7101 default:
cerion5b2325f2005-12-23 00:55:09 +00007102 vex_printf("dis_av_cmp(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00007103 return False;
7104 }
cerion0c439222005-09-15 14:22:58 +00007105
7106 putVReg( vD_addr, mkexpr(vD) );
7107
cerion76de5cf2005-11-18 18:25:12 +00007108 if (flag_rC) {
cerion8ea0d3e2005-11-14 00:44:47 +00007109 set_AV_CR6( mkexpr(vD), True );
cerion0c439222005-09-15 14:22:58 +00007110 }
cerion32aad402005-09-10 12:02:24 +00007111 return True;
7112}
7113
7114/*
7115 AltiVec Multiply-Sum Instructions
7116*/
7117static Bool dis_av_multarith ( UInt theInstr )
7118{
cerion76de5cf2005-11-18 18:25:12 +00007119 /* VA-Form */
7120 UChar opc1 = ifieldOPC(theInstr);
7121 UChar vD_addr = ifieldRegDS(theInstr);
7122 UChar vA_addr = ifieldRegA(theInstr);
7123 UChar vB_addr = ifieldRegB(theInstr);
7124 UChar vC_addr = ifieldRegC(theInstr);
7125 UChar opc2 = toUChar( IFIELD( theInstr, 0, 6 ) );
cerion32aad402005-09-10 12:02:24 +00007126
cerion4a49b032005-11-08 16:23:07 +00007127 IRTemp vA = newTemp(Ity_V128);
7128 IRTemp vB = newTemp(Ity_V128);
7129 IRTemp vC = newTemp(Ity_V128);
sewardj197bd172005-10-12 11:34:33 +00007130 IRTemp zeros = newTemp(Ity_V128);
cerion4a49b032005-11-08 16:23:07 +00007131 IRTemp aLo = newTemp(Ity_V128);
7132 IRTemp bLo = newTemp(Ity_V128);
7133 IRTemp cLo = newTemp(Ity_V128);
7134 IRTemp zLo = newTemp(Ity_V128);
7135 IRTemp aHi = newTemp(Ity_V128);
7136 IRTemp bHi = newTemp(Ity_V128);
7137 IRTemp cHi = newTemp(Ity_V128);
7138 IRTemp zHi = newTemp(Ity_V128);
7139 IRTemp abEvn = newTemp(Ity_V128);
7140 IRTemp abOdd = newTemp(Ity_V128);
7141 IRTemp z3 = newTemp(Ity_I64);
7142 IRTemp z2 = newTemp(Ity_I64);
7143 IRTemp z1 = newTemp(Ity_I64);
7144 IRTemp z0 = newTemp(Ity_I64);
7145 IRTemp ab7, ab6, ab5, ab4, ab3, ab2, ab1, ab0;
7146 IRTemp c3, c2, c1, c0;
7147
7148 ab7 = ab6 = ab5 = ab4 = ab3 = ab2 = ab1 = ab0 = IRTemp_INVALID;
7149 c3 = c2 = c1 = c0 = IRTemp_INVALID;
7150
cerion6f1cc0f2005-09-16 16:02:11 +00007151 assign( vA, getVReg(vA_addr));
7152 assign( vB, getVReg(vB_addr));
7153 assign( vC, getVReg(vC_addr));
cerion4a49b032005-11-08 16:23:07 +00007154 assign( zeros, unop(Iop_Dup32x4, mkU32(0)) );
cerion6f1cc0f2005-09-16 16:02:11 +00007155
cerion32aad402005-09-10 12:02:24 +00007156 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00007157 vex_printf("dis_av_multarith(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00007158 return False;
7159 }
7160
7161 switch (opc2) {
cerion32aad402005-09-10 12:02:24 +00007162 /* Multiply-Add */
cerion5b2325f2005-12-23 00:55:09 +00007163 case 0x20: { // vmhaddshs (Mult Hi, Add Signed HW Saturate, AV p185)
cerion6f1cc0f2005-09-16 16:02:11 +00007164 IRTemp cSigns = newTemp(Ity_V128);
cerion5b2325f2005-12-23 00:55:09 +00007165 DIP("vmhaddshs v%d,v%d,v%d,v%d\n",
7166 vD_addr, vA_addr, vB_addr, vC_addr);
7167 assign(cSigns, binop(Iop_CmpGT16Sx8, mkexpr(zeros), mkexpr(vC)));
7168 assign(aLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vA)));
7169 assign(bLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vB)));
7170 assign(cLo, binop(Iop_InterleaveLO16x8, mkexpr(cSigns),mkexpr(vC)));
7171 assign(aHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vA)));
7172 assign(bHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vB)));
7173 assign(cHi, binop(Iop_InterleaveHI16x8, mkexpr(cSigns),mkexpr(vC)));
cerion32aad402005-09-10 12:02:24 +00007174
cerion24d06f12005-11-09 21:34:20 +00007175 assign( zLo, binop(Iop_Add32x4, mkexpr(cLo),
cerion6f1cc0f2005-09-16 16:02:11 +00007176 binop(Iop_SarN32x4,
cerion1ac656a2005-11-04 19:44:48 +00007177 binop(Iop_MullEven16Sx8,
cerion24d06f12005-11-09 21:34:20 +00007178 mkexpr(aLo), mkexpr(bLo)),
7179 mkU8(15))) );
cerion6f1cc0f2005-09-16 16:02:11 +00007180
cerion24d06f12005-11-09 21:34:20 +00007181 assign( zHi, binop(Iop_Add32x4, mkexpr(cHi),
cerion6f1cc0f2005-09-16 16:02:11 +00007182 binop(Iop_SarN32x4,
cerion1ac656a2005-11-04 19:44:48 +00007183 binop(Iop_MullEven16Sx8,
cerion24d06f12005-11-09 21:34:20 +00007184 mkexpr(aHi), mkexpr(bHi)),
7185 mkU8(15))) );
cerion6f1cc0f2005-09-16 16:02:11 +00007186
cerion5b2325f2005-12-23 00:55:09 +00007187 putVReg( vD_addr,
7188 binop(Iop_QNarrow32Sx4, mkexpr(zHi), mkexpr(zLo)) );
cerion6f1cc0f2005-09-16 16:02:11 +00007189 break;
7190 }
cerion5b2325f2005-12-23 00:55:09 +00007191 case 0x21: { // vmhraddshs (Mult High Round, Add Signed HW Saturate, AV p186)
cerion6f1cc0f2005-09-16 16:02:11 +00007192 IRTemp zKonst = newTemp(Ity_V128);
cerion6f1cc0f2005-09-16 16:02:11 +00007193 IRTemp cSigns = newTemp(Ity_V128);
cerion5b2325f2005-12-23 00:55:09 +00007194 DIP("vmhraddshs v%d,v%d,v%d,v%d\n",
7195 vD_addr, vA_addr, vB_addr, vC_addr);
7196 assign(cSigns, binop(Iop_CmpGT16Sx8, mkexpr(zeros), mkexpr(vC)) );
7197 assign(aLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vA)));
7198 assign(bLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vB)));
7199 assign(cLo, binop(Iop_InterleaveLO16x8, mkexpr(cSigns),mkexpr(vC)));
7200 assign(aHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vA)));
7201 assign(bHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vB)));
7202 assign(cHi, binop(Iop_InterleaveHI16x8, mkexpr(cSigns),mkexpr(vC)));
cerion32aad402005-09-10 12:02:24 +00007203
cerion6f1cc0f2005-09-16 16:02:11 +00007204 /* shifting our const avoids store/load version of Dup */
cerion4a49b032005-11-08 16:23:07 +00007205 assign( zKonst, binop(Iop_ShlN32x4, unop(Iop_Dup32x4, mkU32(0x1)),
7206 mkU8(14)) );
cerion6f1cc0f2005-09-16 16:02:11 +00007207
cerion24d06f12005-11-09 21:34:20 +00007208 assign( zLo, binop(Iop_Add32x4, mkexpr(cLo),
cerion6f1cc0f2005-09-16 16:02:11 +00007209 binop(Iop_SarN32x4,
7210 binop(Iop_Add32x4, mkexpr(zKonst),
cerion1ac656a2005-11-04 19:44:48 +00007211 binop(Iop_MullEven16Sx8,
cerion24d06f12005-11-09 21:34:20 +00007212 mkexpr(aLo), mkexpr(bLo))),
7213 mkU8(15))) );
cerion6f1cc0f2005-09-16 16:02:11 +00007214
cerion24d06f12005-11-09 21:34:20 +00007215 assign( zHi, binop(Iop_Add32x4, mkexpr(cHi),
cerion6f1cc0f2005-09-16 16:02:11 +00007216 binop(Iop_SarN32x4,
7217 binop(Iop_Add32x4, mkexpr(zKonst),
cerion1ac656a2005-11-04 19:44:48 +00007218 binop(Iop_MullEven16Sx8,
cerion24d06f12005-11-09 21:34:20 +00007219 mkexpr(aHi), mkexpr(bHi))),
7220 mkU8(15))) );
cerion6f1cc0f2005-09-16 16:02:11 +00007221
7222 putVReg( vD_addr, binop(Iop_QNarrow32Sx4, mkexpr(zHi), mkexpr(zLo)) );
7223 break;
7224 }
cerion5b2325f2005-12-23 00:55:09 +00007225 case 0x22: { // vmladduhm (Mult Low, Add Unsigned HW Modulo, AV p194)
7226 DIP("vmladduhm v%d,v%d,v%d,v%d\n",
7227 vD_addr, vA_addr, vB_addr, vC_addr);
7228 assign(aLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vA)));
7229 assign(bLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vB)));
7230 assign(cLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vC)));
7231 assign(aHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vA)));
7232 assign(bHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vB)));
7233 assign(cHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vC)));
7234 assign(zLo, binop(Iop_Add32x4,
7235 binop(Iop_MullEven16Ux8, mkexpr(aLo), mkexpr(bLo)),
7236 mkexpr(cLo)) );
7237 assign(zHi, binop(Iop_Add32x4,
7238 binop(Iop_MullEven16Ux8, mkexpr(aHi), mkexpr(bHi)),
7239 mkexpr(cHi)));
7240 putVReg(vD_addr, binop(Iop_Narrow32x4, mkexpr(zHi), mkexpr(zLo)));
cerion6f1cc0f2005-09-16 16:02:11 +00007241 break;
7242 }
cerion32aad402005-09-10 12:02:24 +00007243
7244
7245 /* Multiply-Sum */
cerion6f1cc0f2005-09-16 16:02:11 +00007246 case 0x24: { // vmsumubm (Multiply Sum Unsigned B Modulo, AV p204)
cerion4a49b032005-11-08 16:23:07 +00007247 IRTemp abEE, abEO, abOE, abOO;
7248 abEE = abEO = abOE = abOO = IRTemp_INVALID;
cerion5b2325f2005-12-23 00:55:09 +00007249 DIP("vmsumubm v%d,v%d,v%d,v%d\n",
7250 vD_addr, vA_addr, vB_addr, vC_addr);
cerion32aad402005-09-10 12:02:24 +00007251
cerion4a49b032005-11-08 16:23:07 +00007252 /* multiply vA,vB (unsigned, widening) */
cerion24d06f12005-11-09 21:34:20 +00007253 assign( abEvn, MK_Iop_MullOdd8Ux16( mkexpr(vA), mkexpr(vB) ));
7254 assign( abOdd, binop(Iop_MullEven8Ux16, mkexpr(vA), mkexpr(vB)) );
cerion4a49b032005-11-08 16:23:07 +00007255
7256 /* evn,odd: V128_16Ux8 -> 2 x V128_32Ux4, zero-extended */
7257 expand16Ux8( mkexpr(abEvn), &abEE, &abEO );
7258 expand16Ux8( mkexpr(abOdd), &abOE, &abOO );
7259
cerion6f1cc0f2005-09-16 16:02:11 +00007260 putVReg( vD_addr,
cerion5b2325f2005-12-23 00:55:09 +00007261 binop(Iop_Add32x4, mkexpr(vC),
7262 binop(Iop_Add32x4,
7263 binop(Iop_Add32x4, mkexpr(abEE), mkexpr(abEO)),
7264 binop(Iop_Add32x4, mkexpr(abOE), mkexpr(abOO)))) );
cerion6f1cc0f2005-09-16 16:02:11 +00007265 break;
7266 }
cerion4a49b032005-11-08 16:23:07 +00007267 case 0x25: { // vmsummbm (Multiply Sum Mixed-Sign B Modulo, AV p201)
7268 IRTemp aEvn, aOdd, bEvn, bOdd;
7269 IRTemp abEE = newTemp(Ity_V128);
7270 IRTemp abEO = newTemp(Ity_V128);
7271 IRTemp abOE = newTemp(Ity_V128);
7272 IRTemp abOO = newTemp(Ity_V128);
7273 aEvn = aOdd = bEvn = bOdd = IRTemp_INVALID;
cerion5b2325f2005-12-23 00:55:09 +00007274 DIP("vmsummbm v%d,v%d,v%d,v%d\n",
7275 vD_addr, vA_addr, vB_addr, vC_addr);
cerion32aad402005-09-10 12:02:24 +00007276
cerion4a49b032005-11-08 16:23:07 +00007277 /* sign-extend vA, zero-extend vB, for mixed-sign multiply
7278 (separating out adjacent lanes to different vectors) */
7279 expand8Sx16( mkexpr(vA), &aEvn, &aOdd );
7280 expand8Ux16( mkexpr(vB), &bEvn, &bOdd );
7281
7282 /* multiply vA, vB, again separating adjacent lanes */
cerion24d06f12005-11-09 21:34:20 +00007283 assign( abEE, MK_Iop_MullOdd16Sx8( mkexpr(aEvn), mkexpr(bEvn) ));
7284 assign( abEO, binop(Iop_MullEven16Sx8, mkexpr(aEvn), mkexpr(bEvn)) );
7285 assign( abOE, MK_Iop_MullOdd16Sx8( mkexpr(aOdd), mkexpr(bOdd) ));
7286 assign( abOO, binop(Iop_MullEven16Sx8, mkexpr(aOdd), mkexpr(bOdd)) );
cerion4a49b032005-11-08 16:23:07 +00007287
7288 /* add results together, + vC */
7289 putVReg( vD_addr,
cerion5b2325f2005-12-23 00:55:09 +00007290 binop(Iop_QAdd32Sx4, mkexpr(vC),
7291 binop(Iop_QAdd32Sx4,
7292 binop(Iop_QAdd32Sx4, mkexpr(abEE), mkexpr(abEO)),
7293 binop(Iop_QAdd32Sx4, mkexpr(abOE), mkexpr(abOO)))) );
cerion4a49b032005-11-08 16:23:07 +00007294 break;
7295 }
cerion6f1cc0f2005-09-16 16:02:11 +00007296 case 0x26: { // vmsumuhm (Multiply Sum Unsigned HW Modulo, AV p205)
cerion5b2325f2005-12-23 00:55:09 +00007297 DIP("vmsumuhm v%d,v%d,v%d,v%d\n",
7298 vD_addr, vA_addr, vB_addr, vC_addr);
cerion24d06f12005-11-09 21:34:20 +00007299 assign( abEvn, MK_Iop_MullOdd16Ux8( mkexpr(vA), mkexpr(vB) ));
7300 assign( abOdd, binop(Iop_MullEven16Ux8, mkexpr(vA), mkexpr(vB)) );
cerion6f1cc0f2005-09-16 16:02:11 +00007301 putVReg( vD_addr,
cerion5b2325f2005-12-23 00:55:09 +00007302 binop(Iop_Add32x4, mkexpr(vC),
7303 binop(Iop_Add32x4, mkexpr(abEvn), mkexpr(abOdd))) );
cerion6f1cc0f2005-09-16 16:02:11 +00007304 break;
7305 }
cerion4a49b032005-11-08 16:23:07 +00007306 case 0x27: { // vmsumuhs (Multiply Sum Unsigned HW Saturate, AV p206)
cerion5b2325f2005-12-23 00:55:09 +00007307 DIP("vmsumuhs v%d,v%d,v%d,v%d\n",
7308 vD_addr, vA_addr, vB_addr, vC_addr);
cerion4a49b032005-11-08 16:23:07 +00007309 /* widening multiply, separating lanes */
cerion24d06f12005-11-09 21:34:20 +00007310 assign( abEvn, MK_Iop_MullOdd16Ux8(mkexpr(vA), mkexpr(vB) ));
7311 assign( abOdd, binop(Iop_MullEven16Ux8, mkexpr(vA), mkexpr(vB)) );
cerion32aad402005-09-10 12:02:24 +00007312
cerion4a49b032005-11-08 16:23:07 +00007313 /* break V128 to 4xI32's, zero-extending to I64's */
7314 breakV128to4x64U( mkexpr(abEvn), &ab7, &ab5, &ab3, &ab1 );
7315 breakV128to4x64U( mkexpr(abOdd), &ab6, &ab4, &ab2, &ab0 );
7316 breakV128to4x64U( mkexpr(vC), &c3, &c2, &c1, &c0 );
7317
7318 /* add lanes */
7319 assign( z3, binop(Iop_Add64, mkexpr(c3),
7320 binop(Iop_Add64, mkexpr(ab7), mkexpr(ab6))));
7321 assign( z2, binop(Iop_Add64, mkexpr(c2),
7322 binop(Iop_Add64, mkexpr(ab5), mkexpr(ab4))));
7323 assign( z1, binop(Iop_Add64, mkexpr(c1),
7324 binop(Iop_Add64, mkexpr(ab3), mkexpr(ab2))));
7325 assign( z0, binop(Iop_Add64, mkexpr(c0),
7326 binop(Iop_Add64, mkexpr(ab1), mkexpr(ab0))));
7327
7328 /* saturate-narrow to 32bit, and combine to V128 */
7329 putVReg( vD_addr, mkV128from4x64U( mkexpr(z3), mkexpr(z2),
7330 mkexpr(z1), mkexpr(z0)) );
7331
cerion6f1cc0f2005-09-16 16:02:11 +00007332 break;
7333 }
cerion4a49b032005-11-08 16:23:07 +00007334 case 0x28: { // vmsumshm (Multiply Sum Signed HW Modulo, AV p202)
cerion5b2325f2005-12-23 00:55:09 +00007335 DIP("vmsumshm v%d,v%d,v%d,v%d\n",
7336 vD_addr, vA_addr, vB_addr, vC_addr);
cerion24d06f12005-11-09 21:34:20 +00007337 assign( abEvn, MK_Iop_MullOdd16Sx8( mkexpr(vA), mkexpr(vB) ));
7338 assign( abOdd, binop(Iop_MullEven16Sx8, mkexpr(vA), mkexpr(vB)) );
cerion4a49b032005-11-08 16:23:07 +00007339 putVReg( vD_addr,
cerion5b2325f2005-12-23 00:55:09 +00007340 binop(Iop_Add32x4, mkexpr(vC),
7341 binop(Iop_Add32x4, mkexpr(abOdd), mkexpr(abEvn))) );
cerion4a49b032005-11-08 16:23:07 +00007342 break;
7343 }
7344 case 0x29: { // vmsumshs (Multiply Sum Signed HW Saturate, AV p203)
cerion5b2325f2005-12-23 00:55:09 +00007345 DIP("vmsumshs v%d,v%d,v%d,v%d\n",
7346 vD_addr, vA_addr, vB_addr, vC_addr);
cerion4a49b032005-11-08 16:23:07 +00007347 /* widening multiply, separating lanes */
cerion24d06f12005-11-09 21:34:20 +00007348 assign( abEvn, MK_Iop_MullOdd16Sx8( mkexpr(vA), mkexpr(vB) ));
7349 assign( abOdd, binop(Iop_MullEven16Sx8, mkexpr(vA), mkexpr(vB)) );
cerion32aad402005-09-10 12:02:24 +00007350
cerion4a49b032005-11-08 16:23:07 +00007351 /* break V128 to 4xI32's, sign-extending to I64's */
7352 breakV128to4x64S( mkexpr(abEvn), &ab7, &ab5, &ab3, &ab1 );
7353 breakV128to4x64S( mkexpr(abOdd), &ab6, &ab4, &ab2, &ab0 );
7354 breakV128to4x64S( mkexpr(vC), &c3, &c2, &c1, &c0 );
7355
7356 /* add lanes */
7357 assign( z3, binop(Iop_Add64, mkexpr(c3),
7358 binop(Iop_Add64, mkexpr(ab7), mkexpr(ab6))));
7359 assign( z2, binop(Iop_Add64, mkexpr(c2),
7360 binop(Iop_Add64, mkexpr(ab5), mkexpr(ab4))));
7361 assign( z1, binop(Iop_Add64, mkexpr(c1),
7362 binop(Iop_Add64, mkexpr(ab3), mkexpr(ab2))));
7363 assign( z0, binop(Iop_Add64, mkexpr(c0),
7364 binop(Iop_Add64, mkexpr(ab1), mkexpr(ab0))));
7365
7366 /* saturate-narrow to 32bit, and combine to V128 */
7367 putVReg( vD_addr, mkV128from4x64S( mkexpr(z3), mkexpr(z2),
7368 mkexpr(z1), mkexpr(z0)) );
7369 break;
7370 }
cerion32aad402005-09-10 12:02:24 +00007371 default:
cerion5b2325f2005-12-23 00:55:09 +00007372 vex_printf("dis_av_multarith(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00007373 return False;
7374 }
7375 return True;
7376}
7377
7378/*
7379 AltiVec Shift/Rotate Instructions
7380*/
7381static Bool dis_av_shift ( UInt theInstr )
7382{
cerion76de5cf2005-11-18 18:25:12 +00007383 /* VX-Form */
7384 UChar opc1 = ifieldOPC(theInstr);
7385 UChar vD_addr = ifieldRegDS(theInstr);
7386 UChar vA_addr = ifieldRegA(theInstr);
7387 UChar vB_addr = ifieldRegB(theInstr);
7388 UInt opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00007389
cerion27b3d7e2005-09-14 20:35:47 +00007390 IRTemp vA = newTemp(Ity_V128);
7391 IRTemp vB = newTemp(Ity_V128);
7392 assign( vA, getVReg(vA_addr));
7393 assign( vB, getVReg(vB_addr));
7394
cerion32aad402005-09-10 12:02:24 +00007395 if (opc1 != 0x4){
cerion5b2325f2005-12-23 00:55:09 +00007396 vex_printf("dis_av_shift(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00007397 return False;
7398 }
7399
7400 switch (opc2) {
7401 /* Rotate */
7402 case 0x004: // vrlb (Rotate Left Integer B, AV p234)
7403 DIP("vrlb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
sewardj1bee5612005-11-10 18:10:58 +00007404 putVReg( vD_addr, binop(Iop_Rol8x16, mkexpr(vA), mkexpr(vB)) );
cerion0a7b4f42005-09-16 07:54:40 +00007405 break;
cerion32aad402005-09-10 12:02:24 +00007406
7407 case 0x044: // vrlh (Rotate Left Integer HW, AV p235)
7408 DIP("vrlh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
sewardj1bee5612005-11-10 18:10:58 +00007409 putVReg( vD_addr, binop(Iop_Rol16x8, mkexpr(vA), mkexpr(vB)) );
cerion0a7b4f42005-09-16 07:54:40 +00007410 break;
cerion32aad402005-09-10 12:02:24 +00007411
7412 case 0x084: // vrlw (Rotate Left Integer W, AV p236)
7413 DIP("vrlw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
sewardj1bee5612005-11-10 18:10:58 +00007414 putVReg( vD_addr, binop(Iop_Rol32x4, mkexpr(vA), mkexpr(vB)) );
cerion0a7b4f42005-09-16 07:54:40 +00007415 break;
cerion32aad402005-09-10 12:02:24 +00007416
7417
7418 /* Shift Left */
7419 case 0x104: // vslb (Shift Left Integer B, AV p240)
7420 DIP("vslb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007421 putVReg( vD_addr, binop(Iop_Shl8x16, mkexpr(vA), mkexpr(vB)) );
7422 break;
cerion32aad402005-09-10 12:02:24 +00007423
7424 case 0x144: // vslh (Shift Left Integer HW, AV p242)
7425 DIP("vslh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007426 putVReg( vD_addr, binop(Iop_Shl16x8, mkexpr(vA), mkexpr(vB)) );
7427 break;
cerion32aad402005-09-10 12:02:24 +00007428
7429 case 0x184: // vslw (Shift Left Integer W, AV p244)
7430 DIP("vslw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007431 putVReg( vD_addr, binop(Iop_Shl32x4, mkexpr(vA), mkexpr(vB)) );
7432 break;
cerion32aad402005-09-10 12:02:24 +00007433
cerion0a7b4f42005-09-16 07:54:40 +00007434 case 0x1C4: { // vsl (Shift Left, AV p239)
cerion0a7b4f42005-09-16 07:54:40 +00007435 IRTemp sh = newTemp(Ity_I8);
sewardj197bd172005-10-12 11:34:33 +00007436 DIP("vsl v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007437 assign( sh, binop(Iop_And8, mkU8(0x7),
7438 unop(Iop_32to8,
7439 unop(Iop_V128to32, mkexpr(vB)))) );
7440 putVReg( vD_addr,
7441 binop(Iop_ShlV128, mkexpr(vA), mkexpr(sh)) );
7442 break;
7443 }
7444 case 0x40C: { // vslo (Shift Left by Octet, AV p243)
cerion0a7b4f42005-09-16 07:54:40 +00007445 IRTemp sh = newTemp(Ity_I8);
sewardj197bd172005-10-12 11:34:33 +00007446 DIP("vslo v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007447 assign( sh, binop(Iop_And8, mkU8(0x78),
7448 unop(Iop_32to8,
7449 unop(Iop_V128to32, mkexpr(vB)))) );
7450 putVReg( vD_addr,
7451 binop(Iop_ShlV128, mkexpr(vA), mkexpr(sh)) );
7452 break;
7453 }
7454
cerion32aad402005-09-10 12:02:24 +00007455
7456 /* Shift Right */
7457 case 0x204: // vsrb (Shift Right B, AV p256)
7458 DIP("vsrb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007459 putVReg( vD_addr, binop(Iop_Shr8x16, mkexpr(vA), mkexpr(vB)) );
7460 break;
cerion32aad402005-09-10 12:02:24 +00007461
7462 case 0x244: // vsrh (Shift Right HW, AV p257)
7463 DIP("vsrh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007464 putVReg( vD_addr, binop(Iop_Shr16x8, mkexpr(vA), mkexpr(vB)) );
7465 break;
cerion32aad402005-09-10 12:02:24 +00007466
7467 case 0x284: // vsrw (Shift Right W, AV p259)
7468 DIP("vsrw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007469 putVReg( vD_addr, binop(Iop_Shr32x4, mkexpr(vA), mkexpr(vB)) );
7470 break;
cerion32aad402005-09-10 12:02:24 +00007471
cerion27b3d7e2005-09-14 20:35:47 +00007472 case 0x2C4: { // vsr (Shift Right, AV p251)
cerion27b3d7e2005-09-14 20:35:47 +00007473 IRTemp sh = newTemp(Ity_I8);
sewardj197bd172005-10-12 11:34:33 +00007474 DIP("vsr v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion27b3d7e2005-09-14 20:35:47 +00007475 assign( sh, binop(Iop_And8, mkU8(0x7),
7476 unop(Iop_32to8,
7477 unop(Iop_V128to32, mkexpr(vB)))) );
7478 putVReg( vD_addr,
7479 binop(Iop_ShrV128, mkexpr(vA), mkexpr(sh)) );
7480 break;
7481 }
cerion5b2325f2005-12-23 00:55:09 +00007482 case 0x304: // vsrab (Shift Right Alg B, AV p253)
cerion32aad402005-09-10 12:02:24 +00007483 DIP("vsrab v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007484 putVReg( vD_addr, binop(Iop_Sar8x16, mkexpr(vA), mkexpr(vB)) );
7485 break;
cerion32aad402005-09-10 12:02:24 +00007486
cerion5b2325f2005-12-23 00:55:09 +00007487 case 0x344: // vsrah (Shift Right Alg HW, AV p254)
cerion32aad402005-09-10 12:02:24 +00007488 DIP("vsrah v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007489 putVReg( vD_addr, binop(Iop_Sar16x8, mkexpr(vA), mkexpr(vB)) );
7490 break;
cerion32aad402005-09-10 12:02:24 +00007491
cerion5b2325f2005-12-23 00:55:09 +00007492 case 0x384: // vsraw (Shift Right Alg W, AV p255)
cerion32aad402005-09-10 12:02:24 +00007493 DIP("vsraw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007494 putVReg( vD_addr, binop(Iop_Sar32x4, mkexpr(vA), mkexpr(vB)) );
7495 break;
cerion32aad402005-09-10 12:02:24 +00007496
cerion0a7b4f42005-09-16 07:54:40 +00007497 case 0x44C: { // vsro (Shift Right by Octet, AV p258)
cerion0a7b4f42005-09-16 07:54:40 +00007498 IRTemp sh = newTemp(Ity_I8);
sewardj197bd172005-10-12 11:34:33 +00007499 DIP("vsro v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007500 assign( sh, binop(Iop_And8, mkU8(0x78),
7501 unop(Iop_32to8,
7502 unop(Iop_V128to32, mkexpr(vB)))) );
7503 putVReg( vD_addr,
7504 binop(Iop_ShrV128, mkexpr(vA), mkexpr(sh)) );
7505 break;
7506 }
cerion32aad402005-09-10 12:02:24 +00007507
7508 default:
cerion5b2325f2005-12-23 00:55:09 +00007509 vex_printf("dis_av_shift(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00007510 return False;
7511 }
7512 return True;
7513}
7514
7515/*
7516 AltiVec Permute Instructions
7517*/
7518static Bool dis_av_permute ( UInt theInstr )
7519{
cerion76de5cf2005-11-18 18:25:12 +00007520 /* VA-Form, VX-Form */
7521 UChar opc1 = ifieldOPC(theInstr);
7522 UChar vD_addr = ifieldRegDS(theInstr);
7523 UChar vA_addr = ifieldRegA(theInstr);
7524 UChar UIMM_5 = vA_addr;
7525 UChar vB_addr = ifieldRegB(theInstr);
7526 UChar vC_addr = ifieldRegC(theInstr);
7527 UChar b10 = ifieldBIT10(theInstr);
7528 UChar SHB_uimm4 = toUChar( IFIELD( theInstr, 6, 4 ) );
7529 UInt opc2 = toUChar( IFIELD( theInstr, 0, 6 ) );
cerion32aad402005-09-10 12:02:24 +00007530
cerion76de5cf2005-11-18 18:25:12 +00007531 UChar SIMM_8 = extend_s_5to8(UIMM_5);
cerion32aad402005-09-10 12:02:24 +00007532
cerion6e7a0ea2005-09-13 13:34:09 +00007533 IRTemp vA = newTemp(Ity_V128);
7534 IRTemp vB = newTemp(Ity_V128);
7535 IRTemp vC = newTemp(Ity_V128);
7536 assign( vA, getVReg(vA_addr));
7537 assign( vB, getVReg(vB_addr));
7538 assign( vC, getVReg(vC_addr));
7539
cerion32aad402005-09-10 12:02:24 +00007540 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00007541 vex_printf("dis_av_permute(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00007542 return False;
7543 }
7544
7545 switch (opc2) {
7546 case 0x2A: // vsel (Conditional Select, AV p238)
7547 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 +00007548 /* vD = (vA & ~vC) | (vB & vC) */
7549 putVReg( vD_addr, binop(Iop_OrV128,
7550 binop(Iop_AndV128, mkexpr(vA), unop(Iop_NotV128, mkexpr(vC))),
7551 binop(Iop_AndV128, mkexpr(vB), mkexpr(vC))) );
7552 return True;
cerion32aad402005-09-10 12:02:24 +00007553
cerion92d9d872005-09-15 21:58:50 +00007554 case 0x2B: { // vperm (Permute, AV p218)
cerion92d9d872005-09-15 21:58:50 +00007555 /* limited to two args for IR, so have to play games... */
sewardjdc1f9132005-10-22 12:49:49 +00007556 IRTemp a_perm = newTemp(Ity_V128);
7557 IRTemp b_perm = newTemp(Ity_V128);
7558 IRTemp mask = newTemp(Ity_V128);
7559 IRTemp vC_andF = newTemp(Ity_V128);
cerion5b2325f2005-12-23 00:55:09 +00007560 DIP("vperm v%d,v%d,v%d,v%d\n",
7561 vD_addr, vA_addr, vB_addr, vC_addr);
sewardjdc1f9132005-10-22 12:49:49 +00007562 /* Limit the Perm8x16 steering values to 0 .. 15 as that is what
7563 IR specifies, and also to hide irrelevant bits from
7564 memcheck */
cerion5b2325f2005-12-23 00:55:09 +00007565 assign( vC_andF,
7566 binop(Iop_AndV128, mkexpr(vC),
7567 unop(Iop_Dup8x16, mkU8(0xF))) );
7568 assign( a_perm,
7569 binop(Iop_Perm8x16, mkexpr(vA), mkexpr(vC_andF)) );
7570 assign( b_perm,
7571 binop(Iop_Perm8x16, mkexpr(vB), mkexpr(vC_andF)) );
cerion92d9d872005-09-15 21:58:50 +00007572 // mask[i8] = (vC[i8]_4 == 1) ? 0xFF : 0x0
7573 assign( mask, binop(Iop_SarN8x16,
7574 binop(Iop_ShlN8x16, mkexpr(vC), mkU8(3)),
7575 mkU8(7)) );
7576 // dst = (a & ~mask) | (b & mask)
7577 putVReg( vD_addr, binop(Iop_OrV128,
7578 binop(Iop_AndV128, mkexpr(a_perm),
7579 unop(Iop_NotV128, mkexpr(mask))),
7580 binop(Iop_AndV128, mkexpr(b_perm),
7581 mkexpr(mask))) );
7582 return True;
7583 }
cerion32aad402005-09-10 12:02:24 +00007584 case 0x2C: // vsldoi (Shift Left Double by Octet Imm, AV p241)
7585 if (b10 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00007586 vex_printf("dis_av_permute(ppc)(vsldoi)\n");
cerion32aad402005-09-10 12:02:24 +00007587 return False;
7588 }
cerion5b2325f2005-12-23 00:55:09 +00007589 DIP("vsldoi v%d,v%d,v%d,%d\n",
7590 vD_addr, vA_addr, vB_addr, SHB_uimm4);
cerion92d9d872005-09-15 21:58:50 +00007591 if (SHB_uimm4 == 0)
7592 putVReg( vD_addr, mkexpr(vA) );
7593 else
7594 putVReg( vD_addr,
cerion5b2325f2005-12-23 00:55:09 +00007595 binop(Iop_OrV128,
7596 binop(Iop_ShlV128, mkexpr(vA), mkU8(SHB_uimm4*8)),
7597 binop(Iop_ShrV128, mkexpr(vB), mkU8((16-SHB_uimm4)*8))) );
cerion92d9d872005-09-15 21:58:50 +00007598 return True;
cerion32aad402005-09-10 12:02:24 +00007599
7600 default:
7601 break; // Fall through...
7602 }
7603
cerion76de5cf2005-11-18 18:25:12 +00007604 opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00007605 switch (opc2) {
7606
7607 /* Merge */
7608 case 0x00C: // vmrghb (Merge High B, AV p195)
7609 DIP("vmrghb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion92d9d872005-09-15 21:58:50 +00007610 putVReg( vD_addr,
7611 binop(Iop_InterleaveHI8x16, mkexpr(vA), mkexpr(vB)) );
7612 break;
cerion32aad402005-09-10 12:02:24 +00007613
7614 case 0x04C: // vmrghh (Merge High HW, AV p196)
7615 DIP("vmrghh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion92d9d872005-09-15 21:58:50 +00007616 putVReg( vD_addr,
7617 binop(Iop_InterleaveHI16x8, mkexpr(vA), mkexpr(vB)) );
7618 break;
cerion32aad402005-09-10 12:02:24 +00007619
7620 case 0x08C: // vmrghw (Merge High W, AV p197)
7621 DIP("vmrghw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion92d9d872005-09-15 21:58:50 +00007622 putVReg( vD_addr,
7623 binop(Iop_InterleaveHI32x4, mkexpr(vA), mkexpr(vB)) );
7624 break;
cerion32aad402005-09-10 12:02:24 +00007625
7626 case 0x10C: // vmrglb (Merge Low B, AV p198)
7627 DIP("vmrglb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion92d9d872005-09-15 21:58:50 +00007628 putVReg( vD_addr,
7629 binop(Iop_InterleaveLO8x16, mkexpr(vA), mkexpr(vB)) );
7630 break;
cerion32aad402005-09-10 12:02:24 +00007631
7632 case 0x14C: // vmrglh (Merge Low HW, AV p199)
7633 DIP("vmrglh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion92d9d872005-09-15 21:58:50 +00007634 putVReg( vD_addr,
7635 binop(Iop_InterleaveLO16x8, mkexpr(vA), mkexpr(vB)) );
7636 break;
cerion32aad402005-09-10 12:02:24 +00007637
7638 case 0x18C: // vmrglw (Merge Low W, AV p200)
7639 DIP("vmrglw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion92d9d872005-09-15 21:58:50 +00007640 putVReg( vD_addr,
7641 binop(Iop_InterleaveLO32x4, mkexpr(vA), mkexpr(vB)) );
7642 break;
7643
cerion32aad402005-09-10 12:02:24 +00007644
7645 /* Splat */
cerion92d9d872005-09-15 21:58:50 +00007646 case 0x20C: { // vspltb (Splat Byte, AV p245)
cerion92d9d872005-09-15 21:58:50 +00007647 /* vD = Dup8x16( vB[UIMM_5] ) */
sewardjd1470942005-10-22 02:01:16 +00007648 UChar sh_uimm = (15 - (UIMM_5 & 15)) * 8;
sewardj197bd172005-10-12 11:34:33 +00007649 DIP("vspltb v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
cerion92d9d872005-09-15 21:58:50 +00007650 putVReg( vD_addr, unop(Iop_Dup8x16,
7651 unop(Iop_32to8, unop(Iop_V128to32,
7652 binop(Iop_ShrV128, mkexpr(vB), mkU8(sh_uimm))))) );
7653 break;
7654 }
7655 case 0x24C: { // vsplth (Splat Half Word, AV p246)
sewardjd1470942005-10-22 02:01:16 +00007656 UChar sh_uimm = (7 - (UIMM_5 & 7)) * 16;
sewardj197bd172005-10-12 11:34:33 +00007657 DIP("vsplth v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
cerion92d9d872005-09-15 21:58:50 +00007658 putVReg( vD_addr, unop(Iop_Dup16x8,
7659 unop(Iop_32to16, unop(Iop_V128to32,
7660 binop(Iop_ShrV128, mkexpr(vB), mkU8(sh_uimm))))) );
7661 break;
7662 }
cerion27b3d7e2005-09-14 20:35:47 +00007663 case 0x28C: { // vspltw (Splat Word, AV p250)
cerion27b3d7e2005-09-14 20:35:47 +00007664 /* vD = Dup32x4( vB[UIMM_5] ) */
sewardjd1470942005-10-22 02:01:16 +00007665 UChar sh_uimm = (3 - (UIMM_5 & 3)) * 32;
sewardj197bd172005-10-12 11:34:33 +00007666 DIP("vspltw v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
cerion27b3d7e2005-09-14 20:35:47 +00007667 putVReg( vD_addr, unop(Iop_Dup32x4,
7668 unop(Iop_V128to32,
7669 binop(Iop_ShrV128, mkexpr(vB), mkU8(sh_uimm)))) );
7670 break;
7671 }
cerion32aad402005-09-10 12:02:24 +00007672 case 0x30C: // vspltisb (Splat Immediate Signed B, AV p247)
7673 DIP("vspltisb v%d,%d\n", vD_addr, (Char)SIMM_8);
cerion92d9d872005-09-15 21:58:50 +00007674 putVReg( vD_addr, unop(Iop_Dup8x16, mkU8(SIMM_8)) );
7675 break;
cerion32aad402005-09-10 12:02:24 +00007676
7677 case 0x34C: // vspltish (Splat Immediate Signed HW, AV p248)
7678 DIP("vspltish v%d,%d\n", vD_addr, (Char)SIMM_8);
cerion5b2325f2005-12-23 00:55:09 +00007679 putVReg( vD_addr,
7680 unop(Iop_Dup16x8, mkU16(extend_s_8to32(SIMM_8))) );
cerion92d9d872005-09-15 21:58:50 +00007681 break;
cerion32aad402005-09-10 12:02:24 +00007682
7683 case 0x38C: // vspltisw (Splat Immediate Signed W, AV p249)
7684 DIP("vspltisw v%d,%d\n", vD_addr, (Char)SIMM_8);
cerion5b2325f2005-12-23 00:55:09 +00007685 putVReg( vD_addr,
7686 unop(Iop_Dup32x4, mkU32(extend_s_8to32(SIMM_8))) );
cerion92d9d872005-09-15 21:58:50 +00007687 break;
cerion32aad402005-09-10 12:02:24 +00007688
7689 default:
cerion5b2325f2005-12-23 00:55:09 +00007690 vex_printf("dis_av_permute(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00007691 return False;
7692 }
7693 return True;
7694}
7695
7696/*
7697 AltiVec Pack/Unpack Instructions
7698*/
7699static Bool dis_av_pack ( UInt theInstr )
7700{
cerion76de5cf2005-11-18 18:25:12 +00007701 /* VX-Form */
7702 UChar opc1 = ifieldOPC(theInstr);
7703 UChar vD_addr = ifieldRegDS(theInstr);
7704 UChar vA_addr = ifieldRegA(theInstr);
7705 UChar vB_addr = ifieldRegB(theInstr);
7706 UInt opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00007707
sewardj197bd172005-10-12 11:34:33 +00007708 IRTemp signs = IRTemp_INVALID;
7709 IRTemp zeros = IRTemp_INVALID;
cerion76de5cf2005-11-18 18:25:12 +00007710 IRTemp vA = newTemp(Ity_V128);
7711 IRTemp vB = newTemp(Ity_V128);
cerion3c052792005-09-16 07:13:44 +00007712 assign( vA, getVReg(vA_addr));
7713 assign( vB, getVReg(vB_addr));
7714
cerion32aad402005-09-10 12:02:24 +00007715 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00007716 vex_printf("dis_av_pack(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00007717 return False;
7718 }
7719
7720 switch (opc2) {
7721 /* Packing */
7722 case 0x00E: // vpkuhum (Pack Unsigned HW Unsigned Modulo, AV p224)
7723 DIP("vpkuhum v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
sewardj1bee5612005-11-10 18:10:58 +00007724 putVReg( vD_addr, binop(Iop_Narrow16x8, mkexpr(vA), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00007725 return True;
cerion32aad402005-09-10 12:02:24 +00007726
7727 case 0x04E: // vpkuwum (Pack Unsigned W Unsigned Modulo, AV p226)
7728 DIP("vpkuwum v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
sewardj1bee5612005-11-10 18:10:58 +00007729 putVReg( vD_addr, binop(Iop_Narrow32x4, mkexpr(vA), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00007730 return True;
cerion32aad402005-09-10 12:02:24 +00007731
7732 case 0x08E: // vpkuhus (Pack Unsigned HW Unsigned Saturate, AV p225)
7733 DIP("vpkuhus v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00007734 putVReg( vD_addr,
7735 binop(Iop_QNarrow16Ux8, mkexpr(vA), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00007736 // TODO: set VSCR[SAT]
7737 return True;
cerion32aad402005-09-10 12:02:24 +00007738
7739 case 0x0CE: // vpkuwus (Pack Unsigned W Unsigned Saturate, AV p227)
7740 DIP("vpkuwus v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00007741 putVReg( vD_addr,
7742 binop(Iop_QNarrow32Ux4, mkexpr(vA), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00007743 // TODO: set VSCR[SAT]
7744 return True;
cerion32aad402005-09-10 12:02:24 +00007745
cerion3c052792005-09-16 07:13:44 +00007746 case 0x10E: { // vpkshus (Pack Signed HW Unsigned Saturate, AV p221)
cerion3c052792005-09-16 07:13:44 +00007747 // This insn does a signed->unsigned saturating conversion.
7748 // Conversion done here, then uses unsigned->unsigned vpk insn:
7749 // => UnsignedSaturatingNarrow( x & ~ (x >>s 15) )
7750 IRTemp vA_tmp = newTemp(Ity_V128);
7751 IRTemp vB_tmp = newTemp(Ity_V128);
sewardj197bd172005-10-12 11:34:33 +00007752 DIP("vpkshus v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00007753 assign( vA_tmp, binop(Iop_AndV128, mkexpr(vA),
7754 unop(Iop_NotV128,
7755 binop(Iop_SarN16x8,
7756 mkexpr(vA), mkU8(15)))) );
7757 assign( vB_tmp, binop(Iop_AndV128, mkexpr(vB),
7758 unop(Iop_NotV128,
7759 binop(Iop_SarN16x8,
7760 mkexpr(vB), mkU8(15)))) );
7761 putVReg( vD_addr, binop(Iop_QNarrow16Ux8,
7762 mkexpr(vA_tmp), mkexpr(vB_tmp)) );
7763 // TODO: set VSCR[SAT]
7764 return True;
7765 }
7766 case 0x14E: { // vpkswus (Pack Signed W Unsigned Saturate, AV p223)
cerion3c052792005-09-16 07:13:44 +00007767 // This insn does a signed->unsigned saturating conversion.
7768 // Conversion done here, then uses unsigned->unsigned vpk insn:
7769 // => UnsignedSaturatingNarrow( x & ~ (x >>s 31) )
7770 IRTemp vA_tmp = newTemp(Ity_V128);
7771 IRTemp vB_tmp = newTemp(Ity_V128);
sewardj197bd172005-10-12 11:34:33 +00007772 DIP("vpkswus v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00007773 assign( vA_tmp, binop(Iop_AndV128, mkexpr(vA),
7774 unop(Iop_NotV128,
7775 binop(Iop_SarN32x4,
7776 mkexpr(vA), mkU8(31)))) );
7777 assign( vB_tmp, binop(Iop_AndV128, mkexpr(vB),
7778 unop(Iop_NotV128,
7779 binop(Iop_SarN32x4,
7780 mkexpr(vB), mkU8(31)))) );
7781 putVReg( vD_addr, binop(Iop_QNarrow32Ux4,
7782 mkexpr(vA_tmp), mkexpr(vB_tmp)) );
7783 // TODO: set VSCR[SAT]
7784 return True;
7785 }
cerion32aad402005-09-10 12:02:24 +00007786 case 0x18E: // vpkshss (Pack Signed HW Signed Saturate, AV p220)
7787 DIP("vpkshss v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00007788 putVReg( vD_addr,
7789 binop(Iop_QNarrow16Sx8, mkexpr(vA), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00007790 // TODO: set VSCR[SAT]
7791 return True;
cerion32aad402005-09-10 12:02:24 +00007792
7793 case 0x1CE: // vpkswss (Pack Signed W Signed Saturate, AV p222)
7794 DIP("vpkswss v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00007795 putVReg( vD_addr,
7796 binop(Iop_QNarrow32Sx4, mkexpr(vA), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00007797 // TODO: set VSCR[SAT]
7798 return True;
cerion32aad402005-09-10 12:02:24 +00007799
cerion3c052792005-09-16 07:13:44 +00007800 case 0x30E: { // vpkpx (Pack Pixel, AV p219)
cerion3c052792005-09-16 07:13:44 +00007801 /* CAB: Worth a new primop? */
cerion5b2325f2005-12-23 00:55:09 +00007802 /* Using shifts to compact pixel elements, then packing them */
cerion3c052792005-09-16 07:13:44 +00007803 IRTemp a1 = newTemp(Ity_V128);
7804 IRTemp a2 = newTemp(Ity_V128);
7805 IRTemp a3 = newTemp(Ity_V128);
7806 IRTemp a_tmp = newTemp(Ity_V128);
7807 IRTemp b1 = newTemp(Ity_V128);
7808 IRTemp b2 = newTemp(Ity_V128);
7809 IRTemp b3 = newTemp(Ity_V128);
7810 IRTemp b_tmp = newTemp(Ity_V128);
sewardj197bd172005-10-12 11:34:33 +00007811 DIP("vpkpx v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00007812 assign( a1, binop(Iop_ShlN16x8,
7813 binop(Iop_ShrN32x4, mkexpr(vA), mkU8(19)),
7814 mkU8(10)) );
7815 assign( a2, binop(Iop_ShlN16x8,
7816 binop(Iop_ShrN16x8, mkexpr(vA), mkU8(11)),
7817 mkU8(5)) );
7818 assign( a3, binop(Iop_ShrN16x8,
7819 binop(Iop_ShlN16x8, mkexpr(vA), mkU8(8)),
7820 mkU8(11)) );
7821 assign( a_tmp, binop(Iop_OrV128, mkexpr(a1),
7822 binop(Iop_OrV128, mkexpr(a2), mkexpr(a3))) );
7823
7824 assign( b1, binop(Iop_ShlN16x8,
7825 binop(Iop_ShrN32x4, mkexpr(vB), mkU8(19)),
7826 mkU8(10)) );
7827 assign( b2, binop(Iop_ShlN16x8,
7828 binop(Iop_ShrN16x8, mkexpr(vB), mkU8(11)),
7829 mkU8(5)) );
7830 assign( b3, binop(Iop_ShrN16x8,
7831 binop(Iop_ShlN16x8, mkexpr(vB), mkU8(8)),
7832 mkU8(11)) );
7833 assign( b_tmp, binop(Iop_OrV128, mkexpr(b1),
7834 binop(Iop_OrV128, mkexpr(b2), mkexpr(b3))) );
7835
sewardj1bee5612005-11-10 18:10:58 +00007836 putVReg( vD_addr, binop(Iop_Narrow32x4,
cerion3c052792005-09-16 07:13:44 +00007837 mkexpr(a_tmp), mkexpr(b_tmp)) );
7838 return True;
7839 }
cerion32aad402005-09-10 12:02:24 +00007840
7841 default:
7842 break; // Fall through...
7843 }
7844
7845
7846 if (vA_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00007847 vex_printf("dis_av_pack(ppc)(vA_addr)\n");
cerion32aad402005-09-10 12:02:24 +00007848 return False;
7849 }
7850
sewardj197bd172005-10-12 11:34:33 +00007851 signs = newTemp(Ity_V128);
7852 zeros = newTemp(Ity_V128);
cerion3c052792005-09-16 07:13:44 +00007853 assign( zeros, unop(Iop_Dup32x4, mkU32(0)) );
7854
cerion32aad402005-09-10 12:02:24 +00007855 switch (opc2) {
7856 /* Unpacking */
cerion3c052792005-09-16 07:13:44 +00007857 case 0x20E: { // vupkhsb (Unpack High Signed B, AV p277)
cerion32aad402005-09-10 12:02:24 +00007858 DIP("vupkhsb v%d,v%d\n", vD_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00007859 assign( signs, binop(Iop_CmpGT8Sx16, mkexpr(zeros), mkexpr(vB)) );
cerion5b2325f2005-12-23 00:55:09 +00007860 putVReg( vD_addr,
7861 binop(Iop_InterleaveHI8x16, mkexpr(signs), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00007862 break;
7863 }
7864 case 0x24E: { // vupkhsh (Unpack High Signed HW, AV p278)
cerion32aad402005-09-10 12:02:24 +00007865 DIP("vupkhsh v%d,v%d\n", vD_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00007866 assign( signs, binop(Iop_CmpGT16Sx8, mkexpr(zeros), mkexpr(vB)) );
cerion5b2325f2005-12-23 00:55:09 +00007867 putVReg( vD_addr,
7868 binop(Iop_InterleaveHI16x8, mkexpr(signs), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00007869 break;
7870 }
7871 case 0x28E: { // vupklsb (Unpack Low Signed B, AV p280)
cerion32aad402005-09-10 12:02:24 +00007872 DIP("vupklsb v%d,v%d\n", vD_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00007873 assign( signs, binop(Iop_CmpGT8Sx16, mkexpr(zeros), mkexpr(vB)) );
cerion5b2325f2005-12-23 00:55:09 +00007874 putVReg( vD_addr,
7875 binop(Iop_InterleaveLO8x16, mkexpr(signs), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00007876 break;
7877 }
7878 case 0x2CE: { // vupklsh (Unpack Low Signed HW, AV p281)
cerion32aad402005-09-10 12:02:24 +00007879 DIP("vupklsh v%d,v%d\n", vD_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00007880 assign( signs, binop(Iop_CmpGT16Sx8, mkexpr(zeros), mkexpr(vB)) );
cerion5b2325f2005-12-23 00:55:09 +00007881 putVReg( vD_addr,
7882 binop(Iop_InterleaveLO16x8, mkexpr(signs), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00007883 break;
7884 }
7885 case 0x34E: { // vupkhpx (Unpack High Pixel16, AV p276)
cerion3c052792005-09-16 07:13:44 +00007886 /* CAB: Worth a new primop? */
7887 /* Using shifts to isolate pixel elements, then expanding them */
7888 IRTemp z0 = newTemp(Ity_V128);
7889 IRTemp z1 = newTemp(Ity_V128);
7890 IRTemp z01 = newTemp(Ity_V128);
7891 IRTemp z2 = newTemp(Ity_V128);
7892 IRTemp z3 = newTemp(Ity_V128);
7893 IRTemp z23 = newTemp(Ity_V128);
sewardj197bd172005-10-12 11:34:33 +00007894 DIP("vupkhpx v%d,v%d\n", vD_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00007895 assign( z0, binop(Iop_ShlN16x8,
7896 binop(Iop_SarN16x8, mkexpr(vB), mkU8(15)),
7897 mkU8(8)) );
7898 assign( z1, binop(Iop_ShrN16x8,
7899 binop(Iop_ShlN16x8, mkexpr(vB), mkU8(1)),
7900 mkU8(11)) );
7901 assign( z01, binop(Iop_InterleaveHI16x8, mkexpr(zeros),
7902 binop(Iop_OrV128, mkexpr(z0), mkexpr(z1))) );
7903 assign( z2, binop(Iop_ShrN16x8,
7904 binop(Iop_ShlN16x8,
7905 binop(Iop_ShrN16x8, mkexpr(vB), mkU8(5)),
7906 mkU8(11)),
7907 mkU8(3)) );
7908 assign( z3, binop(Iop_ShrN16x8,
7909 binop(Iop_ShlN16x8, mkexpr(vB), mkU8(11)),
7910 mkU8(11)) );
7911 assign( z23, binop(Iop_InterleaveHI16x8, mkexpr(zeros),
7912 binop(Iop_OrV128, mkexpr(z2), mkexpr(z3))) );
cerion5b2325f2005-12-23 00:55:09 +00007913 putVReg( vD_addr,
7914 binop(Iop_OrV128,
7915 binop(Iop_ShlN32x4, mkexpr(z01), mkU8(16)),
7916 mkexpr(z23)) );
cerion3c052792005-09-16 07:13:44 +00007917 break;
7918 }
7919 case 0x3CE: { // vupklpx (Unpack Low Pixel16, AV p279)
cerion3c052792005-09-16 07:13:44 +00007920 /* identical to vupkhpx, except interleaving LO */
7921 IRTemp z0 = newTemp(Ity_V128);
7922 IRTemp z1 = newTemp(Ity_V128);
7923 IRTemp z01 = newTemp(Ity_V128);
7924 IRTemp z2 = newTemp(Ity_V128);
7925 IRTemp z3 = newTemp(Ity_V128);
7926 IRTemp z23 = newTemp(Ity_V128);
sewardj197bd172005-10-12 11:34:33 +00007927 DIP("vupklpx v%d,v%d\n", vD_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00007928 assign( z0, binop(Iop_ShlN16x8,
7929 binop(Iop_SarN16x8, mkexpr(vB), mkU8(15)),
7930 mkU8(8)) );
7931 assign( z1, binop(Iop_ShrN16x8,
7932 binop(Iop_ShlN16x8, mkexpr(vB), mkU8(1)),
7933 mkU8(11)) );
7934 assign( z01, binop(Iop_InterleaveLO16x8, mkexpr(zeros),
7935 binop(Iop_OrV128, mkexpr(z0), mkexpr(z1))) );
7936 assign( z2, binop(Iop_ShrN16x8,
7937 binop(Iop_ShlN16x8,
7938 binop(Iop_ShrN16x8, mkexpr(vB), mkU8(5)),
7939 mkU8(11)),
7940 mkU8(3)) );
7941 assign( z3, binop(Iop_ShrN16x8,
7942 binop(Iop_ShlN16x8, mkexpr(vB), mkU8(11)),
7943 mkU8(11)) );
7944 assign( z23, binop(Iop_InterleaveLO16x8, mkexpr(zeros),
7945 binop(Iop_OrV128, mkexpr(z2), mkexpr(z3))) );
cerion5b2325f2005-12-23 00:55:09 +00007946 putVReg( vD_addr,
7947 binop(Iop_OrV128,
7948 binop(Iop_ShlN32x4, mkexpr(z01), mkU8(16)),
7949 mkexpr(z23)) );
cerion3c052792005-09-16 07:13:44 +00007950 break;
7951 }
cerion32aad402005-09-10 12:02:24 +00007952 default:
cerion5b2325f2005-12-23 00:55:09 +00007953 vex_printf("dis_av_pack(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00007954 return False;
7955 }
7956 return True;
7957}
7958
7959
7960/*
7961 AltiVec Floating Point Arithmetic Instructions
7962*/
7963static Bool dis_av_fp_arith ( UInt theInstr )
7964{
cerion76de5cf2005-11-18 18:25:12 +00007965 /* VA-Form */
7966 UChar opc1 = ifieldOPC(theInstr);
7967 UChar vD_addr = ifieldRegDS(theInstr);
7968 UChar vA_addr = ifieldRegA(theInstr);
7969 UChar vB_addr = ifieldRegB(theInstr);
7970 UChar vC_addr = ifieldRegC(theInstr);
cerion32aad402005-09-10 12:02:24 +00007971 UInt opc2=0;
7972
cerion8ea0d3e2005-11-14 00:44:47 +00007973 IRTemp vA = newTemp(Ity_V128);
7974 IRTemp vB = newTemp(Ity_V128);
7975 IRTemp vC = newTemp(Ity_V128);
7976 assign( vA, getVReg(vA_addr));
7977 assign( vB, getVReg(vB_addr));
7978 assign( vC, getVReg(vC_addr));
7979
cerion32aad402005-09-10 12:02:24 +00007980 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00007981 vex_printf("dis_av_fp_arith(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00007982 return False;
7983 }
7984
cerion76de5cf2005-11-18 18:25:12 +00007985 opc2 = IFIELD( theInstr, 0, 6 );
cerion32aad402005-09-10 12:02:24 +00007986 switch (opc2) {
7987 case 0x2E: // vmaddfp (Multiply Add FP, AV p177)
cerion5b2325f2005-12-23 00:55:09 +00007988 DIP("vmaddfp v%d,v%d,v%d,v%d\n",
7989 vD_addr, vA_addr, vC_addr, vB_addr);
7990 putVReg( vD_addr,
7991 binop(Iop_Add32Fx4, mkexpr(vB),
7992 binop(Iop_Mul32Fx4, mkexpr(vA), mkexpr(vC))) );
cerionf3f173c2005-11-14 02:37:44 +00007993 return True;
cerion32aad402005-09-10 12:02:24 +00007994
cerionf3f173c2005-11-14 02:37:44 +00007995 case 0x2F: { // vnmsubfp (Negative Multiply-Subtract FP, AV p215)
cerion5b2325f2005-12-23 00:55:09 +00007996 DIP("vnmsubfp v%d,v%d,v%d,v%d\n",
7997 vD_addr, vA_addr, vC_addr, vB_addr);
7998 putVReg( vD_addr,
7999 binop(Iop_Sub32Fx4,
8000 mkexpr(vB),
8001 binop(Iop_Mul32Fx4, mkexpr(vA), mkexpr(vC))) );
cerionf3f173c2005-11-14 02:37:44 +00008002 return True;
8003 }
cerion32aad402005-09-10 12:02:24 +00008004
8005 default:
8006 break; // Fall through...
8007 }
8008
cerion76de5cf2005-11-18 18:25:12 +00008009 opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00008010 switch (opc2) {
8011 case 0x00A: // vaddfp (Add FP, AV p137)
8012 DIP("vaddfp v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008013 putVReg( vD_addr, binop(Iop_Add32Fx4, mkexpr(vA), mkexpr(vB)) );
8014 return True;
cerion32aad402005-09-10 12:02:24 +00008015
8016 case 0x04A: // vsubfp (Subtract FP, AV p261)
8017 DIP("vsubfp v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008018 putVReg( vD_addr, binop(Iop_Sub32Fx4, mkexpr(vA), mkexpr(vB)) );
8019 return True;
cerion32aad402005-09-10 12:02:24 +00008020
8021 case 0x40A: // vmaxfp (Maximum FP, AV p178)
8022 DIP("vmaxfp v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008023 putVReg( vD_addr, binop(Iop_Max32Fx4, mkexpr(vA), mkexpr(vB)) );
8024 return True;
cerion32aad402005-09-10 12:02:24 +00008025
8026 case 0x44A: // vminfp (Minimum FP, AV p187)
8027 DIP("vminfp v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008028 putVReg( vD_addr, binop(Iop_Min32Fx4, mkexpr(vA), mkexpr(vB)) );
8029 return True;
cerion32aad402005-09-10 12:02:24 +00008030
8031 default:
8032 break; // Fall through...
8033 }
8034
8035
8036 if (vA_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00008037 vex_printf("dis_av_fp_arith(ppc)(vA_addr)\n");
cerion32aad402005-09-10 12:02:24 +00008038 return False;
8039 }
8040
8041 switch (opc2) {
8042 case 0x10A: // vrefp (Reciprocal Esimate FP, AV p228)
8043 DIP("vrefp v%d,v%d\n", vD_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008044 putVReg( vD_addr, unop(Iop_Recip32Fx4, mkexpr(vB)) );
8045 return True;
cerion32aad402005-09-10 12:02:24 +00008046
cerion5b2325f2005-12-23 00:55:09 +00008047 case 0x14A: // vrsqrtefp (Reciprocal Sqrt Estimate FP, AV p237)
cerion32aad402005-09-10 12:02:24 +00008048 DIP("vrsqrtefp v%d,v%d\n", vD_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008049 putVReg( vD_addr, unop(Iop_RSqrt32Fx4, mkexpr(vB)) );
8050 return True;
cerion32aad402005-09-10 12:02:24 +00008051
8052 case 0x18A: // vexptefp (2 Raised to the Exp Est FP, AV p173)
8053 DIP("vexptefp v%d,v%d\n", vD_addr, vB_addr);
8054 DIP(" => not implemented\n");
8055 return False;
8056
8057 case 0x1CA: // vlogefp (Log2 Estimate FP, AV p175)
8058 DIP("vlogefp v%d,v%d\n", vD_addr, vB_addr);
8059 DIP(" => not implemented\n");
8060 return False;
8061
8062 default:
cerion5b2325f2005-12-23 00:55:09 +00008063 vex_printf("dis_av_fp_arith(ppc)(opc2=0x%x)\n",opc2);
cerion32aad402005-09-10 12:02:24 +00008064 return False;
8065 }
8066 return True;
8067}
8068
8069/*
8070 AltiVec Floating Point Compare Instructions
8071*/
8072static Bool dis_av_fp_cmp ( UInt theInstr )
8073{
cerion76de5cf2005-11-18 18:25:12 +00008074 /* VXR-Form */
8075 UChar opc1 = ifieldOPC(theInstr);
8076 UChar vD_addr = ifieldRegDS(theInstr);
8077 UChar vA_addr = ifieldRegA(theInstr);
8078 UChar vB_addr = ifieldRegB(theInstr);
8079 UChar flag_rC = ifieldBIT10(theInstr);
8080 UInt opc2 = IFIELD( theInstr, 0, 10 );
cerion32aad402005-09-10 12:02:24 +00008081
cerion8ea0d3e2005-11-14 00:44:47 +00008082 Bool cmp_bounds = False;
8083
8084 IRTemp vA = newTemp(Ity_V128);
8085 IRTemp vB = newTemp(Ity_V128);
8086 IRTemp vD = newTemp(Ity_V128);
8087 assign( vA, getVReg(vA_addr));
8088 assign( vB, getVReg(vB_addr));
8089
cerion32aad402005-09-10 12:02:24 +00008090 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00008091 vex_printf("dis_av_fp_cmp(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00008092 return False;
8093 }
8094
8095 switch (opc2) {
8096 case 0x0C6: // vcmpeqfp (Compare Equal-to FP, AV p159)
cerion5b2325f2005-12-23 00:55:09 +00008097 DIP("vcmpeqfp%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
8098 vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008099 assign( vD, binop(Iop_CmpEQ32Fx4, mkexpr(vA), mkexpr(vB)) );
8100 break;
cerion32aad402005-09-10 12:02:24 +00008101
cerion5b2325f2005-12-23 00:55:09 +00008102 case 0x1C6: // vcmpgefp (Compare Greater-than-or-Equal-to, AV p163)
8103 DIP("vcmpgefp%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
8104 vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008105 assign( vD, binop(Iop_CmpGE32Fx4, mkexpr(vA), mkexpr(vB)) );
8106 break;
cerion32aad402005-09-10 12:02:24 +00008107
8108 case 0x2C6: // vcmpgtfp (Compare Greater-than FP, AV p164)
cerion5b2325f2005-12-23 00:55:09 +00008109 DIP("vcmpgtfp%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
8110 vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008111 assign( vD, binop(Iop_CmpGT32Fx4, mkexpr(vA), mkexpr(vB)) );
8112 break;
cerion32aad402005-09-10 12:02:24 +00008113
cerion8ea0d3e2005-11-14 00:44:47 +00008114 case 0x3C6: { // vcmpbfp (Compare Bounds FP, AV p157)
8115 IRTemp gt = newTemp(Ity_V128);
8116 IRTemp lt = newTemp(Ity_V128);
8117 IRTemp zeros = newTemp(Ity_V128);
cerion5b2325f2005-12-23 00:55:09 +00008118 DIP("vcmpbfp%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
8119 vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008120 cmp_bounds = True;
8121 assign( zeros, unop(Iop_Dup32x4, mkU32(0)) );
8122
8123 /* Note: making use of fact that the ppc backend for compare insns
cerion5b2325f2005-12-23 00:55:09 +00008124 return zero'd lanes if either of the corresponding arg lanes is
8125 a nan.
cerion8ea0d3e2005-11-14 00:44:47 +00008126
8127 Perhaps better to have an irop Iop_isNan32Fx4, but then we'd
8128 need this for the other compares too (vcmpeqfp etc)...
8129 Better still, tighten down the spec for compare irops.
8130 */
8131 assign( gt, unop(Iop_NotV128,
8132 binop(Iop_CmpLE32Fx4, mkexpr(vA), mkexpr(vB))) );
8133 assign( lt, unop(Iop_NotV128,
8134 binop(Iop_CmpGE32Fx4, mkexpr(vA),
cerion5b2325f2005-12-23 00:55:09 +00008135 binop(Iop_Sub32Fx4, mkexpr(zeros),
8136 mkexpr(vB)))) );
cerion8ea0d3e2005-11-14 00:44:47 +00008137
8138 // finally, just shift gt,lt to correct position
8139 assign( vD, binop(Iop_ShlN32x4,
8140 binop(Iop_OrV128,
8141 binop(Iop_AndV128, mkexpr(gt),
8142 unop(Iop_Dup32x4, mkU32(0x2))),
8143 binop(Iop_AndV128, mkexpr(lt),
8144 unop(Iop_Dup32x4, mkU32(0x1)))),
8145 mkU8(30)) );
8146 break;
8147 }
cerion32aad402005-09-10 12:02:24 +00008148
8149 default:
cerion5b2325f2005-12-23 00:55:09 +00008150 vex_printf("dis_av_fp_cmp(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00008151 return False;
8152 }
cerion8ea0d3e2005-11-14 00:44:47 +00008153
8154 putVReg( vD_addr, mkexpr(vD) );
8155
cerion76de5cf2005-11-18 18:25:12 +00008156 if (flag_rC) {
cerion8ea0d3e2005-11-14 00:44:47 +00008157 set_AV_CR6( mkexpr(vD), !cmp_bounds );
8158 }
cerion32aad402005-09-10 12:02:24 +00008159 return True;
8160}
8161
8162/*
8163 AltiVec Floating Point Convert/Round Instructions
8164*/
8165static Bool dis_av_fp_convert ( UInt theInstr )
8166{
cerion76de5cf2005-11-18 18:25:12 +00008167 /* VX-Form */
8168 UChar opc1 = ifieldOPC(theInstr);
8169 UChar vD_addr = ifieldRegDS(theInstr);
8170 UChar UIMM_5 = ifieldRegA(theInstr);
8171 UChar vB_addr = ifieldRegB(theInstr);
8172 UInt opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00008173
cerion76de5cf2005-11-18 18:25:12 +00008174 IRTemp vB = newTemp(Ity_V128);
8175 IRTemp vScale = newTemp(Ity_V128);
ceriond963eb42005-11-16 18:02:58 +00008176 IRTemp vInvScale = newTemp(Ity_V128);
sewardj41a7b702005-11-18 22:18:23 +00008177
8178 float scale, inv_scale;
8179
ceriond963eb42005-11-16 18:02:58 +00008180 assign( vB, getVReg(vB_addr));
8181
8182 /* scale = 2^UIMM, cast to float, reinterpreted as uint */
sewardj41a7b702005-11-18 22:18:23 +00008183 scale = (float)( (unsigned int) 1<<UIMM_5 );
sewardj2ead5222005-11-23 03:53:45 +00008184 assign( vScale, unop(Iop_Dup32x4, mkU32( float_to_bits(scale) )) );
sewardj41a7b702005-11-18 22:18:23 +00008185 inv_scale = 1/scale;
cerion5b2325f2005-12-23 00:55:09 +00008186 assign( vInvScale,
8187 unop(Iop_Dup32x4, mkU32( float_to_bits(inv_scale) )) );
ceriond963eb42005-11-16 18:02:58 +00008188
cerion32aad402005-09-10 12:02:24 +00008189 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00008190 vex_printf("dis_av_fp_convert(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00008191 return False;
8192 }
8193
8194 switch (opc2) {
8195 case 0x30A: // vcfux (Convert from Unsigned Fixed-Point W, AV p156)
8196 DIP("vcfux v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
ceriond963eb42005-11-16 18:02:58 +00008197 putVReg( vD_addr, binop(Iop_Mul32Fx4,
8198 unop(Iop_I32UtoFx4, mkexpr(vB)),
8199 mkexpr(vInvScale)) );
8200 return True;
cerion32aad402005-09-10 12:02:24 +00008201
8202 case 0x34A: // vcfsx (Convert from Signed Fixed-Point W, AV p155)
8203 DIP("vcfsx v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
ceriond963eb42005-11-16 18:02:58 +00008204
8205 putVReg( vD_addr, binop(Iop_Mul32Fx4,
8206 unop(Iop_I32StoFx4, mkexpr(vB)),
8207 mkexpr(vInvScale)) );
8208 return True;
cerion32aad402005-09-10 12:02:24 +00008209
8210 case 0x38A: // vctuxs (Convert to Unsigned Fixed-Point W Saturate, AV p172)
8211 DIP("vctuxs v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
ceriond963eb42005-11-16 18:02:58 +00008212 putVReg( vD_addr,
8213 unop(Iop_QFtoI32Ux4_RZ,
8214 binop(Iop_Mul32Fx4, mkexpr(vB), mkexpr(vScale))) );
8215 return True;
cerion32aad402005-09-10 12:02:24 +00008216
8217 case 0x3CA: // vctsxs (Convert to Signed Fixed-Point W Saturate, AV p171)
8218 DIP("vctsxs v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
ceriond963eb42005-11-16 18:02:58 +00008219 putVReg( vD_addr,
8220 unop(Iop_QFtoI32Sx4_RZ,
8221 binop(Iop_Mul32Fx4, mkexpr(vB), mkexpr(vScale))) );
8222 return True;
cerion32aad402005-09-10 12:02:24 +00008223
8224 default:
8225 break; // Fall through...
8226 }
8227
8228 if (UIMM_5 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00008229 vex_printf("dis_av_fp_convert(ppc)(UIMM_5)\n");
cerion32aad402005-09-10 12:02:24 +00008230 return False;
8231 }
8232
8233 switch (opc2) {
8234 case 0x20A: // vrfin (Round to FP Integer Nearest, AV p231)
8235 DIP("vrfin v%d,v%d\n", vD_addr, vB_addr);
ceriond963eb42005-11-16 18:02:58 +00008236 putVReg( vD_addr, unop(Iop_RoundF32x4_RN, mkexpr(vB)) );
8237 break;
cerion32aad402005-09-10 12:02:24 +00008238
8239 case 0x24A: // vrfiz (Round to FP Integer toward zero, AV p233)
8240 DIP("vrfiz v%d,v%d\n", vD_addr, vB_addr);
ceriond963eb42005-11-16 18:02:58 +00008241 putVReg( vD_addr, unop(Iop_RoundF32x4_RZ, mkexpr(vB)) );
8242 break;
cerion32aad402005-09-10 12:02:24 +00008243
8244 case 0x28A: // vrfip (Round to FP Integer toward +inf, AV p232)
8245 DIP("vrfip v%d,v%d\n", vD_addr, vB_addr);
ceriond963eb42005-11-16 18:02:58 +00008246 putVReg( vD_addr, unop(Iop_RoundF32x4_RP, mkexpr(vB)) );
8247 break;
cerion32aad402005-09-10 12:02:24 +00008248
8249 case 0x2CA: // vrfim (Round to FP Integer toward -inf, AV p230)
8250 DIP("vrfim v%d,v%d\n", vD_addr, vB_addr);
ceriond963eb42005-11-16 18:02:58 +00008251 putVReg( vD_addr, unop(Iop_RoundF32x4_RM, mkexpr(vB)) );
8252 break;
cerion32aad402005-09-10 12:02:24 +00008253
8254 default:
cerion5b2325f2005-12-23 00:55:09 +00008255 vex_printf("dis_av_fp_convert(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00008256 return False;
8257 }
8258 return True;
8259}
cerion3d870a32005-03-18 12:23:33 +00008260
8261
cerion91ad5362005-01-27 23:02:41 +00008262
8263
8264
8265
cerion896a1372005-01-25 12:24:25 +00008266/*------------------------------------------------------------*/
8267/*--- Disassemble a single instruction ---*/
8268/*------------------------------------------------------------*/
8269
8270/* Disassemble a single instruction into IR. The instruction
sewardj9e6491a2005-07-02 19:24:10 +00008271 is located in host memory at &guest_code[delta]. */
8272
8273static
cerion5b2325f2005-12-23 00:55:09 +00008274DisResult disInstr_PPC_WRK (
sewardj9e6491a2005-07-02 19:24:10 +00008275 Bool put_IP,
8276 Bool (*resteerOkFn) ( Addr64 ),
8277 Long delta64,
8278 VexArchInfo* archinfo
8279 )
cerion896a1372005-01-25 12:24:25 +00008280{
sewardj9e6491a2005-07-02 19:24:10 +00008281 UChar opc1;
8282 UInt opc2;
8283 DisResult dres;
cerion896a1372005-01-25 12:24:25 +00008284 UInt theInstr;
ceriond953ebb2005-11-29 13:27:20 +00008285 IRType ty = mode64 ? Ity_I64 : Ity_I32;
cerion896a1372005-01-25 12:24:25 +00008286
sewardj059601a2005-11-13 00:53:05 +00008287 /* What insn variants are we supporting today? */
cerionf0de28c2005-12-13 20:21:11 +00008288 Bool allow_FP = archinfo->subarch == VexSubArchPPC32_FI ||
8289 archinfo->subarch == VexSubArchPPC32_VFI ||
8290 archinfo->subarch == VexSubArchPPC64_FI ||
8291 archinfo->subarch == VexSubArchPPC64_VFI;
sewardj059601a2005-11-13 00:53:05 +00008292
cerionf0de28c2005-12-13 20:21:11 +00008293 Bool allow_VMX = archinfo->subarch == VexSubArchPPC32_VFI ||
8294 archinfo->subarch == VexSubArchPPC64_VFI;
sewardj059601a2005-11-13 00:53:05 +00008295
sewardj9e6491a2005-07-02 19:24:10 +00008296 /* The running delta */
cerion2831b002005-11-30 19:55:22 +00008297 Long delta = (Long)mkSzAddr(ty, (ULong)delta64);
sewardj9e6491a2005-07-02 19:24:10 +00008298
8299 /* Set result defaults. */
8300 dres.whatNext = Dis_Continue;
8301 dres.len = 0;
8302 dres.continueAt = 0;
cerion896a1372005-01-25 12:24:25 +00008303
cerion1515db92005-01-25 17:21:23 +00008304 /* At least this is simple on PPC32: insns are all 4 bytes long, and
cerion896a1372005-01-25 12:24:25 +00008305 4-aligned. So just fish the whole thing out of memory right now
8306 and have done. */
cerioncf004462005-01-31 15:24:55 +00008307 theInstr = getUIntBigendianly( (UChar*)(&guest_code[delta]) );
cerion896a1372005-01-25 12:24:25 +00008308
cerionf0de28c2005-12-13 20:21:11 +00008309// vex_printf("insn: 0x%x\n", theInstr);
8310
ceriond953ebb2005-11-29 13:27:20 +00008311 if (mode64) {
8312 DIP("\t0x%llx: ", guest_CIA_curr_instr);
8313 } else {
8314 DIP("\t0x%x: ", (Addr32)guest_CIA_curr_instr);
8315 }
sewardjb51f0f42005-07-18 11:38:02 +00008316
8317 /* We may be asked to update the guest CIA before going further. */
8318 if (put_IP)
cerion2831b002005-11-30 19:55:22 +00008319 putGST( PPC_GST_CIA, mkSzImm(ty, guest_CIA_curr_instr) );
cerion896a1372005-01-25 12:24:25 +00008320
cerion896a1372005-01-25 12:24:25 +00008321 /* Spot the client-request magic sequence. */
8322 // Essentially a v. unlikely sequence of noops that we can catch
8323 {
sewardj2f52de42005-07-03 01:51:29 +00008324 UChar* code = (UChar*)(&guest_code[delta]);
cerion896a1372005-01-25 12:24:25 +00008325
8326 /* Spot this:
sewardj2f52de42005-07-03 01:51:29 +00008327 0x7C03D808 tw 0,3,27 => trap word if (0) => nop
cerionb85e8bb2005-02-16 08:54:33 +00008328 0x5400E800 rlwinm 0,0,29,0,0 => r0 = rotl(r0,29)
ceriond953ebb2005-11-29 13:27:20 +00008329 0x54001800 rlwinm 0,0, 3,0,0 => r0 = rotl(r0, 3)
cerionb85e8bb2005-02-16 08:54:33 +00008330 0x54006800 rlwinm 0,0,13,0,0 => r0 = rotl(r0,13)
8331 0x54009800 rlwinm 0,0,19,0,0 => r0 = rotl(r0,19)
cerion0fe6b7e2005-06-20 16:28:32 +00008332 0x60000000 nop
cerion896a1372005-01-25 12:24:25 +00008333 */
sewardj2f52de42005-07-03 01:51:29 +00008334 if (getUIntBigendianly(code+ 0) == 0x7C03D808 &&
8335 getUIntBigendianly(code+ 4) == 0x5400E800 &&
8336 getUIntBigendianly(code+ 8) == 0x54001800 &&
8337 getUIntBigendianly(code+12) == 0x54006800 &&
8338 getUIntBigendianly(code+16) == 0x54009800 &&
8339 getUIntBigendianly(code+20) == 0x60000000) {
cerion84ad6162005-06-23 15:25:57 +00008340 DIP("%%r3 = client_request ( %%r31 )\n");
sewardj9e6491a2005-07-02 19:24:10 +00008341 dres.len = 24;
cerioned623db2005-06-20 12:42:04 +00008342 delta += 24;
8343
cerion2831b002005-11-30 19:55:22 +00008344 irbb->next = mkSzImm( ty, guest_CIA_bbstart + delta );
cerionb85e8bb2005-02-16 08:54:33 +00008345 irbb->jumpkind = Ijk_ClientReq;
sewardj9e6491a2005-07-02 19:24:10 +00008346 dres.whatNext = Dis_StopHere;
cerion896a1372005-01-25 12:24:25 +00008347 goto decode_success;
8348 }
8349 }
8350
cerion76de5cf2005-11-18 18:25:12 +00008351 opc1 = ifieldOPC(theInstr);
sewardjb51f0f42005-07-18 11:38:02 +00008352 opc2 = ifieldOPClo10(theInstr);
cerion932ad942005-01-30 10:18:50 +00008353
cerion91ad5362005-01-27 23:02:41 +00008354 // Note: all 'reserved' bits must be cleared, else invalid
8355 switch (opc1) {
cerion896a1372005-01-25 12:24:25 +00008356
cerione9d361a2005-03-04 17:35:29 +00008357 /* Integer Arithmetic Instructions */
8358 case 0x0C: case 0x0D: case 0x0E: // addic, addic., addi
8359 case 0x0F: case 0x07: case 0x08: // addis, mulli, subfic
8360 if (dis_int_arith( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008361 goto decode_failure;
cerion7aa4bbc2005-01-29 09:32:07 +00008362
cerione9d361a2005-03-04 17:35:29 +00008363 /* Integer Compare Instructions */
8364 case 0x0B: case 0x0A: // cmpi, cmpli
8365 if (dis_int_cmp( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008366 goto decode_failure;
cerion7aa4bbc2005-01-29 09:32:07 +00008367
cerione9d361a2005-03-04 17:35:29 +00008368 /* Integer Logical Instructions */
8369 case 0x1C: case 0x1D: case 0x18: // andi., andis., ori
8370 case 0x19: case 0x1A: case 0x1B: // oris, xori, xoris
8371 if (dis_int_logic( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008372 goto decode_failure;
cerion7aa4bbc2005-01-29 09:32:07 +00008373
cerione9d361a2005-03-04 17:35:29 +00008374 /* Integer Rotate Instructions */
8375 case 0x14: case 0x15: case 0x17: // rlwimi, rlwinm, rlwnm
8376 if (dis_int_rot( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008377 goto decode_failure;
cerion645c9302005-01-31 10:09:59 +00008378
cerionf0de28c2005-12-13 20:21:11 +00008379 /* 64bit Integer Rotate Instructions */
8380 case 0x1E: // rldcl, rldcr, rldic, rldicl, rldicr, rldimi
8381 if (dis_int_rot( theInstr )) goto decode_success;
8382 goto decode_failure;
8383
cerione9d361a2005-03-04 17:35:29 +00008384 /* Integer Load Instructions */
8385 case 0x22: case 0x23: case 0x2A: // lbz, lbzu, lha
8386 case 0x2B: case 0x28: case 0x29: // lhau, lhz, lhzu
8387 case 0x20: case 0x21: // lwz, lwzu
8388 if (dis_int_load( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008389 goto decode_failure;
cerion645c9302005-01-31 10:09:59 +00008390
cerione9d361a2005-03-04 17:35:29 +00008391 /* Integer Store Instructions */
8392 case 0x26: case 0x27: case 0x2C: // stb, stbu, sth
8393 case 0x2D: case 0x24: case 0x25: // sthu, stw, stwu
8394 if (dis_int_store( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008395 goto decode_failure;
ceriond23be4e2005-01-31 07:23:07 +00008396
sewardj7787af42005-08-04 18:32:19 +00008397 /* Integer Load and Store Multiple Instructions */
8398 case 0x2E: case 0x2F: // lmw, stmw
8399 if (dis_int_ldst_mult( theInstr )) goto decode_success;
8400 goto decode_failure;
cerion645c9302005-01-31 10:09:59 +00008401
cerione9d361a2005-03-04 17:35:29 +00008402 /* Branch Instructions */
8403 case 0x12: case 0x10: // b, bc
sewardj9d540e52005-10-08 11:28:16 +00008404 if (dis_branch(theInstr, &dres, resteerOkFn)) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008405 goto decode_failure;
cerion896a1372005-01-25 12:24:25 +00008406
cerione9d361a2005-03-04 17:35:29 +00008407 /* System Linkage Instructions */
cerion8c3adda2005-01-31 11:54:05 +00008408 case 0x11: // sc
sewardj9e6491a2005-07-02 19:24:10 +00008409 if (dis_syslink(theInstr, &dres)) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008410 goto decode_failure;
cerion26d07b22005-02-02 17:13:28 +00008411
sewardjb51f0f42005-07-18 11:38:02 +00008412//zz /* Trap Instructions */
cerionf0de28c2005-12-13 20:21:11 +00008413//zz case 0x02: // tdi
8414//zz DIP("trap op (tdi) => not implemented\n");
8415//zz goto decode_failure;
sewardjb51f0f42005-07-18 11:38:02 +00008416//zz case 0x03: // twi
8417//zz DIP("trap op (twi) => not implemented\n");
8418//zz goto decode_failure;
cerion8c3adda2005-01-31 11:54:05 +00008419
cerion3d870a32005-03-18 12:23:33 +00008420 /* Floating Point Load Instructions */
cerion094d1392005-06-20 13:45:57 +00008421 case 0x30: case 0x31: case 0x32: // lfs, lfsu, lfd
8422 case 0x33: // lfdu
cerion5b2325f2005-12-23 00:55:09 +00008423 if (!allow_FP) goto decode_noFP;
cerion3d870a32005-03-18 12:23:33 +00008424 if (dis_fp_load( theInstr )) goto decode_success;
cerione9d361a2005-03-04 17:35:29 +00008425 goto decode_failure;
cerion995bc362005-02-03 11:03:31 +00008426
cerion3d870a32005-03-18 12:23:33 +00008427 /* Floating Point Store Instructions */
8428 case 0x34: case 0x35: case 0x36: // stfsx, stfsux, stfdx
8429 case 0x37: // stfdux
cerion5b2325f2005-12-23 00:55:09 +00008430 if (!allow_FP) goto decode_noFP;
cerion3d870a32005-03-18 12:23:33 +00008431 if (dis_fp_store( theInstr )) goto decode_success;
8432 goto decode_failure;
8433
cerionf0de28c2005-12-13 20:21:11 +00008434 /* 64bit Integer Loads */
8435 case 0x3A: // ld, ldu, lwa
8436 if (!mode64) goto decode_failure;
8437 if (dis_int_load( theInstr )) goto decode_success;
8438 goto decode_failure;
8439
sewardje14bb9f2005-07-22 09:39:02 +00008440 case 0x3B:
cerion5b2325f2005-12-23 00:55:09 +00008441 if (!allow_FP) goto decode_noFP;
cerion76de5cf2005-11-18 18:25:12 +00008442
8443 opc2 = IFIELD(theInstr, 1, 5);
sewardje14bb9f2005-07-22 09:39:02 +00008444 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00008445 /* Floating Point Arith Instructions */
8446 case 0x12: case 0x14: case 0x15: // fdivs, fsubs, fadds
8447 case 0x16: case 0x18: case 0x19: // fsqrts, fres, fmuls
8448 if (dis_fp_arith(theInstr)) goto decode_success;
8449 goto decode_failure;
8450
8451 /* Floating Point Mult-Add Instructions */
8452 case 0x1C: case 0x1D: case 0x1E: // fmsubs, fmadds, fnmsubs
8453 case 0x1F: // fnmadds
8454 if (dis_fp_multadd(theInstr)) goto decode_success;
8455 goto decode_failure;
8456
8457 default:
8458 goto decode_failure;
sewardje14bb9f2005-07-22 09:39:02 +00008459 }
8460 break;
cerion3d870a32005-03-18 12:23:33 +00008461
cerionf0de28c2005-12-13 20:21:11 +00008462 /* 64bit Integer Stores */
8463 case 0x3E: // std, stdu
8464 if (!mode64) goto decode_failure;
8465 if (dis_int_store( theInstr )) goto decode_success;
8466 goto decode_failure;
8467
cerion3d870a32005-03-18 12:23:33 +00008468 case 0x3F:
cerion5b2325f2005-12-23 00:55:09 +00008469 if (!allow_FP) goto decode_noFP;
8470 /* Instrs using opc[1:5] never overlap instrs using opc[1:10],
cerion3d870a32005-03-18 12:23:33 +00008471 so we can simply fall through the first switch statement */
8472
cerion76de5cf2005-11-18 18:25:12 +00008473 opc2 = IFIELD(theInstr, 1, 5);
cerion3d870a32005-03-18 12:23:33 +00008474 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00008475 /* Floating Point Arith Instructions */
8476 case 0x12: case 0x14: case 0x15: // fdiv, fsub, fadd
8477 case 0x16: case 0x17: case 0x19: // fsqrt, fsel, fmul
8478 case 0x1A: // frsqrte
8479 if (dis_fp_arith(theInstr)) goto decode_success;
8480 goto decode_failure;
sewardje14bb9f2005-07-22 09:39:02 +00008481
ceriond953ebb2005-11-29 13:27:20 +00008482 /* Floating Point Mult-Add Instructions */
8483 case 0x1C: case 0x1D: case 0x1E: // fmsub, fmadd, fnmsub
8484 case 0x1F: // fnmadd
8485 if (dis_fp_multadd(theInstr)) goto decode_success;
8486 goto decode_failure;
8487
8488 default:
8489 break; // Fall through
cerion3d870a32005-03-18 12:23:33 +00008490 }
8491
cerion76de5cf2005-11-18 18:25:12 +00008492 opc2 = IFIELD(theInstr, 1, 10);
sewardje14bb9f2005-07-22 09:39:02 +00008493 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00008494 /* Floating Point Compare Instructions */
8495 case 0x000: // fcmpu
8496 case 0x020: // fcmpo
8497 if (dis_fp_cmp(theInstr)) goto decode_success;
8498 goto decode_failure;
cerion2831b002005-11-30 19:55:22 +00008499
ceriond953ebb2005-11-29 13:27:20 +00008500 /* Floating Point Rounding/Conversion Instructions */
8501 case 0x00C: // frsp
8502 case 0x00E: // fctiw
8503 case 0x00F: // fctiwz
8504 if (dis_fp_round(theInstr)) goto decode_success;
8505 goto decode_failure;
8506
8507 /* Floating Point Move Instructions */
8508 case 0x028: // fneg
8509 case 0x048: // fmr
8510 case 0x088: // fnabs
8511 case 0x108: // fabs
8512 if (dis_fp_move( theInstr )) goto decode_success;
8513 goto decode_failure;
sewardje14bb9f2005-07-22 09:39:02 +00008514
ceriond953ebb2005-11-29 13:27:20 +00008515 /* Floating Point Status/Control Register Instructions */
sewardjb51f0f42005-07-18 11:38:02 +00008516//zz case 0x026: // mtfsb1
8517//zz case 0x040: // mcrfs
ceriond953ebb2005-11-29 13:27:20 +00008518 case 0x046: // mtfsb0
8519 case 0x086: // mtfsfi
8520 case 0x247: // mffs
8521 case 0x2C7: // mtfsf
8522 if (dis_fp_scr( theInstr )) goto decode_success;
8523 goto decode_failure;
cerionf0de28c2005-12-13 20:21:11 +00008524
8525 /* 64bit FP conversions */
8526 case 0x32E: // fctid
8527 case 0x32F: // fctidz
8528 case 0x34E: // fcfid
8529 if (!mode64) goto decode_failure;
8530 if (dis_fp_round(theInstr)) goto decode_success;
8531 goto decode_failure;
8532
ceriond953ebb2005-11-29 13:27:20 +00008533 default:
8534 goto decode_failure;
sewardje14bb9f2005-07-22 09:39:02 +00008535 }
cerion3d870a32005-03-18 12:23:33 +00008536 break;
ceriond953ebb2005-11-29 13:27:20 +00008537
cerion91ad5362005-01-27 23:02:41 +00008538 case 0x13:
cerionb85e8bb2005-02-16 08:54:33 +00008539 switch (opc2) {
cerion91ad5362005-01-27 23:02:41 +00008540
ceriond953ebb2005-11-29 13:27:20 +00008541 /* Condition Register Logical Instructions */
8542 case 0x101: case 0x081: case 0x121: // crand, crandc, creqv
8543 case 0x0E1: case 0x021: case 0x1C1: // crnand, crnor, cror
8544 case 0x1A1: case 0x0C1: case 0x000: // crorc, crxor, mcrf
8545 if (dis_cond_logic( theInstr )) goto decode_success;
8546 goto decode_failure;
cerionb85e8bb2005-02-16 08:54:33 +00008547
ceriond953ebb2005-11-29 13:27:20 +00008548 /* Branch Instructions */
8549 case 0x210: case 0x010: // bcctr, bclr
8550 if (dis_branch(theInstr, &dres, resteerOkFn)) goto decode_success;
8551 goto decode_failure;
8552
8553 /* Memory Synchronization Instructions */
8554 case 0x096: // isync
8555 if (dis_memsync( theInstr )) goto decode_success;
8556 goto decode_failure;
8557
8558 default:
8559 goto decode_failure;
cerionb85e8bb2005-02-16 08:54:33 +00008560 }
8561 break;
cerion91ad5362005-01-27 23:02:41 +00008562
8563
cerionb85e8bb2005-02-16 08:54:33 +00008564 case 0x1F:
cerione9d361a2005-03-04 17:35:29 +00008565
8566 /* For arith instns, bit10 is the OE flag (overflow enable) */
8567
cerion76de5cf2005-11-18 18:25:12 +00008568 opc2 = IFIELD(theInstr, 1, 9);
cerionb85e8bb2005-02-16 08:54:33 +00008569 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00008570 /* Integer Arithmetic Instructions */
8571 case 0x10A: case 0x00A: case 0x08A: // add, addc, adde
8572 case 0x0EA: case 0x0CA: case 0x1EB: // addme, addze, divw
8573 case 0x1CB: case 0x04B: case 0x00B: // divwu, mulhw, mulhwu
8574 case 0x0EB: case 0x068: case 0x028: // mullw, neg, subf
8575 case 0x008: case 0x088: case 0x0E8: // subfc, subfe, subfme
8576 case 0x0C8: // subfze
8577 if (dis_int_arith( theInstr )) goto decode_success;
8578 goto decode_failure;
cerionf0de28c2005-12-13 20:21:11 +00008579
8580 /* 64bit Integer Arithmetic */
8581 case 0x009: case 0x049: case 0x0E9: // mulhdu, mulhd, mulld
8582 case 0x1C9: case 0x1E9: // divdu, divd
8583 if (!mode64) goto decode_failure;
8584 if (dis_int_arith( theInstr )) goto decode_success;
8585 goto decode_failure;
8586
ceriond953ebb2005-11-29 13:27:20 +00008587 default:
8588 break; // Fall through...
cerionb85e8bb2005-02-16 08:54:33 +00008589 }
cerion91ad5362005-01-27 23:02:41 +00008590
cerione9d361a2005-03-04 17:35:29 +00008591 /* All remaining opcodes use full 10 bits. */
8592
cerion76de5cf2005-11-18 18:25:12 +00008593 opc2 = IFIELD(theInstr, 1, 10);
cerionb85e8bb2005-02-16 08:54:33 +00008594 switch (opc2) {
cerione9d361a2005-03-04 17:35:29 +00008595 /* Integer Compare Instructions */
8596 case 0x000: case 0x020: // cmp, cmpl
8597 if (dis_int_cmp( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008598 goto decode_failure;
cerion7aa4bbc2005-01-29 09:32:07 +00008599
cerione9d361a2005-03-04 17:35:29 +00008600 /* Integer Logical Instructions */
8601 case 0x01C: case 0x03C: case 0x01A: // and, andc, cntlzw
8602 case 0x11C: case 0x3BA: case 0x39A: // eqv, extsb, extsh
8603 case 0x1DC: case 0x07C: case 0x1BC: // nand, nor, or
8604 case 0x19C: case 0x13C: // orc, xor
8605 if (dis_int_logic( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008606 goto decode_failure;
cerion932ad942005-01-30 10:18:50 +00008607
cerionf0de28c2005-12-13 20:21:11 +00008608 /* 64bit Integer Logical Instructions */
cerion07b07a92005-12-22 14:32:35 +00008609 case 0x3DA: case 0x03A: // extsw, cntlzd
cerionf0de28c2005-12-13 20:21:11 +00008610 if (!mode64) goto decode_failure;
8611 if (dis_int_logic( theInstr )) goto decode_success;
8612 goto decode_failure;
8613
cerione9d361a2005-03-04 17:35:29 +00008614 /* Integer Shift Instructions */
8615 case 0x018: case 0x318: case 0x338: // slw, sraw, srawi
8616 case 0x218: // srw
8617 if (dis_int_shift( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008618 goto decode_failure;
cerion7aa4bbc2005-01-29 09:32:07 +00008619
cerionf0de28c2005-12-13 20:21:11 +00008620 /* 64bit Integer Shift Instructions */
8621 case 0x01B: case 0x31A: // sld, srad
cerion07b07a92005-12-22 14:32:35 +00008622 case 0x33A: case 0x33B: // sradi
cerionf0de28c2005-12-13 20:21:11 +00008623 case 0x21B: // srd
8624 if (!mode64) goto decode_failure;
8625 if (dis_int_shift( theInstr )) goto decode_success;
8626 goto decode_failure;
8627
cerione9d361a2005-03-04 17:35:29 +00008628 /* Integer Load Instructions */
8629 case 0x057: case 0x077: case 0x157: // lbzx, lbzux, lhax
8630 case 0x177: case 0x117: case 0x137: // lhaux, lhzx, lhzux
8631 case 0x017: case 0x037: // lwzx, lwzux
8632 if (dis_int_load( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008633 goto decode_failure;
cerion7aa4bbc2005-01-29 09:32:07 +00008634
cerionf0de28c2005-12-13 20:21:11 +00008635 /* 64bit Integer Load Instructions */
8636 case 0x035: case 0x015: // ldux, ldx
8637 case 0x175: case 0x155: // lwaux, lwax
8638 if (!mode64) goto decode_failure;
8639 if (dis_int_load( theInstr )) goto decode_success;
8640 goto decode_failure;
8641
sewardjb51f0f42005-07-18 11:38:02 +00008642 /* Integer Store Instructions */
cerione9d361a2005-03-04 17:35:29 +00008643 case 0x0F7: case 0x0D7: case 0x1B7: // stbux, stbx, sthux
8644 case 0x197: case 0x0B7: case 0x097: // sthx, stwux, stwx
8645 if (dis_int_store( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008646 goto decode_failure;
cerion91ad5362005-01-27 23:02:41 +00008647
cerionf0de28c2005-12-13 20:21:11 +00008648 /* 64bit Integer Store Instructions */
8649 case 0x0B5: case 0x095: // stdux, stdx
8650 if (!mode64) goto decode_failure;
8651 if (dis_int_store( theInstr )) goto decode_success;
8652 goto decode_failure;
8653
sewardj602857d2005-09-06 09:10:09 +00008654 /* Integer Load and Store with Byte Reverse Instructions */
8655 case 0x316: case 0x216: case 0x396: // lhbrx, lwbrx, sthbrx
8656 case 0x296: // stwbrx
8657 if (dis_int_ldst_rev( theInstr )) goto decode_success;
8658 goto decode_failure;
8659
sewardj87e651f2005-09-09 08:31:18 +00008660 /* Integer Load and Store String Instructions */
8661 case 0x255: case 0x215: case 0x2D5: // lswi, lswx, stswi
8662 case 0x295: { // stswx
8663 Bool stopHere = False;
8664 Bool ok = dis_int_ldst_str( theInstr, &stopHere );
8665 if (!ok) goto decode_failure;
8666 if (stopHere) {
cerion2831b002005-11-30 19:55:22 +00008667 irbb->next = mkSzImm(ty, nextInsnAddr());
sewardj87e651f2005-09-09 08:31:18 +00008668 irbb->jumpkind = Ijk_Boring;
ceriond953ebb2005-11-29 13:27:20 +00008669 dres.whatNext = Dis_StopHere;
sewardj87e651f2005-09-09 08:31:18 +00008670 }
8671 goto decode_success;
8672 }
cerion645c9302005-01-31 10:09:59 +00008673
cerione9d361a2005-03-04 17:35:29 +00008674 /* Memory Synchronization Instructions */
8675 case 0x356: case 0x014: case 0x096: // eieio, lwarx, stwcx.
8676 case 0x256: // sync
8677 if (dis_memsync( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008678 goto decode_failure;
8679
cerionf0de28c2005-12-13 20:21:11 +00008680 /* 64bit Memory Synchronization Instructions */
8681 case 0x054: case 0x0D6: // ldarx, stdcx.
8682 if (!mode64) goto decode_failure;
8683 if (dis_memsync( theInstr )) goto decode_success;
8684 goto decode_failure;
8685
cerione9d361a2005-03-04 17:35:29 +00008686 /* Processor Control Instructions */
8687 case 0x200: case 0x013: case 0x153: // mcrxr, mfcr, mfspr
8688 case 0x173: case 0x090: case 0x1D3: // mftb, mtcrf, mtspr
8689 if (dis_proc_ctl( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008690 goto decode_failure;
cerion645c9302005-01-31 10:09:59 +00008691
cerione9d361a2005-03-04 17:35:29 +00008692 /* Cache Management Instructions */
8693 case 0x2F6: case 0x056: case 0x036: // dcba, dcbf, dcbst
8694 case 0x116: case 0x0F6: case 0x3F6: // dcbt, dcbtst, dcbz
8695 case 0x3D6: // icbi
sewardj9e6491a2005-07-02 19:24:10 +00008696 if (dis_cache_manage( theInstr, &dres, archinfo ))
sewardjd94b73a2005-06-30 12:08:48 +00008697 goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008698 goto decode_failure;
ceriond23be4e2005-01-31 07:23:07 +00008699
sewardjb51f0f42005-07-18 11:38:02 +00008700//zz /* External Control Instructions */
8701//zz case 0x136: case 0x1B6: // eciwx, ecowx
8702//zz DIP("external control op => not implemented\n");
8703//zz goto decode_failure;
8704//zz
8705//zz /* Trap Instructions */
8706//zz case 0x004: // tw
8707//zz DIP("trap op (tw) => not implemented\n");
8708//zz goto decode_failure;
cerionf0de28c2005-12-13 20:21:11 +00008709//zz case 0x044: // td
8710//zz DIP("trap op (td) => not implemented\n");
8711//zz goto decode_failure;
sewardje14bb9f2005-07-22 09:39:02 +00008712
8713 /* Floating Point Load Instructions */
8714 case 0x217: case 0x237: case 0x257: // lfsx, lfsux, lfdx
8715 case 0x277: // lfdux
cerion5b2325f2005-12-23 00:55:09 +00008716 if (!allow_FP) goto decode_noFP;
sewardje14bb9f2005-07-22 09:39:02 +00008717 if (dis_fp_load( theInstr )) goto decode_success;
8718 goto decode_failure;
8719
8720 /* Floating Point Store Instructions */
8721 case 0x297: case 0x2B7: case 0x2D7: // stfs, stfsu, stfd
8722 case 0x2F7: case 0x3D7: // stfdu, stfiwx
cerion5b2325f2005-12-23 00:55:09 +00008723 if (!allow_FP) goto decode_noFP;
sewardje14bb9f2005-07-22 09:39:02 +00008724 if (dis_fp_store( theInstr )) goto decode_success;
8725 goto decode_failure;
8726
8727
cerion32aad402005-09-10 12:02:24 +00008728 /* AltiVec instructions */
8729
8730 /* AV Cache Control - Data streams */
8731 case 0x156: case 0x176: case 0x336: // dst, dstst, dss
cerion5b2325f2005-12-23 00:55:09 +00008732 if (!allow_VMX) goto decode_noVMX;
cerion32aad402005-09-10 12:02:24 +00008733 if (dis_av_datastream( theInstr )) goto decode_success;
8734 goto decode_failure;
ceriona982c052005-06-28 17:23:09 +00008735
8736 /* AV Load */
8737 case 0x006: case 0x026: // lvsl, lvsr
8738 case 0x007: case 0x027: case 0x047: // lvebx, lvehx, lvewx
8739 case 0x067: case 0x167: // lvx, lvxl
cerion5b2325f2005-12-23 00:55:09 +00008740 if (!allow_VMX) goto decode_noVMX;
ceriona982c052005-06-28 17:23:09 +00008741 if (dis_av_load( theInstr )) goto decode_success;
8742 goto decode_failure;
8743
8744 /* AV Store */
8745 case 0x087: case 0x0A7: case 0x0C7: // stvebx, stvehx, stvewx
8746 case 0x0E7: case 0x1E7: // stvx, stvxl
cerion5b2325f2005-12-23 00:55:09 +00008747 if (!allow_VMX) goto decode_noVMX;
ceriona982c052005-06-28 17:23:09 +00008748 if (dis_av_store( theInstr )) goto decode_success;
8749 goto decode_failure;
8750
8751 default:
8752 goto decode_failure;
8753 }
8754 break;
8755
8756
cerion32aad402005-09-10 12:02:24 +00008757 case 0x04:
8758 /* AltiVec instructions */
8759
cerion76de5cf2005-11-18 18:25:12 +00008760 opc2 = IFIELD(theInstr, 0, 6);
cerion32aad402005-09-10 12:02:24 +00008761 switch (opc2) {
8762 /* AV Mult-Add, Mult-Sum */
8763 case 0x20: case 0x21: case 0x22: // vmhaddshs, vmhraddshs, vmladduhm
8764 case 0x24: case 0x25: case 0x26: // vmsumubm, vmsummbm, vmsumuhm
8765 case 0x27: case 0x28: case 0x29: // vmsumuhs, vmsumshm, vmsumshs
cerion5b2325f2005-12-23 00:55:09 +00008766 if (!allow_VMX) goto decode_noVMX;
cerion32aad402005-09-10 12:02:24 +00008767 if (dis_av_multarith( theInstr )) goto decode_success;
8768 goto decode_failure;
8769
8770 /* AV Permutations */
8771 case 0x2A: // vsel
8772 case 0x2B: // vperm
cerion32aad402005-09-10 12:02:24 +00008773 case 0x2C: // vsldoi
cerion5b2325f2005-12-23 00:55:09 +00008774 if (!allow_VMX) goto decode_noVMX;
cerion92d9d872005-09-15 21:58:50 +00008775 if (dis_av_permute( theInstr )) goto decode_success;
cerion32aad402005-09-10 12:02:24 +00008776 goto decode_failure;
8777
8778 /* AV Floating Point Mult-Add/Sub */
8779 case 0x2E: case 0x2F: // vmaddfp, vnmsubfp
cerion5b2325f2005-12-23 00:55:09 +00008780 if (!allow_VMX) goto decode_noVMX;
cerion32aad402005-09-10 12:02:24 +00008781 if (dis_av_fp_arith( theInstr )) goto decode_success;
8782 goto decode_failure;
8783
8784 default:
8785 break; // Fall through...
8786 }
8787
cerion76de5cf2005-11-18 18:25:12 +00008788 opc2 = IFIELD(theInstr, 0, 11);
cerion32aad402005-09-10 12:02:24 +00008789 switch (opc2) {
8790 /* AV Arithmetic */
8791 case 0x180: // vaddcuw
8792 case 0x000: case 0x040: case 0x080: // vaddubm, vadduhm, vadduwm
8793 case 0x200: case 0x240: case 0x280: // vaddubs, vadduhs, vadduws
8794 case 0x300: case 0x340: case 0x380: // vaddsbs, vaddshs, vaddsws
8795 case 0x580: // vsubcuw
8796 case 0x400: case 0x440: case 0x480: // vsububm, vsubuhm, vsubuwm
8797 case 0x600: case 0x640: case 0x680: // vsububs, vsubuhs, vsubuws
8798 case 0x700: case 0x740: case 0x780: // vsubsbs, vsubshs, vsubsws
8799 case 0x402: case 0x442: case 0x482: // vavgub, vavguh, vavguw
8800 case 0x502: case 0x542: case 0x582: // vavgsb, vavgsh, vavgsw
8801 case 0x002: case 0x042: case 0x082: // vmaxub, vmaxuh, vmaxuw
8802 case 0x102: case 0x142: case 0x182: // vmaxsb, vmaxsh, vmaxsw
8803 case 0x202: case 0x242: case 0x282: // vminub, vminuh, vminuw
8804 case 0x302: case 0x342: case 0x382: // vminsb, vminsh, vminsw
8805 case 0x008: case 0x048: // vmuloub, vmulouh
8806 case 0x108: case 0x148: // vmulosb, vmulosh
8807 case 0x208: case 0x248: // vmuleub, vmuleuh
8808 case 0x308: case 0x348: // vmulesb, vmulesh
8809 case 0x608: case 0x708: case 0x648: // vsum4ubs, vsum4sbs, vsum4shs
8810 case 0x688: case 0x788: // vsum2sws, vsumsws
cerion5b2325f2005-12-23 00:55:09 +00008811 if (!allow_VMX) goto decode_noVMX;
cerion32aad402005-09-10 12:02:24 +00008812 if (dis_av_arith( theInstr )) goto decode_success;
8813 goto decode_failure;
8814
8815 /* AV Rotate, Shift */
8816 case 0x004: case 0x044: case 0x084: // vrlb, vrlh, vrlw
8817 case 0x104: case 0x144: case 0x184: // vslb, vslh, vslw
8818 case 0x204: case 0x244: case 0x284: // vsrb, vsrh, vsrw
8819 case 0x304: case 0x344: case 0x384: // vsrab, vsrah, vsraw
8820 case 0x1C4: case 0x2C4: // vsl, vsr
8821 case 0x40C: case 0x44C: // vslo, vsro
cerion5b2325f2005-12-23 00:55:09 +00008822 if (!allow_VMX) goto decode_noVMX;
cerion32aad402005-09-10 12:02:24 +00008823 if (dis_av_shift( theInstr )) goto decode_success;
8824 goto decode_failure;
8825
8826 /* AV Logic */
8827 case 0x404: case 0x444: case 0x484: // vand, vandc, vor
8828 case 0x4C4: case 0x504: // vxor, vnor
cerion5b2325f2005-12-23 00:55:09 +00008829 if (!allow_VMX) goto decode_noVMX;
cerion32aad402005-09-10 12:02:24 +00008830 if (dis_av_logic( theInstr )) goto decode_success;
8831 goto decode_failure;
8832
8833 /* AV Processor Control */
8834 case 0x604: case 0x644: // mfvscr, mtvscr
cerion5b2325f2005-12-23 00:55:09 +00008835 if (!allow_VMX) goto decode_noVMX;
cerion32aad402005-09-10 12:02:24 +00008836 if (dis_av_procctl( theInstr )) goto decode_success;
8837 goto decode_failure;
8838
8839 /* AV Floating Point Arithmetic */
8840 case 0x00A: case 0x04A: // vaddfp, vsubfp
8841 case 0x10A: case 0x14A: case 0x18A: // vrefp, vrsqrtefp, vexptefp
8842 case 0x1CA: // vlogefp
8843 case 0x40A: case 0x44A: // vmaxfp, vminfp
cerion5b2325f2005-12-23 00:55:09 +00008844 if (!allow_VMX) goto decode_noVMX;
cerion32aad402005-09-10 12:02:24 +00008845 if (dis_av_fp_arith( theInstr )) goto decode_success;
8846 goto decode_failure;
8847
8848 /* AV Floating Point Round/Convert */
8849 case 0x20A: case 0x24A: case 0x28A: // vrfin, vrfiz, vrfip
8850 case 0x2CA: // vrfim
8851 case 0x30A: case 0x34A: case 0x38A: // vcfux, vcfsx, vctuxs
8852 case 0x3CA: // vctsxs
cerion5b2325f2005-12-23 00:55:09 +00008853 if (!allow_VMX) goto decode_noVMX;
cerion32aad402005-09-10 12:02:24 +00008854 if (dis_av_fp_convert( theInstr )) goto decode_success;
8855 goto decode_failure;
8856
8857 /* AV Merge, Splat */
8858 case 0x00C: case 0x04C: case 0x08C: // vmrghb, vmrghh, vmrghw
8859 case 0x10C: case 0x14C: case 0x18C: // vmrglb, vmrglh, vmrglw
8860 case 0x20C: case 0x24C: case 0x28C: // vspltb, vsplth, vspltw
8861 case 0x30C: case 0x34C: case 0x38C: // vspltisb, vspltish, vspltisw
cerion5b2325f2005-12-23 00:55:09 +00008862 if (!allow_VMX) goto decode_noVMX;
cerion32aad402005-09-10 12:02:24 +00008863 if (dis_av_permute( theInstr )) goto decode_success;
8864 goto decode_failure;
8865
8866 /* AV Pack, Unpack */
8867 case 0x00E: case 0x04E: case 0x08E: // vpkuhum, vpkuwum, vpkuhus
8868 case 0x0CE: // vpkuwus
8869 case 0x10E: case 0x14E: case 0x18E: // vpkshus, vpkswus, vpkshss
8870 case 0x1CE: // vpkswss
8871 case 0x20E: case 0x24E: case 0x28E: // vupkhsb, vupkhsh, vupklsb
8872 case 0x2CE: // vupklsh
8873 case 0x30E: case 0x34E: case 0x3CE: // vpkpx, vupkhpx, vupklpx
cerion5b2325f2005-12-23 00:55:09 +00008874 if (!allow_VMX) goto decode_noVMX;
cerion32aad402005-09-10 12:02:24 +00008875 if (dis_av_pack( theInstr )) goto decode_success;
8876 goto decode_failure;
8877
8878 default:
8879 break; // Fall through...
8880 }
8881
cerion76de5cf2005-11-18 18:25:12 +00008882 opc2 = IFIELD(theInstr, 0, 10);
cerion32aad402005-09-10 12:02:24 +00008883 switch (opc2) {
8884
8885 /* AV Compare */
8886 case 0x006: case 0x046: case 0x086: // vcmpequb, vcmpequh, vcmpequw
8887 case 0x206: case 0x246: case 0x286: // vcmpgtub, vcmpgtuh, vcmpgtuw
8888 case 0x306: case 0x346: case 0x386: // vcmpgtsb, vcmpgtsh, vcmpgtsw
cerion5b2325f2005-12-23 00:55:09 +00008889 if (!allow_VMX) goto decode_noVMX;
cerion32aad402005-09-10 12:02:24 +00008890 if (dis_av_cmp( theInstr )) goto decode_success;
8891 goto decode_failure;
8892
8893 /* AV Floating Point Compare */
8894 case 0x0C6: case 0x1C6: case 0x2C6: // vcmpeqfp, vcmpgefp, vcmpgtfp
8895 case 0x3C6: // vcmpbfp
cerion5b2325f2005-12-23 00:55:09 +00008896 if (!allow_VMX) goto decode_noVMX;
cerion32aad402005-09-10 12:02:24 +00008897 if (dis_av_fp_cmp( theInstr )) goto decode_success;
8898 goto decode_failure;
8899
8900 default:
8901 goto decode_failure;
8902 }
8903 break;
cerion7aa4bbc2005-01-29 09:32:07 +00008904
cerion896a1372005-01-25 12:24:25 +00008905 default:
cerion5b2325f2005-12-23 00:55:09 +00008906 decode_noFP:
8907 vassert(!allow_FP);
8908 vex_printf("disInstr(ppc): Floating Point insns disabled for this arch.\n");
8909 goto decode_failure;
8910
8911 decode_noVMX:
8912 vassert(!allow_VMX);
8913 vex_printf("disInstr(ppc): AltiVec insns disabled for this arch.\n");
8914 goto decode_failure;
8915
cerion896a1372005-01-25 12:24:25 +00008916 decode_failure:
8917 /* All decode failures end up here. */
cerion225a0342005-09-12 20:49:09 +00008918 opc2 = (theInstr) & 0x7FF;
cerion5b2325f2005-12-23 00:55:09 +00008919 vex_printf("disInstr(ppc): unhandled instruction: "
cerion896a1372005-01-25 12:24:25 +00008920 "0x%x\n", theInstr);
sewardjc7cd2142005-09-09 22:31:49 +00008921 vex_printf(" primary %d(0x%x), secondary %u(0x%x)\n",
sewardjb51f0f42005-07-18 11:38:02 +00008922 opc1, opc1, opc2, opc2);
cerion995bc362005-02-03 11:03:31 +00008923
sewardj01a9e802005-02-01 20:46:00 +00008924 /* Tell the dispatcher that this insn cannot be decoded, and so has
8925 not been executed, and (is currently) the next to be executed.
8926 CIA should be up-to-date since it made so at the start of each
8927 insn, but nevertheless be paranoid and update it again right
8928 now. */
cerion2831b002005-11-30 19:55:22 +00008929 putGST( PPC_GST_CIA, mkSzImm(ty, guest_CIA_curr_instr) );
8930 irbb->next = mkSzImm(ty, guest_CIA_curr_instr);
sewardj01a9e802005-02-01 20:46:00 +00008931 irbb->jumpkind = Ijk_NoDecode;
ceriond953ebb2005-11-29 13:27:20 +00008932 dres.whatNext = Dis_StopHere;
8933 dres.len = 0;
sewardj9e6491a2005-07-02 19:24:10 +00008934 return dres;
cerion896a1372005-01-25 12:24:25 +00008935
8936 } /* switch (opc) for the main (primary) opcode switch. */
8937
8938 decode_success:
8939 /* All decode successes end up here. */
cerion896a1372005-01-25 12:24:25 +00008940 DIP("\n");
8941
sewardj9e6491a2005-07-02 19:24:10 +00008942 dres.len = 4;
8943 return dres;
cerion896a1372005-01-25 12:24:25 +00008944}
8945
8946#undef DIP
8947#undef DIS
8948
sewardj9e6491a2005-07-02 19:24:10 +00008949
8950/*------------------------------------------------------------*/
8951/*--- Top-level fn ---*/
8952/*------------------------------------------------------------*/
8953
8954/* Disassemble a single instruction into IR. The instruction
8955 is located in host memory at &guest_code[delta]. */
8956
cerion5b2325f2005-12-23 00:55:09 +00008957DisResult disInstr_PPC ( IRBB* irbb_IN,
8958 Bool put_IP,
8959 Bool (*resteerOkFn) ( Addr64 ),
8960 UChar* guest_code_IN,
8961 Long delta,
8962 Addr64 guest_IP,
8963 VexArchInfo* archinfo,
8964 Bool host_bigendian_IN )
sewardj9e6491a2005-07-02 19:24:10 +00008965{
sewardj5df65bb2005-11-29 14:47:04 +00008966 IRType ty;
8967 DisResult dres;
8968 VexSubArch gsa = archinfo->subarch;
8969
8970 /* Figure out whether we're being ppc32 or ppc64 today. */
8971 switch (gsa) {
8972 case VexSubArchPPC32_VFI:
8973 case VexSubArchPPC32_FI:
8974 case VexSubArchPPC32_I:
8975 mode64 = False;
8976 break;
8977 case VexSubArchPPC64_VFI:
8978 case VexSubArchPPC64_FI:
8979 mode64 = True;
8980 break;
8981 default:
cerion5b2325f2005-12-23 00:55:09 +00008982 vpanic("disInstr_PPC(): illegal subarch");
sewardj5df65bb2005-11-29 14:47:04 +00008983 }
8984
8985 ty = mode64 ? Ity_I64 : Ity_I32;
sewardj9e6491a2005-07-02 19:24:10 +00008986
8987 /* Set globals (see top of this file) */
8988 guest_code = guest_code_IN;
8989 irbb = irbb_IN;
8990 host_is_bigendian = host_bigendian_IN;
ceriond953ebb2005-11-29 13:27:20 +00008991
cerion2831b002005-11-30 19:55:22 +00008992 guest_CIA_curr_instr = mkSzAddr(ty, guest_IP);
8993 guest_CIA_bbstart = mkSzAddr(ty, guest_IP - delta);
sewardj9e6491a2005-07-02 19:24:10 +00008994
cerion5b2325f2005-12-23 00:55:09 +00008995 dres = disInstr_PPC_WRK ( put_IP, resteerOkFn,
8996 delta, archinfo );
sewardj9e6491a2005-07-02 19:24:10 +00008997
8998 return dres;
8999}
9000
9001
sewardjc808ef72005-08-18 11:50:43 +00009002/*------------------------------------------------------------*/
9003/*--- Unused stuff ---*/
9004/*------------------------------------------------------------*/
9005
9006///* A potentially more memcheck-friendly implementation of Clz32, with
9007// the boundary case Clz32(0) = 32, which is what ppc requires. */
9008//
9009//static IRExpr* /* :: Ity_I32 */ verbose_Clz32 ( IRTemp arg )
9010//{
9011// /* Welcome ... to SSA R Us. */
9012// IRTemp n1 = newTemp(Ity_I32);
9013// IRTemp n2 = newTemp(Ity_I32);
9014// IRTemp n3 = newTemp(Ity_I32);
9015// IRTemp n4 = newTemp(Ity_I32);
9016// IRTemp n5 = newTemp(Ity_I32);
9017// IRTemp n6 = newTemp(Ity_I32);
9018// IRTemp n7 = newTemp(Ity_I32);
9019// IRTemp n8 = newTemp(Ity_I32);
9020// IRTemp n9 = newTemp(Ity_I32);
9021// IRTemp n10 = newTemp(Ity_I32);
9022// IRTemp n11 = newTemp(Ity_I32);
9023// IRTemp n12 = newTemp(Ity_I32);
9024//
9025// /* First, propagate the most significant 1-bit into all lower
9026// positions in the word. */
9027// /* unsigned int clz ( unsigned int n )
9028// {
9029// n |= (n >> 1);
9030// n |= (n >> 2);
9031// n |= (n >> 4);
9032// n |= (n >> 8);
9033// n |= (n >> 16);
9034// return bitcount(~n);
9035// }
9036// */
9037// assign(n1, mkexpr(arg));
9038// assign(n2, binop(Iop_Or32, mkexpr(n1), binop(Iop_Shr32, mkexpr(n1), mkU8(1))));
9039// assign(n3, binop(Iop_Or32, mkexpr(n2), binop(Iop_Shr32, mkexpr(n2), mkU8(2))));
9040// assign(n4, binop(Iop_Or32, mkexpr(n3), binop(Iop_Shr32, mkexpr(n3), mkU8(4))));
9041// assign(n5, binop(Iop_Or32, mkexpr(n4), binop(Iop_Shr32, mkexpr(n4), mkU8(8))));
9042// assign(n6, binop(Iop_Or32, mkexpr(n5), binop(Iop_Shr32, mkexpr(n5), mkU8(16))));
9043// /* This gives a word of the form 0---01---1. Now invert it, giving
9044// a word of the form 1---10---0, then do a population-count idiom
9045// (to count the 1s, which is the number of leading zeroes, or 32
9046// if the original word was 0. */
9047// assign(n7, unop(Iop_Not32, mkexpr(n6)));
9048//
9049// /* unsigned int bitcount ( unsigned int n )
9050// {
9051// n = n - ((n >> 1) & 0x55555555);
9052// n = (n & 0x33333333) + ((n >> 2) & 0x33333333);
9053// n = (n + (n >> 4)) & 0x0F0F0F0F;
9054// n = n + (n >> 8);
9055// n = (n + (n >> 16)) & 0x3F;
9056// return n;
9057// }
9058// */
9059// assign(n8,
9060// binop(Iop_Sub32,
9061// mkexpr(n7),
9062// binop(Iop_And32,
9063// binop(Iop_Shr32, mkexpr(n7), mkU8(1)),
9064// mkU32(0x55555555))));
9065// assign(n9,
9066// binop(Iop_Add32,
9067// binop(Iop_And32, mkexpr(n8), mkU32(0x33333333)),
9068// binop(Iop_And32,
9069// binop(Iop_Shr32, mkexpr(n8), mkU8(2)),
9070// mkU32(0x33333333))));
9071// assign(n10,
9072// binop(Iop_And32,
9073// binop(Iop_Add32,
9074// mkexpr(n9),
9075// binop(Iop_Shr32, mkexpr(n9), mkU8(4))),
9076// mkU32(0x0F0F0F0F)));
9077// assign(n11,
9078// binop(Iop_Add32,
9079// mkexpr(n10),
9080// binop(Iop_Shr32, mkexpr(n10), mkU8(8))));
9081// assign(n12,
9082// binop(Iop_Add32,
9083// mkexpr(n11),
9084// binop(Iop_Shr32, mkexpr(n11), mkU8(16))));
9085// return
9086// binop(Iop_And32, mkexpr(n12), mkU32(0x3F));
9087//}
9088
cerion896a1372005-01-25 12:24:25 +00009089/*--------------------------------------------------------------------*/
ceriond0eae2d2005-12-23 11:43:01 +00009090/*--- end guest-ppc/toIR.c ---*/
cerion896a1372005-01-25 12:24:25 +00009091/*--------------------------------------------------------------------*/