blob: f19f0a4ddbb6130b21957d19fe8d9978fa4b8157 [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
sewardjcf8986c2006-01-18 04:14:52 +000049 Spot rld... cases which are simply left/right shifts and emit
50 Shl64/Shr64 accordingly.
sewardje14bb9f2005-07-22 09:39:02 +000051
cerionedf7fc52005-11-18 20:57:41 +000052 Altivec
53 - datastream insns
54 - lvxl,stvxl: load/store with 'least recently used' hint
55 - vexptefp, vlogefp
sewardj87e651f2005-09-09 08:31:18 +000056
57 LIMITATIONS:
58
59 Various, including:
60
61 - Some invalid forms of lswi and lswx are accepted when they should
62 not be.
63
cerionedf7fc52005-11-18 20:57:41 +000064 - Floating Point:
65 - All exceptions disabled in FPSCR
66 - condition codes not set in FPSCR
cerion76de5cf2005-11-18 18:25:12 +000067
cerionedf7fc52005-11-18 20:57:41 +000068 - Altivec floating point:
69 - vmaddfp, vnmsubfp
70 Because we're using Java/IEEE mode (FPSCR[NJ]), rather than the
71 system default of Non-Java mode, we get some small errors
72 (lowest bit only).
73 This is because Non-Java mode brutally hacks denormalised results
74 to zero, whereas we keep maximum accuracy. However, using
75 Non-Java mode would give us more inaccuracy, as our intermediate
76 results would then be zeroed, too.
sewardjcf8986c2006-01-18 04:14:52 +000077
78 - 64-bit mode: AbiHints for the stack red zone are only emitted for
79 unconditional calls and returns (bl, blr). They should also be
80 emitted for conditional calls and returns, but we don't have a
81 way to express that right now. Ah well.
sewardjb51f0f42005-07-18 11:38:02 +000082*/
83
sewardjce02aa72006-01-12 12:27:58 +000084/* "Special" instructions.
85
sewardj09e88d12006-01-27 16:05:49 +000086 This instruction decoder can decode four special instructions
sewardjce02aa72006-01-12 12:27:58 +000087 which mean nothing natively (are no-ops as far as regs/mem are
88 concerned) but have meaning for supporting Valgrind. A special
sewardj1eb7e6b2006-01-12 21:13:14 +000089 instruction is flagged by a 16-byte preamble:
90
91 32-bit mode: 54001800 54006800 5400E800 54009800
92 (rlwinm 0,0,3,0,0; rlwinm 0,0,13,0,0;
93 rlwinm 0,0,29,0,0; rlwinm 0,0,19,0,0)
94
95 64-bit mode: 78001800 78006800 7800E802 78009802
96 (rotldi 0,0,3; rotldi 0,0,13;
97 rotldi 0,0,61; rotldi 0,0,51)
98
99 Following that, one of the following 3 are allowed
sewardjce02aa72006-01-12 12:27:58 +0000100 (standard interpretation in parentheses):
101
102 7C210B78 (or 1,1,1) %R3 = client_request ( %R4 )
103 7C421378 (or 2,2,2) %R3 = guest_NRADDR
104 7C631B78 (or 3,3,3) branch-and-link-to-noredir %R11
sewardj5ff11dd2006-01-20 14:19:25 +0000105 7C842378 (or 4,4,4) %R3 = guest_NRADDR_GPR2 (64-bit mode only)
sewardjce02aa72006-01-12 12:27:58 +0000106
107 Any other bytes following the 16-byte preamble are illegal and
108 constitute a failure in instruction decoding. This all assumes
109 that the preamble will never occur except in specific code
110 fragments designed for Valgrind to catch.
111*/
112
sewardjb51f0f42005-07-18 11:38:02 +0000113
cerion5b2325f2005-12-23 00:55:09 +0000114/* Translates PPC32/64 code to IR. */
cerion896a1372005-01-25 12:24:25 +0000115
cerion645c9302005-01-31 10:09:59 +0000116/* References
ceriona982c052005-06-28 17:23:09 +0000117
118#define PPC32
cerion645c9302005-01-31 10:09:59 +0000119 "PowerPC Microprocessor Family:
ceriond953ebb2005-11-29 13:27:20 +0000120 The Programming Environments Manual for 32-Bit Microprocessors"
cerione9d361a2005-03-04 17:35:29 +0000121 02/21/2000
122 http://www-3.ibm.com/chips/techlib/techlib.nsf/techdocs/852569B20050FF778525699600719DF2
123
ceriond953ebb2005-11-29 13:27:20 +0000124#define PPC64
125 "PowerPC Microprocessor Family:
126 Programming Environments Manual for 64-Bit Microprocessors"
127 06/10/2003
128 http://www-3.ibm.com/chips/techlib/techlib.nsf/techdocs/F7E732FF811F783187256FDD004D3797
129
ceriona982c052005-06-28 17:23:09 +0000130#define AV
131 "PowerPC Microprocessor Family:
132 AltiVec(TM) Technology Programming Environments Manual"
133 07/10/2003
134 http://www-3.ibm.com/chips/techlib/techlib.nsf/techdocs/FBFA164F824370F987256D6A006F424D
cerion645c9302005-01-31 10:09:59 +0000135*/
136
cerion896a1372005-01-25 12:24:25 +0000137#include "libvex_basictypes.h"
138#include "libvex_ir.h"
139#include "libvex.h"
cerion1515db92005-01-25 17:21:23 +0000140#include "libvex_guest_ppc32.h"
ceriond953ebb2005-11-29 13:27:20 +0000141#include "libvex_guest_ppc64.h"
cerion896a1372005-01-25 12:24:25 +0000142
143#include "main/vex_util.h"
144#include "main/vex_globals.h"
sewardj9e6491a2005-07-02 19:24:10 +0000145#include "guest-generic/bb_to_IR.h"
ceriond0eae2d2005-12-23 11:43:01 +0000146#include "guest-ppc/gdefs.h"
cerion896a1372005-01-25 12:24:25 +0000147
148
149/*------------------------------------------------------------*/
150/*--- Globals ---*/
151/*------------------------------------------------------------*/
152
sewardj9e6491a2005-07-02 19:24:10 +0000153/* These are set at the start of the translation of an insn, right
cerion5b2325f2005-12-23 00:55:09 +0000154 down in disInstr_PPC, so that we don't have to pass them around
sewardj9e6491a2005-07-02 19:24:10 +0000155 endlessly. They are all constant during the translation of any
156 given insn. */
cerion896a1372005-01-25 12:24:25 +0000157
cerioned623db2005-06-20 12:42:04 +0000158/* We need to know this to do sub-register accesses correctly. */
cerioned623db2005-06-20 12:42:04 +0000159static Bool host_is_bigendian;
160
cerion896a1372005-01-25 12:24:25 +0000161/* Pointer to the guest code area. */
cerion896a1372005-01-25 12:24:25 +0000162static UChar* guest_code;
163
164/* The guest address corresponding to guest_code[0]. */
ceriond953ebb2005-11-29 13:27:20 +0000165static Addr64 guest_CIA_bbstart;
cerion896a1372005-01-25 12:24:25 +0000166
sewardj01a9e802005-02-01 20:46:00 +0000167/* The guest address for the instruction currently being
168 translated. */
ceriond953ebb2005-11-29 13:27:20 +0000169static Addr64 guest_CIA_curr_instr;
sewardj01a9e802005-02-01 20:46:00 +0000170
cerion896a1372005-01-25 12:24:25 +0000171/* The IRBB* into which we're generating code. */
172static IRBB* irbb;
173
sewardj5df65bb2005-11-29 14:47:04 +0000174/* Is our guest binary 32 or 64bit? Set at each call to
cerion5b2325f2005-12-23 00:55:09 +0000175 disInstr_PPC below. */
sewardj5df65bb2005-11-29 14:47:04 +0000176static Bool mode64 = False;
ceriond953ebb2005-11-29 13:27:20 +0000177
cerion4c4f5ef2006-01-02 14:41:50 +0000178// Given a pointer to a function as obtained by "& functionname" in C,
179// produce a pointer to the actual entry point for the function. For
180// most platforms it's the identity function. Unfortunately, on
181// ppc64-linux it isn't (sigh).
182static void* fnptr_to_fnentry( void* f )
183{
184#if defined(__powerpc64__)
185 /* f is a pointer to a 3-word function descriptor, of which
186 the first word is the entry address. */
187 ULong* fdescr = (ULong*)f;
188 return (void*)(fdescr[0]);
189#else
190 return f;
191#endif
192}
193
cerion896a1372005-01-25 12:24:25 +0000194
195/*------------------------------------------------------------*/
196/*--- Debugging output ---*/
197/*------------------------------------------------------------*/
198
199#define DIP(format, args...) \
200 if (vex_traceflags & VEX_TRACE_FE) \
201 vex_printf(format, ## args)
202
203#define DIS(buf, format, args...) \
204 if (vex_traceflags & VEX_TRACE_FE) \
205 vex_sprintf(buf, format, ## args)
206
207
cerion896a1372005-01-25 12:24:25 +0000208/*------------------------------------------------------------*/
ceriond953ebb2005-11-29 13:27:20 +0000209/*--- Offsets of various parts of the ppc32/64 guest state ---*/
cerion896a1372005-01-25 12:24:25 +0000210/*------------------------------------------------------------*/
211
cerion5b2325f2005-12-23 00:55:09 +0000212#define offsetofPPCGuestState(_x) \
213 (mode64 ? offsetof(VexGuestPPC64State, _x) : \
214 offsetof(VexGuestPPC32State, _x))
cerion91ad5362005-01-27 23:02:41 +0000215
cerion5b2325f2005-12-23 00:55:09 +0000216#define OFFB_CIA offsetofPPCGuestState(guest_CIA)
217#define OFFB_LR offsetofPPCGuestState(guest_LR)
218#define OFFB_CTR offsetofPPCGuestState(guest_CTR)
219#define OFFB_XER_SO offsetofPPCGuestState(guest_XER_SO)
220#define OFFB_XER_OV offsetofPPCGuestState(guest_XER_OV)
221#define OFFB_XER_CA offsetofPPCGuestState(guest_XER_CA)
222#define OFFB_XER_BC offsetofPPCGuestState(guest_XER_BC)
223#define OFFB_FPROUND offsetofPPCGuestState(guest_FPROUND)
224#define OFFB_VRSAVE offsetofPPCGuestState(guest_VRSAVE)
225#define OFFB_VSCR offsetofPPCGuestState(guest_VSCR)
226#define OFFB_EMWARN offsetofPPCGuestState(guest_EMWARN)
227#define OFFB_TISTART offsetofPPCGuestState(guest_TISTART)
228#define OFFB_TILEN offsetofPPCGuestState(guest_TILEN)
229#define OFFB_RESVN offsetofPPCGuestState(guest_RESVN)
sewardjce02aa72006-01-12 12:27:58 +0000230#define OFFB_NRADDR offsetofPPCGuestState(guest_NRADDR)
sewardj7787af42005-08-04 18:32:19 +0000231
sewardj5ff11dd2006-01-20 14:19:25 +0000232/* This only exists in the 64-bit guest state */
233#define OFFB64_NRADDR_GPR2 \
234 offsetof(VexGuestPPC64State,guest_NRADDR_GPR2)
235
cerion91ad5362005-01-27 23:02:41 +0000236
cerion38674602005-02-08 02:19:25 +0000237/*------------------------------------------------------------*/
sewardjb51f0f42005-07-18 11:38:02 +0000238/*--- Extract instruction fields --- */
cerion38674602005-02-08 02:19:25 +0000239/*------------------------------------------------------------*/
cerione9d361a2005-03-04 17:35:29 +0000240
cerion76de5cf2005-11-18 18:25:12 +0000241/* Extract field from insn, given idx (zero = lsb) and field length */
242#define IFIELD( insn, idx, len ) ((insn >> idx) & ((1<<len)-1))
243
sewardjb51f0f42005-07-18 11:38:02 +0000244/* Extract primary opcode, instr[31:26] */
cerion76de5cf2005-11-18 18:25:12 +0000245static UChar ifieldOPC( UInt instr ) {
246 return toUChar( IFIELD( instr, 26, 6 ) );
sewardjb51f0f42005-07-18 11:38:02 +0000247}
cerione9d361a2005-03-04 17:35:29 +0000248
cerion76de5cf2005-11-18 18:25:12 +0000249/* Extract 10-bit secondary opcode, instr[10:1] */
sewardjb51f0f42005-07-18 11:38:02 +0000250static UInt ifieldOPClo10 ( UInt instr) {
cerion76de5cf2005-11-18 18:25:12 +0000251 return IFIELD( instr, 1, 10 );
252}
253
254/* Extract 9-bit secondary opcode, instr[9:1] */
255static UInt ifieldOPClo9 ( UInt instr) {
256 return IFIELD( instr, 1, 9 );
257}
258
259/* Extract 5-bit secondary opcode, instr[5:1] */
260static UInt ifieldOPClo5 ( UInt instr) {
261 return IFIELD( instr, 1, 5 );
sewardjb51f0f42005-07-18 11:38:02 +0000262}
cerione9d361a2005-03-04 17:35:29 +0000263
sewardjb51f0f42005-07-18 11:38:02 +0000264/* Extract RD (destination register) field, instr[25:21] */
cerion76de5cf2005-11-18 18:25:12 +0000265static UChar ifieldRegDS( UInt instr ) {
266 return toUChar( IFIELD( instr, 21, 5 ) );
sewardjb51f0f42005-07-18 11:38:02 +0000267}
cerion094d1392005-06-20 13:45:57 +0000268
cerion76de5cf2005-11-18 18:25:12 +0000269/* Extract RA (1st source register) field, instr[20:16] */
270static UChar ifieldRegA ( UInt instr ) {
271 return toUChar( IFIELD( instr, 16, 5 ) );
sewardjb51f0f42005-07-18 11:38:02 +0000272}
273
cerion76de5cf2005-11-18 18:25:12 +0000274/* Extract RB (2nd source register) field, instr[15:11] */
275static UChar ifieldRegB ( UInt instr ) {
276 return toUChar( IFIELD( instr, 11, 5 ) );
sewardjb51f0f42005-07-18 11:38:02 +0000277}
278
cerion76de5cf2005-11-18 18:25:12 +0000279/* Extract RC (3rd source register) field, instr[10:6] */
280static UChar ifieldRegC ( UInt instr ) {
281 return toUChar( IFIELD( instr, 6, 5 ) );
sewardjb51f0f42005-07-18 11:38:02 +0000282}
283
cerion76de5cf2005-11-18 18:25:12 +0000284/* Extract 2nd lowest bit, instr[1] */
285static UChar ifieldBIT10 ( UInt instr ) {
286 return toUChar( IFIELD( instr, 10, 1 ) );
287}
288
289/* Extract 2nd lowest bit, instr[1] */
290static UChar ifieldBIT1 ( UInt instr ) {
291 return toUChar( IFIELD( instr, 1, 1 ) );
292}
293
294/* Extract lowest bit, instr[0] */
295static UChar ifieldBIT0 ( UInt instr ) {
296 return toUChar( instr & 0x1 );
297}
298
299/* Extract unsigned bottom half, instr[15:0] */
300static UInt ifieldUIMM16 ( UInt instr ) {
301 return instr & 0xFFFF;
302}
303
ceriond953ebb2005-11-29 13:27:20 +0000304/* Extract unsigned bottom 26 bits, instr[25:0] */
305static UInt ifieldUIMM26 ( UInt instr ) {
306 return instr & 0x3FFFFFF;
cerion76de5cf2005-11-18 18:25:12 +0000307}
308
sewardjb51f0f42005-07-18 11:38:02 +0000309
cerionedf7fc52005-11-18 20:57:41 +0000310/*------------------------------------------------------------*/
ceriond953ebb2005-11-29 13:27:20 +0000311/*--- Guest-state identifiers ---*/
cerionedf7fc52005-11-18 20:57:41 +0000312/*------------------------------------------------------------*/
sewardje14bb9f2005-07-22 09:39:02 +0000313
cerione9d361a2005-03-04 17:35:29 +0000314typedef enum {
ceriond953ebb2005-11-29 13:27:20 +0000315 PPC_GST_CIA, // Current Instruction Address
316 PPC_GST_LR, // Link Register
317 PPC_GST_CTR, // Count Register
318 PPC_GST_XER, // Overflow, carry flags, byte count
319 PPC_GST_CR, // Condition Register
320 PPC_GST_FPSCR, // Floating Point Status/Control Register
321 PPC_GST_VRSAVE, // Vector Save/Restore Register
322 PPC_GST_VSCR, // Vector Status and Control Register
323 PPC_GST_EMWARN, // Emulation warnings
324 PPC_GST_TISTART,// For icbi: start of area to invalidate
325 PPC_GST_TILEN, // For icbi: length of area to invalidate
326 PPC_GST_RESVN, // For lwarx/stwcx.
327 PPC_GST_MAX
328} PPC_GST;
cerione9d361a2005-03-04 17:35:29 +0000329
cerionedf7fc52005-11-18 20:57:41 +0000330#define MASK_FPSCR_RN 0x3
331#define MASK_VSCR_VALID 0x00010001
sewardje14bb9f2005-07-22 09:39:02 +0000332
cerionedf7fc52005-11-18 20:57:41 +0000333
334/*------------------------------------------------------------*/
335/*--- FP Helpers ---*/
336/*------------------------------------------------------------*/
337
sewardj2ead5222005-11-23 03:53:45 +0000338/* Produce the 32-bit pattern corresponding to the supplied
339 float. */
340static UInt float_to_bits ( Float f )
341{
342 union { UInt i; Float f; } u;
343 vassert(4 == sizeof(UInt));
344 vassert(4 == sizeof(Float));
345 vassert(4 == sizeof(u));
346 u.f = f;
347 return u.i;
348}
349
cerion38674602005-02-08 02:19:25 +0000350
cerion38674602005-02-08 02:19:25 +0000351/*------------------------------------------------------------*/
352/*--- Misc Helpers ---*/
353/*------------------------------------------------------------*/
354
cerionf0de28c2005-12-13 20:21:11 +0000355/* Generate mask with 1's from 'begin' through 'end',
356 wrapping if begin > end.
357 begin->end works from right to left, 0=lsb
358*/
ceriond953ebb2005-11-29 13:27:20 +0000359static UInt MASK32( UInt begin, UInt end )
cerion38674602005-02-08 02:19:25 +0000360{
sewardj63327402006-01-25 03:26:27 +0000361 UInt m1, m2, mask;
ceriond953ebb2005-11-29 13:27:20 +0000362 vassert(begin < 32);
363 vassert(end < 32);
sewardj63327402006-01-25 03:26:27 +0000364 m1 = ((UInt)(-1)) << begin;
365 m2 = ((UInt)(-1)) << end << 1;
366 mask = m1 ^ m2;
cerionb85e8bb2005-02-16 08:54:33 +0000367 if (begin > end) mask = ~mask; // wrap mask
368 return mask;
cerion38674602005-02-08 02:19:25 +0000369}
370
cerion5b2325f2005-12-23 00:55:09 +0000371/* ditto for 64bit mask */
ceriond953ebb2005-11-29 13:27:20 +0000372static ULong MASK64( UInt begin, UInt end )
373{
sewardj63327402006-01-25 03:26:27 +0000374 ULong m1, m2, mask;
ceriond953ebb2005-11-29 13:27:20 +0000375 vassert(begin < 64);
376 vassert(end < 64);
sewardj63327402006-01-25 03:26:27 +0000377 m1 = ((ULong)(-1)) << begin;
378 m2 = ((ULong)(-1)) << end << 1;
379 mask = m1 ^ m2;
ceriond953ebb2005-11-29 13:27:20 +0000380 if (begin > end) mask = ~mask; // wrap mask
381 return mask;
382}
383
cerionf0de28c2005-12-13 20:21:11 +0000384static Addr64 nextInsnAddr( void )
385{
386 return guest_CIA_curr_instr + 4;
387}
ceriond953ebb2005-11-29 13:27:20 +0000388
cerion896a1372005-01-25 12:24:25 +0000389
cerion896a1372005-01-25 12:24:25 +0000390/*------------------------------------------------------------*/
391/*--- Helper bits and pieces for deconstructing the ---*/
ceriond953ebb2005-11-29 13:27:20 +0000392/*--- ppc32/64 insn stream. ---*/
cerion896a1372005-01-25 12:24:25 +0000393/*------------------------------------------------------------*/
394
395/* Add a statement to the list held by "irbb". */
396static void stmt ( IRStmt* st )
397{
398 addStmtToIRBB( irbb, st );
399}
400
cerion896a1372005-01-25 12:24:25 +0000401/* Generate a new temporary of the given type. */
402static IRTemp newTemp ( IRType ty )
403{
sewardj496a58d2005-03-20 18:44:44 +0000404 vassert(isPlausibleIRType(ty));
cerion896a1372005-01-25 12:24:25 +0000405 return newIRTemp( irbb->tyenv, ty );
406}
cerion896a1372005-01-25 12:24:25 +0000407
cerion32aad402005-09-10 12:02:24 +0000408/* Various simple conversions */
409
410static UChar extend_s_5to8 ( UChar x )
411{
412 return toUChar((((Int)x) << 27) >> 27);
413}
414
cerion92d9d872005-09-15 21:58:50 +0000415static UInt extend_s_8to32( UChar x )
416{
417 return (UInt)((((Int)x) << 24) >> 24);
418}
cerion91ad5362005-01-27 23:02:41 +0000419
cerion896a1372005-01-25 12:24:25 +0000420static UInt extend_s_16to32 ( UInt x )
421{
422 return (UInt)((((Int)x) << 16) >> 16);
423}
cerion896a1372005-01-25 12:24:25 +0000424
ceriond953ebb2005-11-29 13:27:20 +0000425static ULong extend_s_16to64 ( UInt x )
426{
427 return (ULong)((((Long)x) << 48) >> 48);
428}
429
430static ULong extend_s_26to64 ( UInt x )
431{
432 return (ULong)((((Long)x) << 38) >> 38);
433}
434
435static ULong extend_s_32to64 ( UInt x )
436{
437 return (ULong)((((Long)x) << 32) >> 32);
438}
439
sewardj684aa952005-01-30 12:52:14 +0000440/* Do a big-endian load of a 32-bit word, regardless of the endianness
441 of the underlying host. */
cerioncf004462005-01-31 15:24:55 +0000442static UInt getUIntBigendianly ( UChar* p )
sewardj684aa952005-01-30 12:52:14 +0000443{
cerioncf004462005-01-31 15:24:55 +0000444 UInt w = 0;
sewardj684aa952005-01-30 12:52:14 +0000445 w = (w << 8) | p[0];
446 w = (w << 8) | p[1];
447 w = (w << 8) | p[2];
448 w = (w << 8) | p[3];
449 return w;
450}
451
cerion896a1372005-01-25 12:24:25 +0000452
453/*------------------------------------------------------------*/
454/*--- Helpers for constructing IR. ---*/
455/*------------------------------------------------------------*/
456
cerion896a1372005-01-25 12:24:25 +0000457static void assign ( IRTemp dst, IRExpr* e )
458{
459 stmt( IRStmt_Tmp(dst, e) );
460}
461
cerionae694622005-01-28 17:52:47 +0000462static void storeBE ( IRExpr* addr, IRExpr* data )
cerion896a1372005-01-25 12:24:25 +0000463{
ceriond953ebb2005-11-29 13:27:20 +0000464 vassert(typeOfIRExpr(irbb->tyenv, addr) == Ity_I32 ||
465 typeOfIRExpr(irbb->tyenv, addr) == Ity_I64);
sewardjaf1ceca2005-06-30 23:31:27 +0000466 stmt( IRStmt_Store(Iend_BE,addr,data) );
cerion896a1372005-01-25 12:24:25 +0000467}
468
469static IRExpr* unop ( IROp op, IRExpr* a )
470{
471 return IRExpr_Unop(op, a);
472}
473
474static IRExpr* binop ( IROp op, IRExpr* a1, IRExpr* a2 )
475{
476 return IRExpr_Binop(op, a1, a2);
477}
478
sewardjb183b852006-02-03 16:08:03 +0000479static IRExpr* triop ( IROp op, IRExpr* a1, IRExpr* a2, IRExpr* a3 )
480{
481 return IRExpr_Triop(op, a1, a2, a3);
482}
483
sewardj40c80262006-02-08 19:30:46 +0000484static IRExpr* qop ( IROp op, IRExpr* a1, IRExpr* a2,
485 IRExpr* a3, IRExpr* a4 )
486{
487 return IRExpr_Qop(op, a1, a2, a3, a4);
488}
489
cerion896a1372005-01-25 12:24:25 +0000490static IRExpr* mkexpr ( IRTemp tmp )
491{
492 return IRExpr_Tmp(tmp);
493}
494
sewardj684c0372005-02-07 02:33:58 +0000495static IRExpr* mkU8 ( UChar i )
cerion896a1372005-01-25 12:24:25 +0000496{
cerion896a1372005-01-25 12:24:25 +0000497 return IRExpr_Const(IRConst_U8(i));
498}
cerion896a1372005-01-25 12:24:25 +0000499
cerion92d9d872005-09-15 21:58:50 +0000500static IRExpr* mkU16 ( UInt i )
501{
502 return IRExpr_Const(IRConst_U16(i));
503}
504
cerion896a1372005-01-25 12:24:25 +0000505static IRExpr* mkU32 ( UInt i )
506{
507 return IRExpr_Const(IRConst_U32(i));
508}
509
cerion4a49b032005-11-08 16:23:07 +0000510static IRExpr* mkU64 ( ULong i )
511{
512 return IRExpr_Const(IRConst_U64(i));
513}
514
cerionae694622005-01-28 17:52:47 +0000515static IRExpr* loadBE ( IRType ty, IRExpr* data )
cerion896a1372005-01-25 12:24:25 +0000516{
sewardjaf1ceca2005-06-30 23:31:27 +0000517 return IRExpr_Load(Iend_BE,ty,data);
cerion896a1372005-01-25 12:24:25 +0000518}
cerion896a1372005-01-25 12:24:25 +0000519
sewardj20ef5472005-07-21 14:48:31 +0000520static IRExpr* mkOR1 ( IRExpr* arg1, IRExpr* arg2 )
521{
522 vassert(typeOfIRExpr(irbb->tyenv, arg1) == Ity_I1);
523 vassert(typeOfIRExpr(irbb->tyenv, arg2) == Ity_I1);
cerion5b2325f2005-12-23 00:55:09 +0000524 return unop(Iop_32to1, binop(Iop_Or32, unop(Iop_1Uto32, arg1),
525 unop(Iop_1Uto32, arg2)));
sewardj20ef5472005-07-21 14:48:31 +0000526}
527
528static IRExpr* mkAND1 ( IRExpr* arg1, IRExpr* arg2 )
529{
530 vassert(typeOfIRExpr(irbb->tyenv, arg1) == Ity_I1);
531 vassert(typeOfIRExpr(irbb->tyenv, arg2) == Ity_I1);
cerion5b2325f2005-12-23 00:55:09 +0000532 return unop(Iop_32to1, binop(Iop_And32, unop(Iop_1Uto32, arg1),
533 unop(Iop_1Uto32, arg2)));
sewardj20ef5472005-07-21 14:48:31 +0000534}
sewardjb51f0f42005-07-18 11:38:02 +0000535
cerion4a49b032005-11-08 16:23:07 +0000536/* expand V128_8Ux16 to 2x V128_16Ux8's */
cerion5b2325f2005-12-23 00:55:09 +0000537static void expand8Ux16( IRExpr* vIn,
538 /*OUTs*/ IRTemp* vEvn, IRTemp* vOdd )
cerion4a49b032005-11-08 16:23:07 +0000539{
540 IRTemp ones8x16 = newTemp(Ity_V128);
541
542 vassert(typeOfIRExpr(irbb->tyenv, vIn) == Ity_V128);
543 vassert(vEvn && *vEvn == IRTemp_INVALID);
544 vassert(vOdd && *vOdd == IRTemp_INVALID);
545 *vEvn = newTemp(Ity_V128);
546 *vOdd = newTemp(Ity_V128);
547
548 assign( ones8x16, unop(Iop_Dup8x16, mkU8(0x1)) );
cerion24d06f12005-11-09 21:34:20 +0000549 assign( *vOdd, binop(Iop_MullEven8Ux16, mkexpr(ones8x16), vIn) );
550 assign( *vEvn, binop(Iop_MullEven8Ux16, mkexpr(ones8x16),
551 binop(Iop_ShrV128, vIn, mkU8(8))) );
cerion4a49b032005-11-08 16:23:07 +0000552}
553
554/* expand V128_8Sx16 to 2x V128_16Sx8's */
cerion5b2325f2005-12-23 00:55:09 +0000555static void expand8Sx16( IRExpr* vIn,
556 /*OUTs*/ IRTemp* vEvn, IRTemp* vOdd )
cerion4a49b032005-11-08 16:23:07 +0000557{
558 IRTemp ones8x16 = newTemp(Ity_V128);
559
560 vassert(typeOfIRExpr(irbb->tyenv, vIn) == Ity_V128);
561 vassert(vEvn && *vEvn == IRTemp_INVALID);
562 vassert(vOdd && *vOdd == IRTemp_INVALID);
563 *vEvn = newTemp(Ity_V128);
564 *vOdd = newTemp(Ity_V128);
565
566 assign( ones8x16, unop(Iop_Dup8x16, mkU8(0x1)) );
cerion24d06f12005-11-09 21:34:20 +0000567 assign( *vOdd, binop(Iop_MullEven8Sx16, mkexpr(ones8x16), vIn) );
568 assign( *vEvn, binop(Iop_MullEven8Sx16, mkexpr(ones8x16),
569 binop(Iop_ShrV128, vIn, mkU8(8))) );
cerion4a49b032005-11-08 16:23:07 +0000570}
571
572/* expand V128_16Uto8 to 2x V128_32Ux4's */
cerion5b2325f2005-12-23 00:55:09 +0000573static void expand16Ux8( IRExpr* vIn,
574 /*OUTs*/ IRTemp* vEvn, IRTemp* vOdd )
cerion4a49b032005-11-08 16:23:07 +0000575{
576 IRTemp ones16x8 = newTemp(Ity_V128);
577
578 vassert(typeOfIRExpr(irbb->tyenv, vIn) == Ity_V128);
579 vassert(vEvn && *vEvn == IRTemp_INVALID);
580 vassert(vOdd && *vOdd == IRTemp_INVALID);
581 *vEvn = newTemp(Ity_V128);
582 *vOdd = newTemp(Ity_V128);
583
584 assign( ones16x8, unop(Iop_Dup16x8, mkU16(0x1)) );
cerion24d06f12005-11-09 21:34:20 +0000585 assign( *vOdd, binop(Iop_MullEven16Ux8, mkexpr(ones16x8), vIn) );
586 assign( *vEvn, binop(Iop_MullEven16Ux8, mkexpr(ones16x8),
587 binop(Iop_ShrV128, vIn, mkU8(16))) );
cerion4a49b032005-11-08 16:23:07 +0000588}
589
590/* expand V128_16Sto8 to 2x V128_32Sx4's */
cerion5b2325f2005-12-23 00:55:09 +0000591static void expand16Sx8( IRExpr* vIn,
592 /*OUTs*/ IRTemp* vEvn, IRTemp* vOdd )
cerion4a49b032005-11-08 16:23:07 +0000593{
594 IRTemp ones16x8 = newTemp(Ity_V128);
595
596 vassert(typeOfIRExpr(irbb->tyenv, vIn) == Ity_V128);
597 vassert(vEvn && *vEvn == IRTemp_INVALID);
598 vassert(vOdd && *vOdd == IRTemp_INVALID);
599 *vEvn = newTemp(Ity_V128);
600 *vOdd = newTemp(Ity_V128);
601
602 assign( ones16x8, unop(Iop_Dup16x8, mkU16(0x1)) );
cerion24d06f12005-11-09 21:34:20 +0000603 assign( *vOdd, binop(Iop_MullEven16Sx8, mkexpr(ones16x8), vIn) );
604 assign( *vEvn, binop(Iop_MullEven16Sx8, mkexpr(ones16x8),
605 binop(Iop_ShrV128, vIn, mkU8(16))) );
cerion4a49b032005-11-08 16:23:07 +0000606}
607
608/* break V128 to 4xI32's, then sign-extend to I64's */
609static void breakV128to4x64S( IRExpr* t128,
610 /*OUTs*/
611 IRTemp* t3, IRTemp* t2,
612 IRTemp* t1, IRTemp* t0 )
613{
614 IRTemp hi64 = newTemp(Ity_I64);
615 IRTemp lo64 = newTemp(Ity_I64);
616
617 vassert(typeOfIRExpr(irbb->tyenv, t128) == Ity_V128);
618 vassert(t0 && *t0 == IRTemp_INVALID);
619 vassert(t1 && *t1 == IRTemp_INVALID);
620 vassert(t2 && *t2 == IRTemp_INVALID);
621 vassert(t3 && *t3 == IRTemp_INVALID);
622 *t0 = newTemp(Ity_I64);
623 *t1 = newTemp(Ity_I64);
624 *t2 = newTemp(Ity_I64);
625 *t3 = newTemp(Ity_I64);
626
627 assign( hi64, unop(Iop_V128HIto64, t128) );
628 assign( lo64, unop(Iop_V128to64, t128) );
629 assign( *t3, unop(Iop_32Sto64, unop(Iop_64HIto32, mkexpr(hi64))) );
630 assign( *t2, unop(Iop_32Sto64, unop(Iop_64to32, mkexpr(hi64))) );
631 assign( *t1, unop(Iop_32Sto64, unop(Iop_64HIto32, mkexpr(lo64))) );
632 assign( *t0, unop(Iop_32Sto64, unop(Iop_64to32, mkexpr(lo64))) );
633}
634
635/* break V128 to 4xI32's, then zero-extend to I64's */
636static void breakV128to4x64U ( IRExpr* t128,
637 /*OUTs*/
638 IRTemp* t3, IRTemp* t2,
639 IRTemp* t1, IRTemp* t0 )
640{
641 IRTemp hi64 = newTemp(Ity_I64);
642 IRTemp lo64 = newTemp(Ity_I64);
643
644 vassert(typeOfIRExpr(irbb->tyenv, t128) == Ity_V128);
645 vassert(t0 && *t0 == IRTemp_INVALID);
646 vassert(t1 && *t1 == IRTemp_INVALID);
647 vassert(t2 && *t2 == IRTemp_INVALID);
648 vassert(t3 && *t3 == IRTemp_INVALID);
649 *t0 = newTemp(Ity_I64);
650 *t1 = newTemp(Ity_I64);
651 *t2 = newTemp(Ity_I64);
652 *t3 = newTemp(Ity_I64);
653
654 assign( hi64, unop(Iop_V128HIto64, t128) );
655 assign( lo64, unop(Iop_V128to64, t128) );
656 assign( *t3, unop(Iop_32Uto64, unop(Iop_64HIto32, mkexpr(hi64))) );
657 assign( *t2, unop(Iop_32Uto64, unop(Iop_64to32, mkexpr(hi64))) );
658 assign( *t1, unop(Iop_32Uto64, unop(Iop_64HIto32, mkexpr(lo64))) );
659 assign( *t0, unop(Iop_32Uto64, unop(Iop_64to32, mkexpr(lo64))) );
660}
661
662/* Signed saturating narrow 64S to 32 */
663static IRExpr* mkQNarrow64Sto32 ( IRExpr* t64 )
664{
665 IRTemp hi32 = newTemp(Ity_I32);
666 IRTemp lo32 = newTemp(Ity_I32);
667
668 vassert(typeOfIRExpr(irbb->tyenv, t64) == Ity_I64);
669
670 assign( hi32, unop(Iop_64HIto32, t64));
671 assign( lo32, unop(Iop_64to32, t64));
672
673 return IRExpr_Mux0X(
674 /* if (hi32 == (lo32 >>s 31)) */
675 unop(Iop_1Uto8,
676 binop(Iop_CmpEQ32, mkexpr(hi32),
677 binop( Iop_Sar32, mkexpr(lo32), mkU8(31)))),
678 /* else: sign dep saturate: 1->0x80000000, 0->0x7FFFFFFF */
679 binop(Iop_Add32, mkU32(0x7FFFFFFF),
680 binop(Iop_Shr32, mkexpr(hi32), mkU8(31))),
681 /* then: within signed-32 range: lo half good enough */
682 mkexpr(lo32) );
683}
684
685/* Unsigned saturating narrow 64S to 32 */
686static IRExpr* mkQNarrow64Uto32 ( IRExpr* t64 )
687{
688 IRTemp hi32 = newTemp(Ity_I32);
689 IRTemp lo32 = newTemp(Ity_I32);
690
691 vassert(typeOfIRExpr(irbb->tyenv, t64) == Ity_I64);
692
693 assign( hi32, unop(Iop_64HIto32, t64));
694 assign( lo32, unop(Iop_64to32, t64));
695
696 return IRExpr_Mux0X(
697 /* if (top 32 bits of t64 are 0) */
698 unop(Iop_1Uto8, binop(Iop_CmpEQ32, mkexpr(hi32), mkU32(0))),
699 /* else: positive saturate -> 0xFFFFFFFF */
700 mkU32(0xFFFFFFFF),
701 /* then: within unsigned-32 range: lo half good enough */
702 mkexpr(lo32) );
703}
704
705/* Signed saturate narrow 64->32, combining to V128 */
706static IRExpr* mkV128from4x64S ( IRExpr* t3, IRExpr* t2,
707 IRExpr* t1, IRExpr* t0 )
708{
709 vassert(typeOfIRExpr(irbb->tyenv, t3) == Ity_I64);
710 vassert(typeOfIRExpr(irbb->tyenv, t2) == Ity_I64);
711 vassert(typeOfIRExpr(irbb->tyenv, t1) == Ity_I64);
712 vassert(typeOfIRExpr(irbb->tyenv, t0) == Ity_I64);
713 return binop(Iop_64HLtoV128,
714 binop(Iop_32HLto64,
715 mkQNarrow64Sto32( t3 ),
716 mkQNarrow64Sto32( t2 )),
717 binop(Iop_32HLto64,
718 mkQNarrow64Sto32( t1 ),
719 mkQNarrow64Sto32( t0 )));
720}
721
722/* Unsigned saturate narrow 64->32, combining to V128 */
723static IRExpr* mkV128from4x64U ( IRExpr* t3, IRExpr* t2,
724 IRExpr* t1, IRExpr* t0 )
725{
726 vassert(typeOfIRExpr(irbb->tyenv, t3) == Ity_I64);
727 vassert(typeOfIRExpr(irbb->tyenv, t2) == Ity_I64);
728 vassert(typeOfIRExpr(irbb->tyenv, t1) == Ity_I64);
729 vassert(typeOfIRExpr(irbb->tyenv, t0) == Ity_I64);
730 return binop(Iop_64HLtoV128,
731 binop(Iop_32HLto64,
732 mkQNarrow64Uto32( t3 ),
733 mkQNarrow64Uto32( t2 )),
734 binop(Iop_32HLto64,
735 mkQNarrow64Uto32( t1 ),
736 mkQNarrow64Uto32( t0 )));
737}
738
cerion24d06f12005-11-09 21:34:20 +0000739/* Simulate irops Iop_MullOdd*, since we don't have them */
740#define MK_Iop_MullOdd8Ux16( expr_vA, expr_vB ) \
741 binop(Iop_MullEven8Ux16, \
742 binop(Iop_ShrV128, expr_vA, mkU8(8)), \
743 binop(Iop_ShrV128, expr_vB, mkU8(8)))
744
745#define MK_Iop_MullOdd8Sx16( expr_vA, expr_vB ) \
746 binop(Iop_MullEven8Sx16, \
747 binop(Iop_ShrV128, expr_vA, mkU8(8)), \
748 binop(Iop_ShrV128, expr_vB, mkU8(8)))
749
750#define MK_Iop_MullOdd16Ux8( expr_vA, expr_vB ) \
751 binop(Iop_MullEven16Ux8, \
752 binop(Iop_ShrV128, expr_vA, mkU8(16)), \
753 binop(Iop_ShrV128, expr_vB, mkU8(16)))
754
755#define MK_Iop_MullOdd16Sx8( expr_vA, expr_vB ) \
756 binop(Iop_MullEven16Sx8, \
757 binop(Iop_ShrV128, expr_vA, mkU8(16)), \
758 binop(Iop_ShrV128, expr_vB, mkU8(16)))
759
cerion59b2c312005-12-17 11:28:53 +0000760static IRExpr* /* :: Ity_I64 */ mk64lo32Sto64 ( IRExpr* src )
ceriond953ebb2005-11-29 13:27:20 +0000761{
762 vassert(typeOfIRExpr(irbb->tyenv, src) == Ity_I64);
763 return unop(Iop_32Sto64, unop(Iop_64to32, src));
764}
765
cerion59b2c312005-12-17 11:28:53 +0000766static IRExpr* /* :: Ity_I64 */ mk64lo32Uto64 ( IRExpr* src )
cerionbb01b7c2005-12-16 13:40:18 +0000767{
768 vassert(typeOfIRExpr(irbb->tyenv, src) == Ity_I64);
769 return unop(Iop_32Uto64, unop(Iop_64to32, src));
770}
771
cerion2831b002005-11-30 19:55:22 +0000772static IROp mkSzOp ( IRType ty, IROp op8 )
ceriond953ebb2005-11-29 13:27:20 +0000773{
774 Int adj;
775 vassert(ty == Ity_I8 || ty == Ity_I16 ||
776 ty == Ity_I32 || ty == Ity_I64);
777 vassert(op8 == Iop_Add8 || op8 == Iop_Sub8 || op8 == Iop_Mul8 ||
778 op8 == Iop_Or8 || op8 == Iop_And8 || op8 == Iop_Xor8 ||
779 op8 == Iop_Shl8 || op8 == Iop_Shr8 || op8 == Iop_Sar8 ||
780 op8 == Iop_CmpEQ8 || op8 == Iop_CmpNE8 ||
781 op8 == Iop_Not8 || op8 == Iop_Neg8 );
782 adj = ty==Ity_I8 ? 0 : (ty==Ity_I16 ? 1 : (ty==Ity_I32 ? 2 : 3));
783 return adj + op8;
784}
785
cerion5b2325f2005-12-23 00:55:09 +0000786/* Make sure we get valid 32 and 64bit addresses */
cerion2831b002005-11-30 19:55:22 +0000787static Addr64 mkSzAddr ( IRType ty, Addr64 addr )
ceriond953ebb2005-11-29 13:27:20 +0000788{
789 vassert(ty == Ity_I32 || ty == Ity_I64);
790 return ( ty == Ity_I64 ?
791 (Addr64)addr :
792 (Addr64)extend_s_32to64( toUInt(addr) ) );
793}
794
795/* sz, ULong -> IRExpr */
cerion2831b002005-11-30 19:55:22 +0000796static IRExpr* mkSzImm ( IRType ty, ULong imm64 )
ceriond953ebb2005-11-29 13:27:20 +0000797{
798 vassert(ty == Ity_I32 || ty == Ity_I64);
799 return ty == Ity_I64 ? mkU64(imm64) : mkU32((UInt)imm64);
800}
801
802/* sz, ULong -> IRConst */
cerion2831b002005-11-30 19:55:22 +0000803static IRConst* mkSzConst ( IRType ty, ULong imm64 )
ceriond953ebb2005-11-29 13:27:20 +0000804{
805 vassert(ty == Ity_I32 || ty == Ity_I64);
806 return ( ty == Ity_I64 ?
807 IRConst_U64(imm64) :
808 IRConst_U32((UInt)imm64) );
809}
810
811/* Sign extend imm16 -> IRExpr* */
cerion2831b002005-11-30 19:55:22 +0000812static IRExpr* mkSzExtendS16 ( IRType ty, UInt imm16 )
ceriond953ebb2005-11-29 13:27:20 +0000813{
814 vassert(ty == Ity_I32 || ty == Ity_I64);
815 return ( ty == Ity_I64 ?
816 mkU64(extend_s_16to64(imm16)) :
817 mkU32(extend_s_16to32(imm16)) );
818}
819
820/* Sign extend imm32 -> IRExpr* */
cerion2831b002005-11-30 19:55:22 +0000821static IRExpr* mkSzExtendS32 ( IRType ty, UInt imm32 )
ceriond953ebb2005-11-29 13:27:20 +0000822{
823 vassert(ty == Ity_I32 || ty == Ity_I64);
824 return ( ty == Ity_I64 ?
825 mkU64(extend_s_32to64(imm32)) :
826 mkU32(imm32) );
827}
828
829/* IR narrows I32/I64 -> I8/I16/I32 */
cerion2831b002005-11-30 19:55:22 +0000830static IRExpr* mkSzNarrow8 ( IRType ty, IRExpr* src )
ceriond953ebb2005-11-29 13:27:20 +0000831{
832 vassert(ty == Ity_I32 || ty == Ity_I64);
833 return ty == Ity_I64 ? unop(Iop_64to8, src) : unop(Iop_32to8, src);
834}
835
cerion2831b002005-11-30 19:55:22 +0000836static IRExpr* mkSzNarrow16 ( IRType ty, IRExpr* src )
ceriond953ebb2005-11-29 13:27:20 +0000837{
838 vassert(ty == Ity_I32 || ty == Ity_I64);
839 return ty == Ity_I64 ? unop(Iop_64to16, src) : unop(Iop_32to16, src);
840}
841
cerion2831b002005-11-30 19:55:22 +0000842static IRExpr* mkSzNarrow32 ( IRType ty, IRExpr* src )
ceriond953ebb2005-11-29 13:27:20 +0000843{
844 vassert(ty == Ity_I32 || ty == Ity_I64);
845 return ty == Ity_I64 ? unop(Iop_64to32, src) : src;
846}
847
848/* Signed/Unsigned IR widens I8/I16/I32 -> I32/I64 */
cerion2831b002005-11-30 19:55:22 +0000849static IRExpr* mkSzWiden8 ( IRType ty, IRExpr* src, Bool sined )
ceriond953ebb2005-11-29 13:27:20 +0000850{
ceriond953ebb2005-11-29 13:27:20 +0000851 IROp op;
sewardj63327402006-01-25 03:26:27 +0000852 vassert(ty == Ity_I32 || ty == Ity_I64);
ceriond953ebb2005-11-29 13:27:20 +0000853 if (sined) op = (ty==Ity_I32) ? Iop_8Sto32 : Iop_8Sto64;
854 else op = (ty==Ity_I32) ? Iop_8Uto32 : Iop_8Uto64;
855 return unop(op, src);
856}
857
cerion2831b002005-11-30 19:55:22 +0000858static IRExpr* mkSzWiden16 ( IRType ty, IRExpr* src, Bool sined )
ceriond953ebb2005-11-29 13:27:20 +0000859{
ceriond953ebb2005-11-29 13:27:20 +0000860 IROp op;
sewardj63327402006-01-25 03:26:27 +0000861 vassert(ty == Ity_I32 || ty == Ity_I64);
ceriond953ebb2005-11-29 13:27:20 +0000862 if (sined) op = (ty==Ity_I32) ? Iop_16Sto32 : Iop_16Sto64;
863 else op = (ty==Ity_I32) ? Iop_16Uto32 : Iop_16Uto64;
864 return unop(op, src);
865}
866
cerion2831b002005-11-30 19:55:22 +0000867static IRExpr* mkSzWiden32 ( IRType ty, IRExpr* src, Bool sined )
ceriond953ebb2005-11-29 13:27:20 +0000868{
869 vassert(ty == Ity_I32 || ty == Ity_I64);
870 if (ty == Ity_I32)
871 return src;
872 return (sined) ? unop(Iop_32Sto64, src) : unop(Iop_32Uto64, src);
873}
cerion24d06f12005-11-09 21:34:20 +0000874
cerion4a49b032005-11-08 16:23:07 +0000875
sewardjb51f0f42005-07-18 11:38:02 +0000876static Int integerGuestRegOffset ( UInt archreg )
cerion45b70ff2005-01-31 17:03:25 +0000877{
sewardjb51f0f42005-07-18 11:38:02 +0000878 vassert(archreg < 32);
879
880 // jrs: probably not necessary; only matters if we reference sub-parts
cerion5b2325f2005-12-23 00:55:09 +0000881 // of the ppc registers, but that isn't the case
sewardjb51f0f42005-07-18 11:38:02 +0000882 // later: this might affect Altivec though?
883 vassert(host_is_bigendian);
884
cerion5b2325f2005-12-23 00:55:09 +0000885 switch (archreg) {
886 case 0: return offsetofPPCGuestState(guest_GPR0);
887 case 1: return offsetofPPCGuestState(guest_GPR1);
888 case 2: return offsetofPPCGuestState(guest_GPR2);
889 case 3: return offsetofPPCGuestState(guest_GPR3);
890 case 4: return offsetofPPCGuestState(guest_GPR4);
891 case 5: return offsetofPPCGuestState(guest_GPR5);
892 case 6: return offsetofPPCGuestState(guest_GPR6);
893 case 7: return offsetofPPCGuestState(guest_GPR7);
894 case 8: return offsetofPPCGuestState(guest_GPR8);
895 case 9: return offsetofPPCGuestState(guest_GPR9);
896 case 10: return offsetofPPCGuestState(guest_GPR10);
897 case 11: return offsetofPPCGuestState(guest_GPR11);
898 case 12: return offsetofPPCGuestState(guest_GPR12);
899 case 13: return offsetofPPCGuestState(guest_GPR13);
900 case 14: return offsetofPPCGuestState(guest_GPR14);
901 case 15: return offsetofPPCGuestState(guest_GPR15);
902 case 16: return offsetofPPCGuestState(guest_GPR16);
903 case 17: return offsetofPPCGuestState(guest_GPR17);
904 case 18: return offsetofPPCGuestState(guest_GPR18);
905 case 19: return offsetofPPCGuestState(guest_GPR19);
906 case 20: return offsetofPPCGuestState(guest_GPR20);
907 case 21: return offsetofPPCGuestState(guest_GPR21);
908 case 22: return offsetofPPCGuestState(guest_GPR22);
909 case 23: return offsetofPPCGuestState(guest_GPR23);
910 case 24: return offsetofPPCGuestState(guest_GPR24);
911 case 25: return offsetofPPCGuestState(guest_GPR25);
912 case 26: return offsetofPPCGuestState(guest_GPR26);
913 case 27: return offsetofPPCGuestState(guest_GPR27);
914 case 28: return offsetofPPCGuestState(guest_GPR28);
915 case 29: return offsetofPPCGuestState(guest_GPR29);
916 case 30: return offsetofPPCGuestState(guest_GPR30);
917 case 31: return offsetofPPCGuestState(guest_GPR31);
918 default: break;
sewardjb51f0f42005-07-18 11:38:02 +0000919 }
cerion5b2325f2005-12-23 00:55:09 +0000920 vpanic("integerGuestRegOffset(ppc,be)"); /*notreached*/
sewardjb51f0f42005-07-18 11:38:02 +0000921}
922
923static IRExpr* getIReg ( UInt archreg )
924{
ceriond953ebb2005-11-29 13:27:20 +0000925 IRType ty = mode64 ? Ity_I64 : Ity_I32;
sewardjb51f0f42005-07-18 11:38:02 +0000926 vassert(archreg < 32);
ceriond953ebb2005-11-29 13:27:20 +0000927 return IRExpr_Get( integerGuestRegOffset(archreg), ty );
sewardjb51f0f42005-07-18 11:38:02 +0000928}
929
930/* Ditto, but write to a reg instead. */
931static void putIReg ( UInt archreg, IRExpr* e )
932{
ceriond953ebb2005-11-29 13:27:20 +0000933 IRType ty = mode64 ? Ity_I64 : Ity_I32;
sewardjb51f0f42005-07-18 11:38:02 +0000934 vassert(archreg < 32);
ceriond953ebb2005-11-29 13:27:20 +0000935 vassert(typeOfIRExpr(irbb->tyenv, e) == ty );
sewardjb51f0f42005-07-18 11:38:02 +0000936 stmt( IRStmt_Put(integerGuestRegOffset(archreg), e) );
937}
938
939
940static Int floatGuestRegOffset ( UInt archreg )
941{
942 vassert(archreg < 32);
943
cerion5b2325f2005-12-23 00:55:09 +0000944 switch (archreg) {
945 case 0: return offsetofPPCGuestState(guest_FPR0);
946 case 1: return offsetofPPCGuestState(guest_FPR1);
947 case 2: return offsetofPPCGuestState(guest_FPR2);
948 case 3: return offsetofPPCGuestState(guest_FPR3);
949 case 4: return offsetofPPCGuestState(guest_FPR4);
950 case 5: return offsetofPPCGuestState(guest_FPR5);
951 case 6: return offsetofPPCGuestState(guest_FPR6);
952 case 7: return offsetofPPCGuestState(guest_FPR7);
953 case 8: return offsetofPPCGuestState(guest_FPR8);
954 case 9: return offsetofPPCGuestState(guest_FPR9);
955 case 10: return offsetofPPCGuestState(guest_FPR10);
956 case 11: return offsetofPPCGuestState(guest_FPR11);
957 case 12: return offsetofPPCGuestState(guest_FPR12);
958 case 13: return offsetofPPCGuestState(guest_FPR13);
959 case 14: return offsetofPPCGuestState(guest_FPR14);
960 case 15: return offsetofPPCGuestState(guest_FPR15);
961 case 16: return offsetofPPCGuestState(guest_FPR16);
962 case 17: return offsetofPPCGuestState(guest_FPR17);
963 case 18: return offsetofPPCGuestState(guest_FPR18);
964 case 19: return offsetofPPCGuestState(guest_FPR19);
965 case 20: return offsetofPPCGuestState(guest_FPR20);
966 case 21: return offsetofPPCGuestState(guest_FPR21);
967 case 22: return offsetofPPCGuestState(guest_FPR22);
968 case 23: return offsetofPPCGuestState(guest_FPR23);
969 case 24: return offsetofPPCGuestState(guest_FPR24);
970 case 25: return offsetofPPCGuestState(guest_FPR25);
971 case 26: return offsetofPPCGuestState(guest_FPR26);
972 case 27: return offsetofPPCGuestState(guest_FPR27);
973 case 28: return offsetofPPCGuestState(guest_FPR28);
974 case 29: return offsetofPPCGuestState(guest_FPR29);
975 case 30: return offsetofPPCGuestState(guest_FPR30);
976 case 31: return offsetofPPCGuestState(guest_FPR31);
977 default: break;
sewardjb51f0f42005-07-18 11:38:02 +0000978 }
cerion5b2325f2005-12-23 00:55:09 +0000979 vpanic("floatGuestRegOffset(ppc)"); /*notreached*/
sewardjb51f0f42005-07-18 11:38:02 +0000980}
981
982static IRExpr* getFReg ( UInt archreg )
983{
984 vassert(archreg < 32);
985 return IRExpr_Get( floatGuestRegOffset(archreg), Ity_F64 );
986}
987
988/* Ditto, but write to a reg instead. */
989static void putFReg ( UInt archreg, IRExpr* e )
990{
991 vassert(archreg < 32);
992 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_F64);
993 stmt( IRStmt_Put(floatGuestRegOffset(archreg), e) );
994}
995
996
997static Int vectorGuestRegOffset ( UInt archreg )
998{
999 vassert(archreg < 32);
1000
cerion5b2325f2005-12-23 00:55:09 +00001001 switch (archreg) {
1002 case 0: return offsetofPPCGuestState(guest_VR0);
1003 case 1: return offsetofPPCGuestState(guest_VR1);
1004 case 2: return offsetofPPCGuestState(guest_VR2);
1005 case 3: return offsetofPPCGuestState(guest_VR3);
1006 case 4: return offsetofPPCGuestState(guest_VR4);
1007 case 5: return offsetofPPCGuestState(guest_VR5);
1008 case 6: return offsetofPPCGuestState(guest_VR6);
1009 case 7: return offsetofPPCGuestState(guest_VR7);
1010 case 8: return offsetofPPCGuestState(guest_VR8);
1011 case 9: return offsetofPPCGuestState(guest_VR9);
1012 case 10: return offsetofPPCGuestState(guest_VR10);
1013 case 11: return offsetofPPCGuestState(guest_VR11);
1014 case 12: return offsetofPPCGuestState(guest_VR12);
1015 case 13: return offsetofPPCGuestState(guest_VR13);
1016 case 14: return offsetofPPCGuestState(guest_VR14);
1017 case 15: return offsetofPPCGuestState(guest_VR15);
1018 case 16: return offsetofPPCGuestState(guest_VR16);
1019 case 17: return offsetofPPCGuestState(guest_VR17);
1020 case 18: return offsetofPPCGuestState(guest_VR18);
1021 case 19: return offsetofPPCGuestState(guest_VR19);
1022 case 20: return offsetofPPCGuestState(guest_VR20);
1023 case 21: return offsetofPPCGuestState(guest_VR21);
1024 case 22: return offsetofPPCGuestState(guest_VR22);
1025 case 23: return offsetofPPCGuestState(guest_VR23);
1026 case 24: return offsetofPPCGuestState(guest_VR24);
1027 case 25: return offsetofPPCGuestState(guest_VR25);
1028 case 26: return offsetofPPCGuestState(guest_VR26);
1029 case 27: return offsetofPPCGuestState(guest_VR27);
1030 case 28: return offsetofPPCGuestState(guest_VR28);
1031 case 29: return offsetofPPCGuestState(guest_VR29);
1032 case 30: return offsetofPPCGuestState(guest_VR30);
1033 case 31: return offsetofPPCGuestState(guest_VR31);
1034 default: break;
sewardjb51f0f42005-07-18 11:38:02 +00001035 }
cerion5b2325f2005-12-23 00:55:09 +00001036 vpanic("vextorGuestRegOffset(ppc)"); /*notreached*/
sewardjb51f0f42005-07-18 11:38:02 +00001037}
1038
1039static IRExpr* getVReg ( UInt archreg )
1040{
1041 vassert(archreg < 32);
1042 return IRExpr_Get( vectorGuestRegOffset(archreg), Ity_V128 );
1043}
1044
1045/* Ditto, but write to a reg instead. */
1046static void putVReg ( UInt archreg, IRExpr* e )
1047{
1048 vassert(archreg < 32);
1049 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_V128);
1050 stmt( IRStmt_Put(vectorGuestRegOffset(archreg), e) );
1051}
1052
1053static Int guestCR321offset ( UInt cr )
1054{
cerion5b2325f2005-12-23 00:55:09 +00001055 switch (cr) {
1056 case 0: return offsetofPPCGuestState(guest_CR0_321 );
1057 case 1: return offsetofPPCGuestState(guest_CR1_321 );
1058 case 2: return offsetofPPCGuestState(guest_CR2_321 );
1059 case 3: return offsetofPPCGuestState(guest_CR3_321 );
1060 case 4: return offsetofPPCGuestState(guest_CR4_321 );
1061 case 5: return offsetofPPCGuestState(guest_CR5_321 );
1062 case 6: return offsetofPPCGuestState(guest_CR6_321 );
1063 case 7: return offsetofPPCGuestState(guest_CR7_321 );
1064 default: vpanic("guestCR321offset(ppc)");
sewardjb51f0f42005-07-18 11:38:02 +00001065 }
1066}
1067
1068static Int guestCR0offset ( UInt cr )
1069{
cerion5b2325f2005-12-23 00:55:09 +00001070 switch (cr) {
1071 case 0: return offsetofPPCGuestState(guest_CR0_0 );
1072 case 1: return offsetofPPCGuestState(guest_CR1_0 );
1073 case 2: return offsetofPPCGuestState(guest_CR2_0 );
1074 case 3: return offsetofPPCGuestState(guest_CR3_0 );
1075 case 4: return offsetofPPCGuestState(guest_CR4_0 );
1076 case 5: return offsetofPPCGuestState(guest_CR5_0 );
1077 case 6: return offsetofPPCGuestState(guest_CR6_0 );
1078 case 7: return offsetofPPCGuestState(guest_CR7_0 );
1079 default: vpanic("guestCR3offset(ppc)");
sewardjb51f0f42005-07-18 11:38:02 +00001080 }
sewardjb51f0f42005-07-18 11:38:02 +00001081}
1082
cerion07b07a92005-12-22 14:32:35 +00001083// ROTL(src32/64, rot_amt5/6)
ceriond953ebb2005-11-29 13:27:20 +00001084static IRExpr* /* :: Ity_I32/64 */ ROTL ( IRExpr* src,
1085 IRExpr* rot_amt )
sewardjb51f0f42005-07-18 11:38:02 +00001086{
ceriond953ebb2005-11-29 13:27:20 +00001087 IRExpr *mask, *rot;
cerionf0de28c2005-12-13 20:21:11 +00001088 vassert(typeOfIRExpr(irbb->tyenv,rot_amt) == Ity_I8);
sewardjb51f0f42005-07-18 11:38:02 +00001089
cerionf0de28c2005-12-13 20:21:11 +00001090 if (typeOfIRExpr(irbb->tyenv,src) == Ity_I64) {
ceriond953ebb2005-11-29 13:27:20 +00001091 // rot = (src << rot_amt) | (src >> (64-rot_amt))
1092 mask = binop(Iop_And8, rot_amt, mkU8(63));
1093 rot = binop(Iop_Or64,
1094 binop(Iop_Shl64, src, mask),
1095 binop(Iop_Shr64, src, binop(Iop_Sub8, mkU8(64), mask)));
1096 } else {
ceriond953ebb2005-11-29 13:27:20 +00001097 // rot = (src << rot_amt) | (src >> (32-rot_amt))
cerionf0de28c2005-12-13 20:21:11 +00001098 mask = binop(Iop_And8, rot_amt, mkU8(31));
ceriond953ebb2005-11-29 13:27:20 +00001099 rot = binop(Iop_Or32,
1100 binop(Iop_Shl32, src, mask),
1101 binop(Iop_Shr32, src, binop(Iop_Sub8, mkU8(32), mask)));
cerion2831b002005-11-30 19:55:22 +00001102 }
sewardjc9659532005-07-21 21:33:57 +00001103 /* Note: the MuxOX is not merely an optimisation; it's needed
cerionf0de28c2005-12-13 20:21:11 +00001104 because otherwise the Shr is a shift by the word size when
ceriond953ebb2005-11-29 13:27:20 +00001105 mask denotes zero. For rotates by immediates, a lot of
sewardjc9659532005-07-21 21:33:57 +00001106 this junk gets folded out. */
ceriond953ebb2005-11-29 13:27:20 +00001107 return IRExpr_Mux0X( mask, /* zero rotate */ src,
1108 /* non-zero rotate */ rot );
cerion45b70ff2005-01-31 17:03:25 +00001109}
cerion896a1372005-01-25 12:24:25 +00001110
ceriond953ebb2005-11-29 13:27:20 +00001111#if 0
1112/* ROTL32_64(src64, rot_amt5)
1113 Weirdo 32bit rotl on ppc64:
1114 rot32 = ROTL(src_lo32,y);
1115 return (rot32|rot32);
1116*/
1117static IRExpr* /* :: Ity_I64 */ ROTL32_64 ( IRExpr* src64,
1118 IRExpr* rot_amt )
sewardj87e651f2005-09-09 08:31:18 +00001119{
ceriond953ebb2005-11-29 13:27:20 +00001120 IRExpr *mask, *rot32;
1121 vassert(mode64); // used only in 64bit mode
1122 vassert(typeOfIRExpr(irbb->tyenv,src64) == Ity_I64);
1123 vassert(typeOfIRExpr(irbb->tyenv,rot_amt) == Ity_I8);
1124
1125 mask = binop(Iop_And8, rot_amt, mkU8(31));
1126 rot32 = ROTL( unop(Iop_64to32, src64), rot_amt );
1127
1128 return binop(Iop_Or64,
1129 binop(Iop_Shl64, unop(Iop_32Uto64, rot32), mkU8(32)),
cerion2831b002005-11-30 19:55:22 +00001130 unop(Iop_32Uto64, rot32));
ceriond953ebb2005-11-29 13:27:20 +00001131}
1132#endif
1133
1134
1135/* Standard effective address calc: (rA + rB) */
1136static IRExpr* ea_rA_idxd ( UInt rA, UInt rB )
1137{
1138 IRType ty = mode64 ? Ity_I64 : Ity_I32;
1139 vassert(rA < 32);
1140 vassert(rB < 32);
cerion2831b002005-11-30 19:55:22 +00001141 return binop(mkSzOp(ty, Iop_Add8), getIReg(rA), getIReg(rB));
sewardj87e651f2005-09-09 08:31:18 +00001142}
1143
ceriond953ebb2005-11-29 13:27:20 +00001144/* Standard effective address calc: (rA + simm) */
1145static IRExpr* ea_rA_simm ( UInt rA, UInt simm16 )
sewardj87e651f2005-09-09 08:31:18 +00001146{
ceriond953ebb2005-11-29 13:27:20 +00001147 IRType ty = mode64 ? Ity_I64 : Ity_I32;
1148 vassert(rA < 32);
cerion2831b002005-11-30 19:55:22 +00001149 return binop(mkSzOp(ty, Iop_Add8), getIReg(rA),
1150 mkSzExtendS16(ty, simm16));
ceriond953ebb2005-11-29 13:27:20 +00001151}
1152
1153/* Standard effective address calc: (rA|0) */
1154static IRExpr* ea_rAor0 ( UInt rA )
1155{
1156 IRType ty = mode64 ? Ity_I64 : Ity_I32;
1157 vassert(rA < 32);
sewardj87e651f2005-09-09 08:31:18 +00001158 if (rA == 0) {
cerion2831b002005-11-30 19:55:22 +00001159 return mkSzImm(ty, 0);
sewardj87e651f2005-09-09 08:31:18 +00001160 } else {
1161 return getIReg(rA);
1162 }
1163}
1164
ceriond953ebb2005-11-29 13:27:20 +00001165/* Standard effective address calc: (rA|0) + rB */
1166static IRExpr* ea_rAor0_idxd ( UInt rA, UInt rB )
1167{
1168 vassert(rA < 32);
1169 vassert(rB < 32);
1170 return (rA == 0) ? getIReg(rB) : ea_rA_idxd( rA, rB );
1171}
1172
1173/* Standard effective address calc: (rA|0) + simm16 */
1174static IRExpr* ea_rAor0_simm ( UInt rA, UInt simm16 )
1175{
1176 IRType ty = mode64 ? Ity_I64 : Ity_I32;
1177 vassert(rA < 32);
1178 if (rA == 0) {
cerion2831b002005-11-30 19:55:22 +00001179 return mkSzExtendS16(ty, simm16);
ceriond953ebb2005-11-29 13:27:20 +00001180 } else {
1181 return ea_rA_simm( rA, simm16 );
1182 }
1183}
1184
1185
1186/* Align effective address */
1187static IRExpr* addr_align( IRExpr* addr, UChar align )
1188{
1189 IRType ty = mode64 ? Ity_I64 : Ity_I32;
1190 Long mask;
1191 switch (align) {
cerion2831b002005-11-30 19:55:22 +00001192 case 1: return addr; // byte aligned
ceriond953ebb2005-11-29 13:27:20 +00001193 case 2: mask = ((Long)-1) << 1; break; // half-word aligned
1194 case 4: mask = ((Long)-1) << 2; break; // word aligned
1195 case 16: mask = ((Long)-1) << 4; break; // quad-word aligned
1196 default:
1197 vex_printf("addr_align: align = %u\n", align);
cerion5b2325f2005-12-23 00:55:09 +00001198 vpanic("addr_align(ppc)");
ceriond953ebb2005-11-29 13:27:20 +00001199 }
1200
1201 vassert(typeOfIRExpr(irbb->tyenv,addr) == ty);
cerionfb197c42005-12-24 12:32:10 +00001202 return binop( mkSzOp(ty, Iop_And8), addr, mkSzImm(ty, mask) );
ceriond953ebb2005-11-29 13:27:20 +00001203}
1204
cerion896a1372005-01-25 12:24:25 +00001205
sewardjcf8986c2006-01-18 04:14:52 +00001206/* Generate AbiHints which mark points at which the ELF ppc64 ABI says
1207 that the stack red zone (viz, -288(r1) .. -1(r1)) becomes
1208 undefined. That is at function calls and returns. Only in 64-bit
1209 mode - ELF ppc32 doesn't have this "feature".
1210*/
1211static void make_redzone_AbiHint ( HChar* who )
1212{
1213 if (0) vex_printf("AbiHint: %s\n", who);
1214 vassert(mode64);
1215 stmt( IRStmt_AbiHint(
1216 binop(Iop_Sub64, getIReg(1), mkU64(288)),
1217 288
1218 ));
1219}
1220
1221
cerion896a1372005-01-25 12:24:25 +00001222/*------------------------------------------------------------*/
sewardjb51f0f42005-07-18 11:38:02 +00001223/*--- Helpers for condition codes. ---*/
cerion896a1372005-01-25 12:24:25 +00001224/*------------------------------------------------------------*/
1225
sewardjb51f0f42005-07-18 11:38:02 +00001226/* Condition register layout.
cerion896a1372005-01-25 12:24:25 +00001227
sewardjb51f0f42005-07-18 11:38:02 +00001228 In the hardware, CR is laid out like this. The leftmost end is the
1229 most significant bit in the register; however the IBM documentation
1230 numbers the bits backwards for some reason.
1231
1232 CR0 CR1 .......... CR6 CR7
1233 0 .. 3 ....................... 28 .. 31 (IBM bit numbering)
1234 31 28 3 0 (normal bit numbering)
1235
cerionedf7fc52005-11-18 20:57:41 +00001236 Each CR field is 4 bits: [<,>,==,SO]
sewardjb51f0f42005-07-18 11:38:02 +00001237
cerionedf7fc52005-11-18 20:57:41 +00001238 Hence in IBM's notation, BI=0 is CR7[SO], BI=1 is CR7[==], etc.
sewardjb51f0f42005-07-18 11:38:02 +00001239
1240 Indexing from BI to guest state:
1241
1242 let n = BI / 4
1243 off = BI % 4
1244 this references CR n:
1245
cerionedf7fc52005-11-18 20:57:41 +00001246 off==0 -> guest_CRn_321 >> 3
1247 off==1 -> guest_CRn_321 >> 2
1248 off==2 -> guest_CRn_321 >> 1
sewardjb51f0f42005-07-18 11:38:02 +00001249 off==3 -> guest_CRn_SO
sewardjb51f0f42005-07-18 11:38:02 +00001250
1251 Bear in mind the only significant bit in guest_CRn_SO is bit 0
cerionedf7fc52005-11-18 20:57:41 +00001252 (normal notation) and in guest_CRn_321 the significant bits are
sewardjb51f0f42005-07-18 11:38:02 +00001253 3, 2 and 1 (normal notation).
1254*/
cerionedf7fc52005-11-18 20:57:41 +00001255
1256static void putCR321 ( UInt cr, IRExpr* e )
1257{
1258 vassert(cr < 8);
1259 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_I8);
1260 stmt( IRStmt_Put(guestCR321offset(cr), e) );
1261}
1262
1263static void putCR0 ( UInt cr, IRExpr* e )
1264{
1265 vassert(cr < 8);
1266 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_I8);
1267 stmt( IRStmt_Put(guestCR0offset(cr), e) );
1268}
1269
1270static IRExpr* /* :: Ity_I8 */ getCR0 ( UInt cr )
1271{
1272 vassert(cr < 8);
1273 return IRExpr_Get(guestCR0offset(cr), Ity_I8);
1274}
1275
1276static IRExpr* /* :: Ity_I8 */ getCR321 ( UInt cr )
1277{
1278 vassert(cr < 8);
1279 return IRExpr_Get(guestCR321offset(cr), Ity_I8);
1280}
1281
sewardjb51f0f42005-07-18 11:38:02 +00001282/* Fetch the specified CR bit (as per IBM/hardware notation) and
1283 return it at the bottom of an I32; the top 31 bits are guaranteed
1284 to be zero. */
1285static IRExpr* /* :: Ity_I32 */ getCRbit ( UInt bi )
cerion896a1372005-01-25 12:24:25 +00001286{
sewardjb51f0f42005-07-18 11:38:02 +00001287 UInt n = bi / 4;
1288 UInt off = bi % 4;
1289 vassert(bi < 32);
1290 if (off == 3) {
1291 /* Fetch the SO bit for this CR field */
1292 /* Note: And32 is redundant paranoia iff guest state only has 0
1293 or 1 in that slot. */
1294 return binop(Iop_And32, unop(Iop_8Uto32, getCR0(n)), mkU32(1));
1295 } else {
1296 /* Fetch the <, > or == bit for this CR field */
1297 return binop( Iop_And32,
1298 binop( Iop_Shr32,
1299 unop(Iop_8Uto32, getCR321(n)),
sewardjc7cd2142005-09-09 22:31:49 +00001300 mkU8(toUChar(3-off)) ),
sewardjb51f0f42005-07-18 11:38:02 +00001301 mkU32(1) );
1302 }
cerion91ad5362005-01-27 23:02:41 +00001303}
1304
sewardjb51f0f42005-07-18 11:38:02 +00001305/* Dually, write the least significant bit of BIT to the specified CR
1306 bit. Indexing as per getCRbit. */
1307static void putCRbit ( UInt bi, IRExpr* bit )
1308{
sewardj197bd172005-10-12 11:34:33 +00001309 UInt n, off;
sewardjb51f0f42005-07-18 11:38:02 +00001310 IRExpr* safe;
1311 vassert(typeOfIRExpr(irbb->tyenv,bit) == Ity_I32);
1312 safe = binop(Iop_And32, bit, mkU32(1));
sewardj197bd172005-10-12 11:34:33 +00001313 n = bi / 4;
1314 off = bi % 4;
sewardjb51f0f42005-07-18 11:38:02 +00001315 vassert(bi < 32);
1316 if (off == 3) {
1317 /* This is the SO bit for this CR field */
1318 putCR0(n, unop(Iop_32to8, safe));
1319 } else {
1320 off = 3 - off;
1321 vassert(off == 1 || off == 2 || off == 3);
1322 putCR321(
1323 n,
1324 unop( Iop_32to8,
1325 binop( Iop_Or32,
1326 /* old value with field masked out */
1327 binop(Iop_And32, unop(Iop_8Uto32, getCR321(n)),
1328 mkU32(~(1 << off))),
1329 /* new value in the right place */
sewardjc7cd2142005-09-09 22:31:49 +00001330 binop(Iop_Shl32, safe, mkU8(toUChar(off)))
sewardjb51f0f42005-07-18 11:38:02 +00001331 )
1332 )
1333 );
1334 }
1335}
1336
sewardjb51f0f42005-07-18 11:38:02 +00001337/* Fetch the specified CR bit (as per IBM/hardware notation) and
1338 return it somewhere in an I32; it does not matter where, but
1339 whichever bit it is, all other bits are guaranteed to be zero. In
1340 other words, the I32-typed expression will be zero if the bit is
1341 zero and nonzero if the bit is 1. Write into *where the index
1342 of where the bit will be. */
1343
cerion5b2325f2005-12-23 00:55:09 +00001344static
1345IRExpr* /* :: Ity_I32 */ getCRbit_anywhere ( UInt bi, Int* where )
sewardjb51f0f42005-07-18 11:38:02 +00001346{
1347 UInt n = bi / 4;
1348 UInt off = bi % 4;
1349 vassert(bi < 32);
1350 if (off == 3) {
1351 /* Fetch the SO bit for this CR field */
1352 /* Note: And32 is redundant paranoia iff guest state only has 0
1353 or 1 in that slot. */
1354 *where = 0;
1355 return binop(Iop_And32, unop(Iop_8Uto32, getCR0(n)), mkU32(1));
1356 } else {
1357 /* Fetch the <, > or == bit for this CR field */
1358 *where = 3-off;
1359 return binop( Iop_And32,
1360 unop(Iop_8Uto32, getCR321(n)),
1361 mkU32(1 << (3-off)) );
1362 }
1363}
1364
sewardjb51f0f42005-07-18 11:38:02 +00001365/* Set the CR0 flags following an arithmetic operation.
1366 (Condition Register CR0 Field Definition, PPC32 p60)
cerion896a1372005-01-25 12:24:25 +00001367*/
cerionedf7fc52005-11-18 20:57:41 +00001368static IRExpr* getXER_SO ( void );
sewardj20ef5472005-07-21 14:48:31 +00001369static void set_CR0 ( IRExpr* result )
cerion896a1372005-01-25 12:24:25 +00001370{
ceriond953ebb2005-11-29 13:27:20 +00001371 vassert(typeOfIRExpr(irbb->tyenv,result) == Ity_I32 ||
1372 typeOfIRExpr(irbb->tyenv,result) == Ity_I64);
1373 if (mode64) {
ceriond953ebb2005-11-29 13:27:20 +00001374 putCR321( 0, unop(Iop_64to8,
cerion2831b002005-11-30 19:55:22 +00001375 binop(Iop_CmpORD64S, result, mkU64(0))) );
ceriond953ebb2005-11-29 13:27:20 +00001376 } else {
1377 putCR321( 0, unop(Iop_32to8,
1378 binop(Iop_CmpORD32S, result, mkU32(0))) );
1379 }
sewardjb51f0f42005-07-18 11:38:02 +00001380 putCR0( 0, getXER_SO() );
cerion896a1372005-01-25 12:24:25 +00001381}
cerion896a1372005-01-25 12:24:25 +00001382
sewardj20ef5472005-07-21 14:48:31 +00001383
cerionedf7fc52005-11-18 20:57:41 +00001384/* Set the CR6 flags following an AltiVec compare operation. */
1385static void set_AV_CR6 ( IRExpr* result, Bool test_all_ones )
1386{
1387 /* CR6[0:3] = {all_ones, 0, all_zeros, 0}
1388 all_ones = (v[0] && v[1] && v[2] && v[3])
1389 all_zeros = ~(v[0] || v[1] || v[2] || v[3])
1390 */
1391 IRTemp v0 = newTemp(Ity_V128);
1392 IRTemp v1 = newTemp(Ity_V128);
1393 IRTemp v2 = newTemp(Ity_V128);
1394 IRTemp v3 = newTemp(Ity_V128);
1395 IRTemp rOnes = newTemp(Ity_I8);
1396 IRTemp rZeros = newTemp(Ity_I8);
1397
1398 vassert(typeOfIRExpr(irbb->tyenv,result) == Ity_V128);
1399
1400 assign( v0, result );
1401 assign( v1, binop(Iop_ShrV128, result, mkU8(32)) );
1402 assign( v2, binop(Iop_ShrV128, result, mkU8(64)) );
1403 assign( v3, binop(Iop_ShrV128, result, mkU8(96)) );
1404
1405 assign( rZeros, unop(Iop_1Uto8,
1406 binop(Iop_CmpEQ32, mkU32(0xFFFFFFFF),
1407 unop(Iop_Not32,
1408 unop(Iop_V128to32,
1409 binop(Iop_OrV128,
1410 binop(Iop_OrV128, mkexpr(v0), mkexpr(v1)),
1411 binop(Iop_OrV128, mkexpr(v2), mkexpr(v3))))
1412 ))) );
1413
1414 if (test_all_ones) {
1415 assign( rOnes, unop(Iop_1Uto8,
1416 binop(Iop_CmpEQ32, mkU32(0xFFFFFFFF),
1417 unop(Iop_V128to32,
1418 binop(Iop_AndV128,
1419 binop(Iop_AndV128, mkexpr(v0), mkexpr(v1)),
cerion5b2325f2005-12-23 00:55:09 +00001420 binop(Iop_AndV128, mkexpr(v2), mkexpr(v3)))
1421 ))) );
cerionedf7fc52005-11-18 20:57:41 +00001422 putCR321( 6, binop(Iop_Or8,
1423 binop(Iop_Shl8, mkexpr(rOnes), mkU8(3)),
1424 binop(Iop_Shl8, mkexpr(rZeros), mkU8(1))) );
1425 } else {
1426 putCR321( 6, binop(Iop_Shl8, mkexpr(rZeros), mkU8(1)) );
1427 }
1428 putCR0( 6, mkU8(0) );
1429}
1430
1431
1432
1433/*------------------------------------------------------------*/
1434/*--- Helpers for XER flags. ---*/
1435/*------------------------------------------------------------*/
1436
1437static void putXER_SO ( IRExpr* e )
1438{
sewardj63327402006-01-25 03:26:27 +00001439 IRExpr* so;
cerionedf7fc52005-11-18 20:57:41 +00001440 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_I8);
sewardj63327402006-01-25 03:26:27 +00001441 so = binop(Iop_And8, e, mkU8(1));
cerion5b2325f2005-12-23 00:55:09 +00001442 stmt( IRStmt_Put( OFFB_XER_SO, so ) );
cerionedf7fc52005-11-18 20:57:41 +00001443}
1444
1445static void putXER_OV ( IRExpr* e )
1446{
sewardj63327402006-01-25 03:26:27 +00001447 IRExpr* ov;
cerionedf7fc52005-11-18 20:57:41 +00001448 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_I8);
sewardj63327402006-01-25 03:26:27 +00001449 ov = binop(Iop_And8, e, mkU8(1));
cerion5b2325f2005-12-23 00:55:09 +00001450 stmt( IRStmt_Put( OFFB_XER_OV, ov ) );
cerionedf7fc52005-11-18 20:57:41 +00001451}
1452
1453static void putXER_CA ( IRExpr* e )
1454{
sewardj63327402006-01-25 03:26:27 +00001455 IRExpr* ca;
cerionedf7fc52005-11-18 20:57:41 +00001456 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_I8);
sewardj63327402006-01-25 03:26:27 +00001457 ca = binop(Iop_And8, e, mkU8(1));
cerion5b2325f2005-12-23 00:55:09 +00001458 stmt( IRStmt_Put( OFFB_XER_CA, ca ) );
cerionedf7fc52005-11-18 20:57:41 +00001459}
1460
1461static void putXER_BC ( IRExpr* e )
1462{
sewardj63327402006-01-25 03:26:27 +00001463 IRExpr* bc;
cerionedf7fc52005-11-18 20:57:41 +00001464 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_I8);
sewardj63327402006-01-25 03:26:27 +00001465 bc = binop(Iop_And8, e, mkU8(0x7F));
cerion5b2325f2005-12-23 00:55:09 +00001466 stmt( IRStmt_Put( OFFB_XER_BC, bc ) );
cerionedf7fc52005-11-18 20:57:41 +00001467}
1468
1469static IRExpr* /* :: Ity_I8 */ getXER_SO ( void )
1470{
cerion5b2325f2005-12-23 00:55:09 +00001471 return IRExpr_Get( OFFB_XER_SO, Ity_I8 );
cerionedf7fc52005-11-18 20:57:41 +00001472}
1473
1474static IRExpr* /* :: Ity_I32 */ getXER_SO32 ( void )
1475{
1476 return binop( Iop_And32, unop(Iop_8Uto32, getXER_SO()), mkU32(1) );
1477}
1478
1479static IRExpr* /* :: Ity_I8 */ getXER_OV ( void )
1480{
cerion5b2325f2005-12-23 00:55:09 +00001481 return IRExpr_Get( OFFB_XER_OV, Ity_I8 );
cerionedf7fc52005-11-18 20:57:41 +00001482}
1483
1484static IRExpr* /* :: Ity_I32 */ getXER_OV32 ( void )
1485{
1486 return binop( Iop_And32, unop(Iop_8Uto32, getXER_OV()), mkU32(1) );
1487}
1488
1489static IRExpr* /* :: Ity_I32 */ getXER_CA32 ( void )
1490{
cerion5b2325f2005-12-23 00:55:09 +00001491 IRExpr* ca = IRExpr_Get( OFFB_XER_CA, Ity_I8 );
ceriond953ebb2005-11-29 13:27:20 +00001492 return binop( Iop_And32, unop(Iop_8Uto32, ca ), mkU32(1) );
cerionedf7fc52005-11-18 20:57:41 +00001493}
1494
1495static IRExpr* /* :: Ity_I8 */ getXER_BC ( void )
1496{
cerion5b2325f2005-12-23 00:55:09 +00001497 return IRExpr_Get( OFFB_XER_BC, Ity_I8 );
cerionedf7fc52005-11-18 20:57:41 +00001498}
1499
1500static IRExpr* /* :: Ity_I32 */ getXER_BC32 ( void )
1501{
cerion5b2325f2005-12-23 00:55:09 +00001502 IRExpr* bc = IRExpr_Get( OFFB_XER_BC, Ity_I8 );
ceriond953ebb2005-11-29 13:27:20 +00001503 return binop( Iop_And32, unop(Iop_8Uto32, bc), mkU32(0x7F) );
cerionedf7fc52005-11-18 20:57:41 +00001504}
1505
1506
sewardj20ef5472005-07-21 14:48:31 +00001507/* RES is the result of doing OP on ARGL and ARGR. Set %XER.OV and
1508 %XER.SO accordingly. */
1509
ceriond953ebb2005-11-29 13:27:20 +00001510static void set_XER_OV_32( UInt op, IRExpr* res,
1511 IRExpr* argL, IRExpr* argR )
sewardj20ef5472005-07-21 14:48:31 +00001512{
1513 IRTemp t64;
1514 IRExpr* xer_ov;
cerion5b2325f2005-12-23 00:55:09 +00001515 vassert(op < PPCG_FLAG_OP_NUMBER);
ceriond953ebb2005-11-29 13:27:20 +00001516 vassert(typeOfIRExpr(irbb->tyenv,res) == Ity_I32);
sewardj20ef5472005-07-21 14:48:31 +00001517 vassert(typeOfIRExpr(irbb->tyenv,argL) == Ity_I32);
1518 vassert(typeOfIRExpr(irbb->tyenv,argR) == Ity_I32);
1519
1520# define INT32_MIN 0x80000000
1521
1522# define XOR2(_aa,_bb) \
1523 binop(Iop_Xor32,(_aa),(_bb))
1524
1525# define XOR3(_cc,_dd,_ee) \
1526 binop(Iop_Xor32,binop(Iop_Xor32,(_cc),(_dd)),(_ee))
1527
1528# define AND3(_ff,_gg,_hh) \
1529 binop(Iop_And32,binop(Iop_And32,(_ff),(_gg)),(_hh))
1530
1531#define NOT(_jj) \
1532 unop(Iop_Not32, (_jj))
1533
1534 switch (op) {
cerion5b2325f2005-12-23 00:55:09 +00001535 case /* 0 */ PPCG_FLAG_OP_ADD:
1536 case /* 1 */ PPCG_FLAG_OP_ADDE:
ceriond953ebb2005-11-29 13:27:20 +00001537 /* (argL^argR^-1) & (argL^res) & (1<<31) ?1:0 */
1538 // i.e. ((both_same_sign) & (sign_changed) & (sign_mask))
1539 xer_ov
1540 = AND3( XOR3(argL,argR,mkU32(-1)),
1541 XOR2(argL,res),
1542 mkU32(INT32_MIN) );
1543 /* xer_ov can only be 0 or 1<<31 */
1544 xer_ov
1545 = binop(Iop_Shr32, xer_ov, mkU8(31) );
1546 break;
1547
cerion5b2325f2005-12-23 00:55:09 +00001548 case /* 2 */ PPCG_FLAG_OP_DIVW:
ceriond953ebb2005-11-29 13:27:20 +00001549 /* (argL == INT32_MIN && argR == -1) || argR == 0 */
1550 xer_ov
1551 = mkOR1(
1552 mkAND1(
1553 binop(Iop_CmpEQ32, argL, mkU32(INT32_MIN)),
1554 binop(Iop_CmpEQ32, argR, mkU32(-1))
1555 ),
1556 binop(Iop_CmpEQ32, argR, mkU32(0) )
1557 );
1558 xer_ov
1559 = unop(Iop_1Uto32, xer_ov);
1560 break;
1561
cerion5b2325f2005-12-23 00:55:09 +00001562 case /* 3 */ PPCG_FLAG_OP_DIVWU:
ceriond953ebb2005-11-29 13:27:20 +00001563 /* argR == 0 */
1564 xer_ov
1565 = unop(Iop_1Uto32, binop(Iop_CmpEQ32, argR, mkU32(0)));
1566 break;
1567
cerion5b2325f2005-12-23 00:55:09 +00001568 case /* 4 */ PPCG_FLAG_OP_MULLW:
ceriond953ebb2005-11-29 13:27:20 +00001569 /* OV true if result can't be represented in 32 bits
1570 i.e sHi != sign extension of sLo */
1571 t64 = newTemp(Ity_I64);
1572 assign( t64, binop(Iop_MullS32, argL, argR) );
1573 xer_ov
1574 = binop( Iop_CmpNE32,
1575 unop(Iop_64HIto32, mkexpr(t64)),
1576 binop( Iop_Sar32,
1577 unop(Iop_64to32, mkexpr(t64)),
1578 mkU8(31))
1579 );
1580 xer_ov
1581 = unop(Iop_1Uto32, xer_ov);
1582 break;
1583
cerion5b2325f2005-12-23 00:55:09 +00001584 case /* 5 */ PPCG_FLAG_OP_NEG:
ceriond953ebb2005-11-29 13:27:20 +00001585 /* argL == INT32_MIN */
1586 xer_ov
1587 = unop( Iop_1Uto32,
1588 binop(Iop_CmpEQ32, argL, mkU32(INT32_MIN)) );
1589 break;
1590
cerion5b2325f2005-12-23 00:55:09 +00001591 case /* 6 */ PPCG_FLAG_OP_SUBF:
1592 case /* 7 */ PPCG_FLAG_OP_SUBFC:
1593 case /* 8 */ PPCG_FLAG_OP_SUBFE:
ceriond953ebb2005-11-29 13:27:20 +00001594 /* ((~argL)^argR^-1) & ((~argL)^res) & (1<<31) ?1:0; */
1595 xer_ov
1596 = AND3( XOR3(NOT(argL),argR,mkU32(-1)),
1597 XOR2(NOT(argL),res),
1598 mkU32(INT32_MIN) );
1599 /* xer_ov can only be 0 or 1<<31 */
1600 xer_ov
1601 = binop(Iop_Shr32, xer_ov, mkU8(31) );
1602 break;
1603
1604 default:
1605 vex_printf("set_XER_OV: op = %u\n", op);
cerion5b2325f2005-12-23 00:55:09 +00001606 vpanic("set_XER_OV(ppc)");
sewardj20ef5472005-07-21 14:48:31 +00001607 }
ceriond953ebb2005-11-29 13:27:20 +00001608
sewardj20ef5472005-07-21 14:48:31 +00001609 /* xer_ov MUST denote either 0 or 1, no other value allowed */
ceriond953ebb2005-11-29 13:27:20 +00001610 putXER_OV( unop(Iop_32to8, xer_ov) );
sewardj20ef5472005-07-21 14:48:31 +00001611
1612 /* Update the summary overflow */
cerionedf7fc52005-11-18 20:57:41 +00001613 putXER_SO( binop(Iop_Or8, getXER_SO(), getXER_OV()) );
sewardj20ef5472005-07-21 14:48:31 +00001614
1615# undef INT32_MIN
1616# undef AND3
1617# undef XOR3
1618# undef XOR2
1619# undef NOT
1620}
1621
ceriond953ebb2005-11-29 13:27:20 +00001622static void set_XER_OV_64( UInt op, IRExpr* res,
1623 IRExpr* argL, IRExpr* argR )
1624{
1625 IRExpr* xer_ov;
cerion5b2325f2005-12-23 00:55:09 +00001626 vassert(op < PPCG_FLAG_OP_NUMBER);
ceriond953ebb2005-11-29 13:27:20 +00001627 vassert(typeOfIRExpr(irbb->tyenv,res) == Ity_I64);
1628 vassert(typeOfIRExpr(irbb->tyenv,argL) == Ity_I64);
1629 vassert(typeOfIRExpr(irbb->tyenv,argR) == Ity_I64);
1630
cerion2831b002005-11-30 19:55:22 +00001631# define INT64_MIN 0x8000000000000000ULL
ceriond953ebb2005-11-29 13:27:20 +00001632
1633# define XOR2(_aa,_bb) \
1634 binop(Iop_Xor64,(_aa),(_bb))
1635
1636# define XOR3(_cc,_dd,_ee) \
1637 binop(Iop_Xor64,binop(Iop_Xor64,(_cc),(_dd)),(_ee))
1638
1639# define AND3(_ff,_gg,_hh) \
1640 binop(Iop_And64,binop(Iop_And64,(_ff),(_gg)),(_hh))
1641
1642#define NOT(_jj) \
1643 unop(Iop_Not64, (_jj))
1644
1645 switch (op) {
cerion5b2325f2005-12-23 00:55:09 +00001646 case /* 0 */ PPCG_FLAG_OP_ADD:
1647 case /* 1 */ PPCG_FLAG_OP_ADDE:
ceriond953ebb2005-11-29 13:27:20 +00001648 /* (argL^argR^-1) & (argL^res) & (1<<63) ? 1:0 */
1649 // i.e. ((both_same_sign) & (sign_changed) & (sign_mask))
1650 xer_ov
1651 = AND3( XOR3(argL,argR,mkU64(-1)),
1652 XOR2(argL,res),
1653 mkU64(INT64_MIN) );
1654 /* xer_ov can only be 0 or 1<<63 */
1655 xer_ov
1656 = unop(Iop_64to1, binop(Iop_Shr64, xer_ov, mkU8(63)));
1657 break;
1658
cerion5b2325f2005-12-23 00:55:09 +00001659 case /* 2 */ PPCG_FLAG_OP_DIVW:
ceriond953ebb2005-11-29 13:27:20 +00001660 /* (argL == INT64_MIN && argR == -1) || argR == 0 */
1661 xer_ov
1662 = mkOR1(
1663 mkAND1(
1664 binop(Iop_CmpEQ64, argL, mkU64(INT64_MIN)),
1665 binop(Iop_CmpEQ64, argR, mkU64(-1))
1666 ),
1667 binop(Iop_CmpEQ64, argR, mkU64(0) )
1668 );
1669 break;
1670
cerion5b2325f2005-12-23 00:55:09 +00001671 case /* 3 */ PPCG_FLAG_OP_DIVWU:
ceriond953ebb2005-11-29 13:27:20 +00001672 /* argR == 0 */
1673 xer_ov
1674 = binop(Iop_CmpEQ64, argR, mkU64(0));
1675 break;
1676
cerion5b2325f2005-12-23 00:55:09 +00001677 case /* 4 */ PPCG_FLAG_OP_MULLW: {
ceriond953ebb2005-11-29 13:27:20 +00001678 /* OV true if result can't be represented in 64 bits
1679 i.e sHi != sign extension of sLo */
ceriond953ebb2005-11-29 13:27:20 +00001680 xer_ov
cerionbb01b7c2005-12-16 13:40:18 +00001681 = binop( Iop_CmpNE32,
1682 unop(Iop_64HIto32, res),
1683 binop( Iop_Sar32,
1684 unop(Iop_64to32, res),
1685 mkU8(31))
1686 );
ceriond953ebb2005-11-29 13:27:20 +00001687 break;
1688 }
1689
cerion5b2325f2005-12-23 00:55:09 +00001690 case /* 5 */ PPCG_FLAG_OP_NEG:
ceriond953ebb2005-11-29 13:27:20 +00001691 /* argL == INT64_MIN */
1692 xer_ov
1693 = binop(Iop_CmpEQ64, argL, mkU64(INT64_MIN));
1694 break;
1695
cerion5b2325f2005-12-23 00:55:09 +00001696 case /* 6 */ PPCG_FLAG_OP_SUBF:
1697 case /* 7 */ PPCG_FLAG_OP_SUBFC:
1698 case /* 8 */ PPCG_FLAG_OP_SUBFE:
ceriond953ebb2005-11-29 13:27:20 +00001699 /* ((~argL)^argR^-1) & ((~argL)^res) & (1<<63) ?1:0; */
1700 xer_ov
1701 = AND3( XOR3(NOT(argL),argR,mkU64(-1)),
1702 XOR2(NOT(argL),res),
1703 mkU64(INT64_MIN) );
1704 /* xer_ov can only be 0 or 1<<63 */
1705 xer_ov
1706 = unop(Iop_64to1, binop(Iop_Shr64, xer_ov, mkU8(63)));
1707 break;
1708
1709 default:
1710 vex_printf("set_XER_OV: op = %u\n", op);
cerion5b2325f2005-12-23 00:55:09 +00001711 vpanic("set_XER_OV(ppc64)");
ceriond953ebb2005-11-29 13:27:20 +00001712 }
1713
1714 /* xer_ov MUST denote either 0 or 1, no other value allowed */
1715 putXER_OV( unop(Iop_1Uto8, xer_ov) );
1716
1717 /* Update the summary overflow */
1718 putXER_SO( binop(Iop_Or8, getXER_SO(), getXER_OV()) );
1719
1720# undef INT64_MIN
1721# undef AND3
1722# undef XOR3
1723# undef XOR2
1724# undef NOT
1725}
1726
1727static void set_XER_OV ( IRType ty, UInt op, IRExpr* res,
1728 IRExpr* argL, IRExpr* argR )
1729{
1730 if (ty == Ity_I32)
1731 set_XER_OV_32( op, res, argL, argR );
1732 else
1733 set_XER_OV_64( op, res, argL, argR );
1734}
1735
1736
1737
sewardjb51f0f42005-07-18 11:38:02 +00001738/* RES is the result of doing OP on ARGL and ARGR with the old %XER.CA
1739 value being OLDCA. Set %XER.CA accordingly. */
cerione9d361a2005-03-04 17:35:29 +00001740
cerion2831b002005-11-30 19:55:22 +00001741static void set_XER_CA_32 ( UInt op, IRExpr* res,
1742 IRExpr* argL, IRExpr* argR, IRExpr* oldca )
cerion38674602005-02-08 02:19:25 +00001743{
sewardj9a036bf2005-03-14 18:19:08 +00001744 IRExpr* xer_ca;
cerion5b2325f2005-12-23 00:55:09 +00001745 vassert(op < PPCG_FLAG_OP_NUMBER);
sewardjb51f0f42005-07-18 11:38:02 +00001746 vassert(typeOfIRExpr(irbb->tyenv,res) == Ity_I32);
1747 vassert(typeOfIRExpr(irbb->tyenv,argL) == Ity_I32);
1748 vassert(typeOfIRExpr(irbb->tyenv,argR) == Ity_I32);
1749 vassert(typeOfIRExpr(irbb->tyenv,oldca) == Ity_I32);
cerion70e24122005-03-16 00:27:37 +00001750
sewardj20ef5472005-07-21 14:48:31 +00001751 /* Incoming oldca is assumed to hold the values 0 or 1 only. This
1752 seems reasonable given that it's always generated by
cerionedf7fc52005-11-18 20:57:41 +00001753 getXER_CA32(), which masks it accordingly. In any case it being
cerion75949202005-12-24 13:14:11 +00001754 0 or 1 is an invariant of the ppc guest state representation;
sewardj20ef5472005-07-21 14:48:31 +00001755 if it has any other value, that invariant has been violated. */
cerione9d361a2005-03-04 17:35:29 +00001756
sewardj20ef5472005-07-21 14:48:31 +00001757 switch (op) {
cerion5b2325f2005-12-23 00:55:09 +00001758 case /* 0 */ PPCG_FLAG_OP_ADD:
ceriond953ebb2005-11-29 13:27:20 +00001759 /* res <u argL */
1760 xer_ca
1761 = unop(Iop_1Uto32, binop(Iop_CmpLT32U, res, argL));
1762 break;
1763
cerion5b2325f2005-12-23 00:55:09 +00001764 case /* 1 */ PPCG_FLAG_OP_ADDE:
ceriond953ebb2005-11-29 13:27:20 +00001765 /* res <u argL || (old_ca==1 && res==argL) */
1766 xer_ca
1767 = mkOR1(
1768 binop(Iop_CmpLT32U, res, argL),
1769 mkAND1(
1770 binop(Iop_CmpEQ32, oldca, mkU32(1)),
1771 binop(Iop_CmpEQ32, res, argL)
1772 )
1773 );
1774 xer_ca
1775 = unop(Iop_1Uto32, xer_ca);
1776 break;
1777
cerion5b2325f2005-12-23 00:55:09 +00001778 case /* 8 */ PPCG_FLAG_OP_SUBFE:
ceriond953ebb2005-11-29 13:27:20 +00001779 /* res <u argR || (old_ca==1 && res==argR) */
1780 xer_ca
1781 = mkOR1(
1782 binop(Iop_CmpLT32U, res, argR),
1783 mkAND1(
1784 binop(Iop_CmpEQ32, oldca, mkU32(1)),
1785 binop(Iop_CmpEQ32, res, argR)
1786 )
1787 );
1788 xer_ca
1789 = unop(Iop_1Uto32, xer_ca);
1790 break;
1791
cerion5b2325f2005-12-23 00:55:09 +00001792 case /* 7 */ PPCG_FLAG_OP_SUBFC:
1793 case /* 9 */ PPCG_FLAG_OP_SUBFI:
ceriond953ebb2005-11-29 13:27:20 +00001794 /* res <=u argR */
1795 xer_ca
1796 = unop(Iop_1Uto32, binop(Iop_CmpLE32U, res, argR));
1797 break;
1798
cerion5b2325f2005-12-23 00:55:09 +00001799 case /* 10 */ PPCG_FLAG_OP_SRAW:
ceriond953ebb2005-11-29 13:27:20 +00001800 /* The shift amount is guaranteed to be in 0 .. 63 inclusive.
1801 If it is <= 31, behave like SRAWI; else XER.CA is the sign
1802 bit of argL. */
1803 /* This term valid for shift amount < 32 only */
1804 xer_ca
1805 = binop(
1806 Iop_And32,
1807 binop(Iop_Sar32, argL, mkU8(31)),
1808 binop( Iop_And32,
1809 argL,
1810 binop( Iop_Sub32,
cerion5b2325f2005-12-23 00:55:09 +00001811 binop(Iop_Shl32, mkU32(1),
1812 unop(Iop_32to8,argR)),
ceriond953ebb2005-11-29 13:27:20 +00001813 mkU32(1) )
1814 )
sewardj20ef5472005-07-21 14:48:31 +00001815 );
ceriond953ebb2005-11-29 13:27:20 +00001816 xer_ca
1817 = IRExpr_Mux0X(
1818 /* shift amt > 31 ? */
1819 unop(Iop_1Uto8, binop(Iop_CmpLT32U, mkU32(31), argR)),
1820 /* no -- be like srawi */
1821 unop(Iop_1Uto32, binop(Iop_CmpNE32, xer_ca, mkU32(0))),
1822 /* yes -- get sign bit of argL */
1823 binop(Iop_Shr32, argL, mkU8(31))
1824 );
1825 break;
sewardj20ef5472005-07-21 14:48:31 +00001826
cerion5b2325f2005-12-23 00:55:09 +00001827 case /* 11 */ PPCG_FLAG_OP_SRAWI:
ceriond953ebb2005-11-29 13:27:20 +00001828 /* xer_ca is 1 iff src was negative and bits_shifted_out !=
1829 0. Since the shift amount is known to be in the range
1830 0 .. 31 inclusive the following seems viable:
1831 xer.ca == 1 iff the following is nonzero:
1832 (argL >>s 31) -- either all 0s or all 1s
1833 & (argL & (1<<argR)-1) -- the stuff shifted out */
1834 xer_ca
1835 = binop(
1836 Iop_And32,
1837 binop(Iop_Sar32, argL, mkU8(31)),
1838 binop( Iop_And32,
1839 argL,
1840 binop( Iop_Sub32,
cerion5b2325f2005-12-23 00:55:09 +00001841 binop(Iop_Shl32, mkU32(1),
1842 unop(Iop_32to8,argR)),
ceriond953ebb2005-11-29 13:27:20 +00001843 mkU32(1) )
1844 )
sewardj20ef5472005-07-21 14:48:31 +00001845 );
ceriond953ebb2005-11-29 13:27:20 +00001846 xer_ca
1847 = unop(Iop_1Uto32, binop(Iop_CmpNE32, xer_ca, mkU32(0)));
1848 break;
1849
1850 default:
1851 vex_printf("set_XER_CA: op = %u\n", op);
cerion5b2325f2005-12-23 00:55:09 +00001852 vpanic("set_XER_CA(ppc)");
sewardj20ef5472005-07-21 14:48:31 +00001853 }
1854
1855 /* xer_ca MUST denote either 0 or 1, no other value allowed */
cerionedf7fc52005-11-18 20:57:41 +00001856 putXER_CA( unop(Iop_32to8, xer_ca) );
sewardjb51f0f42005-07-18 11:38:02 +00001857}
1858
cerion2831b002005-11-30 19:55:22 +00001859static void set_XER_CA_64 ( UInt op, IRExpr* res,
1860 IRExpr* argL, IRExpr* argR, IRExpr* oldca )
sewardje14bb9f2005-07-22 09:39:02 +00001861{
ceriond953ebb2005-11-29 13:27:20 +00001862 IRExpr* xer_ca;
cerion5b2325f2005-12-23 00:55:09 +00001863 vassert(op < PPCG_FLAG_OP_NUMBER);
ceriond953ebb2005-11-29 13:27:20 +00001864 vassert(typeOfIRExpr(irbb->tyenv,res) == Ity_I64);
1865 vassert(typeOfIRExpr(irbb->tyenv,argL) == Ity_I64);
1866 vassert(typeOfIRExpr(irbb->tyenv,argR) == Ity_I64);
1867 vassert(typeOfIRExpr(irbb->tyenv,oldca) == Ity_I64);
sewardje14bb9f2005-07-22 09:39:02 +00001868
ceriond953ebb2005-11-29 13:27:20 +00001869 /* Incoming oldca is assumed to hold the values 0 or 1 only. This
1870 seems reasonable given that it's always generated by
1871 getXER_CA32(), which masks it accordingly. In any case it being
cerion75949202005-12-24 13:14:11 +00001872 0 or 1 is an invariant of the ppc guest state representation;
ceriond953ebb2005-11-29 13:27:20 +00001873 if it has any other value, that invariant has been violated. */
1874
1875 switch (op) {
cerion5b2325f2005-12-23 00:55:09 +00001876 case /* 0 */ PPCG_FLAG_OP_ADD:
ceriond953ebb2005-11-29 13:27:20 +00001877 /* res <u argL */
1878 xer_ca
cerionf0de28c2005-12-13 20:21:11 +00001879 = unop(Iop_1Uto32, binop(Iop_CmpLT64U, res, argL));
ceriond953ebb2005-11-29 13:27:20 +00001880 break;
sewardje14bb9f2005-07-22 09:39:02 +00001881
cerion5b2325f2005-12-23 00:55:09 +00001882 case /* 1 */ PPCG_FLAG_OP_ADDE:
ceriond953ebb2005-11-29 13:27:20 +00001883 /* res <u argL || (old_ca==1 && res==argL) */
1884 xer_ca
1885 = mkOR1(
1886 binop(Iop_CmpLT64U, res, argL),
1887 mkAND1(
cerionf0de28c2005-12-13 20:21:11 +00001888 binop(Iop_CmpEQ64, oldca, mkU64(1)),
ceriond953ebb2005-11-29 13:27:20 +00001889 binop(Iop_CmpEQ64, res, argL)
1890 )
1891 );
cerionf0de28c2005-12-13 20:21:11 +00001892 xer_ca
1893 = unop(Iop_1Uto32, xer_ca);
cerionedf7fc52005-11-18 20:57:41 +00001894 break;
ceriond953ebb2005-11-29 13:27:20 +00001895
cerion5b2325f2005-12-23 00:55:09 +00001896 case /* 8 */ PPCG_FLAG_OP_SUBFE:
ceriond953ebb2005-11-29 13:27:20 +00001897 /* res <u argR || (old_ca==1 && res==argR) */
1898 xer_ca
1899 = mkOR1(
1900 binop(Iop_CmpLT64U, res, argR),
1901 mkAND1(
1902 binop(Iop_CmpEQ64, oldca, mkU64(1)),
1903 binop(Iop_CmpEQ64, res, argR)
1904 )
1905 );
cerionf0de28c2005-12-13 20:21:11 +00001906 xer_ca
1907 = unop(Iop_1Uto32, xer_ca);
ceriond953ebb2005-11-29 13:27:20 +00001908 break;
1909
cerion5b2325f2005-12-23 00:55:09 +00001910 case /* 7 */ PPCG_FLAG_OP_SUBFC:
1911 case /* 9 */ PPCG_FLAG_OP_SUBFI:
ceriond953ebb2005-11-29 13:27:20 +00001912 /* res <=u argR */
1913 xer_ca
cerionf0de28c2005-12-13 20:21:11 +00001914 = unop(Iop_1Uto32, binop(Iop_CmpLE64U, res, argR));
ceriond953ebb2005-11-29 13:27:20 +00001915 break;
1916
1917
cerion5b2325f2005-12-23 00:55:09 +00001918 case /* 10 */ PPCG_FLAG_OP_SRAW:
cerionf0de28c2005-12-13 20:21:11 +00001919 /* The shift amount is guaranteed to be in 0 .. 31 inclusive.
ceriond953ebb2005-11-29 13:27:20 +00001920 If it is <= 31, behave like SRAWI; else XER.CA is the sign
1921 bit of argL. */
cerionf0de28c2005-12-13 20:21:11 +00001922 /* This term valid for shift amount < 31 only */
1923
ceriond953ebb2005-11-29 13:27:20 +00001924 xer_ca
1925 = binop(
cerionf0de28c2005-12-13 20:21:11 +00001926 Iop_And64,
1927 binop(Iop_Sar64, argL, mkU8(31)),
1928 binop( Iop_And64,
ceriond953ebb2005-11-29 13:27:20 +00001929 argL,
cerionf0de28c2005-12-13 20:21:11 +00001930 binop( Iop_Sub64,
cerion5b2325f2005-12-23 00:55:09 +00001931 binop(Iop_Shl64, mkU64(1),
1932 unop(Iop_64to8,argR)),
cerionf0de28c2005-12-13 20:21:11 +00001933 mkU64(1) )
ceriond953ebb2005-11-29 13:27:20 +00001934 )
1935 );
1936 xer_ca
1937 = IRExpr_Mux0X(
1938 /* shift amt > 31 ? */
cerionf0de28c2005-12-13 20:21:11 +00001939 unop(Iop_1Uto8, binop(Iop_CmpLT64U, mkU64(31), argR)),
ceriond953ebb2005-11-29 13:27:20 +00001940 /* no -- be like srawi */
cerionf0de28c2005-12-13 20:21:11 +00001941 unop(Iop_1Uto32, binop(Iop_CmpNE64, xer_ca, mkU64(0))),
ceriond953ebb2005-11-29 13:27:20 +00001942 /* yes -- get sign bit of argL */
cerionf0de28c2005-12-13 20:21:11 +00001943 unop(Iop_64to32, binop(Iop_Shr64, argL, mkU8(63)))
ceriond953ebb2005-11-29 13:27:20 +00001944 );
1945 break;
1946
cerion5b2325f2005-12-23 00:55:09 +00001947 case /* 11 */ PPCG_FLAG_OP_SRAWI:
cerionf0de28c2005-12-13 20:21:11 +00001948 /* xer_ca is 1 iff src was negative and bits_shifted_out != 0.
1949 Since the shift amount is known to be in the range 0 .. 31
1950 inclusive the following seems viable:
ceriond953ebb2005-11-29 13:27:20 +00001951 xer.ca == 1 iff the following is nonzero:
1952 (argL >>s 31) -- either all 0s or all 1s
1953 & (argL & (1<<argR)-1) -- the stuff shifted out */
cerionf0de28c2005-12-13 20:21:11 +00001954
ceriond953ebb2005-11-29 13:27:20 +00001955 xer_ca
1956 = binop(
cerionf0de28c2005-12-13 20:21:11 +00001957 Iop_And64,
1958 binop(Iop_Sar64, argL, mkU8(31)),
1959 binop( Iop_And64,
ceriond953ebb2005-11-29 13:27:20 +00001960 argL,
cerionf0de28c2005-12-13 20:21:11 +00001961 binop( Iop_Sub64,
cerion5b2325f2005-12-23 00:55:09 +00001962 binop(Iop_Shl64, mkU64(1),
1963 unop(Iop_64to8,argR)),
cerionf0de28c2005-12-13 20:21:11 +00001964 mkU64(1) )
ceriond953ebb2005-11-29 13:27:20 +00001965 )
1966 );
1967 xer_ca
cerionf0de28c2005-12-13 20:21:11 +00001968 = unop(Iop_1Uto32, binop(Iop_CmpNE64, xer_ca, mkU64(0)));
ceriond953ebb2005-11-29 13:27:20 +00001969 break;
ceriond953ebb2005-11-29 13:27:20 +00001970
cerionf0de28c2005-12-13 20:21:11 +00001971
cerion5b2325f2005-12-23 00:55:09 +00001972 case /* 12 */ PPCG_FLAG_OP_SRAD:
cerionf0de28c2005-12-13 20:21:11 +00001973 /* The shift amount is guaranteed to be in 0 .. 63 inclusive.
1974 If it is <= 63, behave like SRADI; else XER.CA is the sign
1975 bit of argL. */
1976 /* This term valid for shift amount < 63 only */
1977
1978 xer_ca
1979 = binop(
1980 Iop_And64,
1981 binop(Iop_Sar64, argL, mkU8(63)),
1982 binop( Iop_And64,
1983 argL,
1984 binop( Iop_Sub64,
cerion5b2325f2005-12-23 00:55:09 +00001985 binop(Iop_Shl64, mkU64(1),
1986 unop(Iop_64to8,argR)),
cerionf0de28c2005-12-13 20:21:11 +00001987 mkU64(1) )
1988 )
1989 );
1990 xer_ca
1991 = IRExpr_Mux0X(
1992 /* shift amt > 63 ? */
1993 unop(Iop_1Uto8, binop(Iop_CmpLT64U, mkU64(63), argR)),
cerion07b07a92005-12-22 14:32:35 +00001994 /* no -- be like sradi */
1995 unop(Iop_1Uto32, binop(Iop_CmpNE64, xer_ca, mkU64(0))),
cerionf0de28c2005-12-13 20:21:11 +00001996 /* yes -- get sign bit of argL */
1997 unop(Iop_64to32, binop(Iop_Shr64, argL, mkU8(63)))
1998 );
1999 break;
2000
2001
cerion5b2325f2005-12-23 00:55:09 +00002002 case /* 13 */ PPCG_FLAG_OP_SRADI:
cerionf0de28c2005-12-13 20:21:11 +00002003 /* xer_ca is 1 iff src was negative and bits_shifted_out != 0.
2004 Since the shift amount is known to be in the range 0 .. 63
2005 inclusive, the following seems viable:
2006 xer.ca == 1 iff the following is nonzero:
2007 (argL >>s 63) -- either all 0s or all 1s
2008 & (argL & (1<<argR)-1) -- the stuff shifted out */
2009
2010 xer_ca
2011 = binop(
2012 Iop_And64,
2013 binop(Iop_Sar64, argL, mkU8(63)),
2014 binop( Iop_And64,
2015 argL,
2016 binop( Iop_Sub64,
cerion5b2325f2005-12-23 00:55:09 +00002017 binop(Iop_Shl64, mkU64(1),
2018 unop(Iop_64to8,argR)),
cerionf0de28c2005-12-13 20:21:11 +00002019 mkU64(1) )
2020 )
2021 );
2022 xer_ca
2023 = unop(Iop_1Uto32, binop(Iop_CmpNE64, xer_ca, mkU64(0)));
2024 break;
2025
ceriond953ebb2005-11-29 13:27:20 +00002026 default:
2027 vex_printf("set_XER_CA: op = %u\n", op);
cerionf0de28c2005-12-13 20:21:11 +00002028 vpanic("set_XER_CA(ppc64)");
sewardje14bb9f2005-07-22 09:39:02 +00002029 }
2030
ceriond953ebb2005-11-29 13:27:20 +00002031 /* xer_ca MUST denote either 0 or 1, no other value allowed */
cerionf0de28c2005-12-13 20:21:11 +00002032 putXER_CA( unop(Iop_32to8, xer_ca) );
sewardje14bb9f2005-07-22 09:39:02 +00002033}
2034
cerion2831b002005-11-30 19:55:22 +00002035static void set_XER_CA ( IRType ty, UInt op, IRExpr* res,
2036 IRExpr* argL, IRExpr* argR, IRExpr* oldca )
cerionedf7fc52005-11-18 20:57:41 +00002037{
cerion2831b002005-11-30 19:55:22 +00002038 if (ty == Ity_I32)
ceriond953ebb2005-11-29 13:27:20 +00002039 set_XER_CA_32( op, res, argL, argR, oldca );
2040 else
2041 set_XER_CA_64( op, res, argL, argR, oldca );
cerionedf7fc52005-11-18 20:57:41 +00002042}
2043
ceriond953ebb2005-11-29 13:27:20 +00002044
2045
2046/*------------------------------------------------------------*/
2047/*--- Read/write to guest-state --- */
2048/*------------------------------------------------------------*/
2049
cerionf0de28c2005-12-13 20:21:11 +00002050static IRExpr* /* :: Ity_I32/64 */ getGST ( PPC_GST reg )
cerionedf7fc52005-11-18 20:57:41 +00002051{
ceriond953ebb2005-11-29 13:27:20 +00002052 IRType ty = mode64 ? Ity_I64 : Ity_I32;
cerionedf7fc52005-11-18 20:57:41 +00002053 switch (reg) {
ceriond953ebb2005-11-29 13:27:20 +00002054 case PPC_GST_LR:
cerion5b2325f2005-12-23 00:55:09 +00002055 return IRExpr_Get( OFFB_LR, ty );
ceriond953ebb2005-11-29 13:27:20 +00002056
2057 case PPC_GST_CTR:
cerion5b2325f2005-12-23 00:55:09 +00002058 return IRExpr_Get( OFFB_CTR, ty );
ceriond953ebb2005-11-29 13:27:20 +00002059
2060 case PPC_GST_VRSAVE:
cerion5b2325f2005-12-23 00:55:09 +00002061 return IRExpr_Get( OFFB_VRSAVE, Ity_I32 );
ceriond953ebb2005-11-29 13:27:20 +00002062
2063 case PPC_GST_VSCR:
cerion5b2325f2005-12-23 00:55:09 +00002064 return binop(Iop_And32, IRExpr_Get( OFFB_VSCR,Ity_I32 ),
2065 mkU32(MASK_VSCR_VALID));
ceriond953ebb2005-11-29 13:27:20 +00002066
2067 case PPC_GST_CR: {
cerionedf7fc52005-11-18 20:57:41 +00002068 /* Synthesise the entire CR into a single word. Expensive. */
2069# define FIELD(_n) \
2070 binop(Iop_Shl32, \
2071 unop(Iop_8Uto32, \
2072 binop(Iop_Or8, \
2073 binop(Iop_And8, getCR321(_n), mkU8(7<<1)), \
2074 binop(Iop_And8, getCR0(_n), mkU8(1)) \
2075 ) \
2076 ), \
2077 mkU8(4 * (7-(_n))) \
2078 )
2079 return binop(Iop_Or32,
2080 binop(Iop_Or32,
2081 binop(Iop_Or32, FIELD(0), FIELD(1)),
2082 binop(Iop_Or32, FIELD(2), FIELD(3))
2083 ),
2084 binop(Iop_Or32,
2085 binop(Iop_Or32, FIELD(4), FIELD(5)),
2086 binop(Iop_Or32, FIELD(6), FIELD(7))
2087 )
2088 );
2089# undef FIELD
2090 }
ceriond953ebb2005-11-29 13:27:20 +00002091
2092 case PPC_GST_XER:
cerionedf7fc52005-11-18 20:57:41 +00002093 return binop(Iop_Or32,
2094 binop(Iop_Or32,
2095 binop( Iop_Shl32, getXER_SO32(), mkU8(31)),
2096 binop( Iop_Shl32, getXER_OV32(), mkU8(30))),
2097 binop(Iop_Or32,
2098 binop( Iop_Shl32, getXER_CA32(), mkU8(29)),
2099 getXER_BC32()));
ceriond953ebb2005-11-29 13:27:20 +00002100
2101 case PPC_GST_RESVN:
cerion5b2325f2005-12-23 00:55:09 +00002102 return IRExpr_Get( OFFB_RESVN, ty);
ceriond953ebb2005-11-29 13:27:20 +00002103
cerionedf7fc52005-11-18 20:57:41 +00002104 default:
cerion5b2325f2005-12-23 00:55:09 +00002105 vex_printf("getGST(ppc): reg = %u", reg);
2106 vpanic("getGST(ppc)");
ceriond953ebb2005-11-29 13:27:20 +00002107 }
2108}
2109
2110/* Get a masked word from the given reg */
2111static IRExpr* /* ::Ity_I32 */ getGST_masked ( PPC_GST reg, UInt mask )
2112{
2113 IRTemp val = newTemp(Ity_I32);
2114 vassert( reg < PPC_GST_MAX );
2115
2116 switch (reg) {
2117
2118 case PPC_GST_FPSCR: {
cerion5b2325f2005-12-23 00:55:09 +00002119 /* Vex-generated code expects the FPSCR to be set as follows:
ceriond953ebb2005-11-29 13:27:20 +00002120 all exceptions masked, round-to-nearest.
2121 This corresponds to a FPSCR value of 0x0. */
2122
2123 /* We're only keeping track of the rounding mode,
2124 so if the mask isn't asking for this, just return 0x0 */
2125 if (mask & 0x3) {
cerion5b2325f2005-12-23 00:55:09 +00002126 assign( val, IRExpr_Get( OFFB_FPROUND, Ity_I32 ) );
ceriond953ebb2005-11-29 13:27:20 +00002127 } else {
2128 assign( val, mkU32(0x0) );
2129 }
2130 break;
2131 }
2132
2133 default:
cerion5b2325f2005-12-23 00:55:09 +00002134 vex_printf("getGST_masked(ppc): reg = %u", reg);
2135 vpanic("getGST_masked(ppc)");
ceriond953ebb2005-11-29 13:27:20 +00002136 }
2137
2138 if (mask != 0xFFFFFFFF) {
2139 return binop(Iop_And32, mkexpr(val), mkU32(mask));
2140 } else {
2141 return mkexpr(val);
2142 }
2143}
2144
2145/* Fetch the specified REG[FLD] nibble (as per IBM/hardware notation)
2146 and return it at the bottom of an I32; the top 27 bits are
2147 guaranteed to be zero. */
2148static IRExpr* /* ::Ity_I32 */ getGST_field ( PPC_GST reg, UInt fld )
2149{
2150 UInt shft, mask;
2151
2152 vassert( fld < 8 );
2153 vassert( reg < PPC_GST_MAX );
2154
2155 shft = 4*(7-fld);
2156 mask = 0xF<<shft;
2157
2158 switch (reg) {
2159 case PPC_GST_XER:
2160 vassert(fld ==7);
2161 return binop(Iop_Or32,
2162 binop(Iop_Or32,
2163 binop(Iop_Shl32, getXER_SO32(), mkU8(3)),
2164 binop(Iop_Shl32, getXER_OV32(), mkU8(2))),
2165 binop( Iop_Shl32, getXER_CA32(), mkU8(1)));
2166 break;
2167
2168 default:
2169 if (shft == 0)
2170 return getGST_masked( reg, mask );
2171 else
2172 return binop(Iop_Shr32,
2173 getGST_masked( reg, mask ),
2174 mkU8(toUChar( shft )));
2175 }
2176}
2177
2178static void putGST ( PPC_GST reg, IRExpr* src )
2179{
cerion5b2325f2005-12-23 00:55:09 +00002180 IRType ty = mode64 ? Ity_I64 : Ity_I32;
2181 IRType ty_src = typeOfIRExpr(irbb->tyenv,src );
ceriond953ebb2005-11-29 13:27:20 +00002182 vassert( reg < PPC_GST_MAX );
2183 switch (reg) {
2184 case PPC_GST_CIA:
cerion5b2325f2005-12-23 00:55:09 +00002185 vassert( ty_src == ty );
2186 stmt( IRStmt_Put( OFFB_CIA, src ) );
ceriond953ebb2005-11-29 13:27:20 +00002187 break;
2188 case PPC_GST_LR:
cerion5b2325f2005-12-23 00:55:09 +00002189 vassert( ty_src == ty );
2190 stmt( IRStmt_Put( OFFB_LR, src ) );
ceriond953ebb2005-11-29 13:27:20 +00002191 break;
2192 case PPC_GST_CTR:
cerion5b2325f2005-12-23 00:55:09 +00002193 vassert( ty_src == ty );
2194 stmt( IRStmt_Put( OFFB_CTR, src ) );
ceriond953ebb2005-11-29 13:27:20 +00002195 break;
2196 case PPC_GST_VRSAVE:
cerion5b2325f2005-12-23 00:55:09 +00002197 vassert( ty_src == Ity_I32 );
2198 stmt( IRStmt_Put( OFFB_VRSAVE,src));
ceriond953ebb2005-11-29 13:27:20 +00002199 break;
2200 case PPC_GST_VSCR:
cerion5b2325f2005-12-23 00:55:09 +00002201 vassert( ty_src == Ity_I32 );
2202 stmt( IRStmt_Put( OFFB_VSCR,
ceriond953ebb2005-11-29 13:27:20 +00002203 binop(Iop_And32, src,
2204 mkU32(MASK_VSCR_VALID)) ) );
2205 break;
2206 case PPC_GST_XER:
cerion5b2325f2005-12-23 00:55:09 +00002207 vassert( ty_src == Ity_I32 );
ceriond953ebb2005-11-29 13:27:20 +00002208 putXER_SO( unop(Iop_32to8, binop(Iop_Shr32, src, mkU8(31))) );
2209 putXER_OV( unop(Iop_32to8, binop(Iop_Shr32, src, mkU8(30))) );
2210 putXER_CA( unop(Iop_32to8, binop(Iop_Shr32, src, mkU8(29))) );
2211 putXER_BC( unop(Iop_32to8, src) );
2212 break;
2213
2214 case PPC_GST_EMWARN:
cerion5b2325f2005-12-23 00:55:09 +00002215 vassert( ty_src == Ity_I32 );
2216 stmt( IRStmt_Put( OFFB_EMWARN,src) );
ceriond953ebb2005-11-29 13:27:20 +00002217 break;
2218
2219 case PPC_GST_TISTART:
cerion5b2325f2005-12-23 00:55:09 +00002220 vassert( ty_src == ty );
2221 stmt( IRStmt_Put( OFFB_TISTART, src) );
ceriond953ebb2005-11-29 13:27:20 +00002222 break;
2223
2224 case PPC_GST_TILEN:
cerion5b2325f2005-12-23 00:55:09 +00002225 vassert( ty_src == ty );
2226 stmt( IRStmt_Put( OFFB_TILEN, src) );
ceriond953ebb2005-11-29 13:27:20 +00002227 break;
2228
2229 case PPC_GST_RESVN:
cerion5b2325f2005-12-23 00:55:09 +00002230 vassert( ty_src == ty );
2231 stmt( IRStmt_Put( OFFB_RESVN, src) );
ceriond953ebb2005-11-29 13:27:20 +00002232 break;
2233
2234 default:
cerion5b2325f2005-12-23 00:55:09 +00002235 vex_printf("putGST(ppc): reg = %u", reg);
2236 vpanic("putGST(ppc)");
cerionedf7fc52005-11-18 20:57:41 +00002237 }
2238}
sewardje14bb9f2005-07-22 09:39:02 +00002239
2240/* Write masked src to the given reg */
ceriond953ebb2005-11-29 13:27:20 +00002241static void putGST_masked ( PPC_GST reg, IRExpr* src, UInt mask )
sewardje14bb9f2005-07-22 09:39:02 +00002242{
ceriond953ebb2005-11-29 13:27:20 +00002243 IRType ty = mode64 ? Ity_I64 : Ity_I32;
2244 vassert( reg < PPC_GST_MAX );
sewardje14bb9f2005-07-22 09:39:02 +00002245 vassert( typeOfIRExpr(irbb->tyenv,src ) == Ity_I32 );
2246
2247 switch (reg) {
ceriond953ebb2005-11-29 13:27:20 +00002248 case PPC_GST_FPSCR: {
sewardje14bb9f2005-07-22 09:39:02 +00002249 /* Allow writes to Rounding Mode */
2250 if (mask & 0x3) {
sewardjb183b852006-02-03 16:08:03 +00002251 /* construct new fpround from new and old values as per mask:
2252 new fpround = (src & (3 & mask)) | (fpround & (3 & ~mask)) */
2253 stmt(
2254 IRStmt_Put(
2255 OFFB_FPROUND,
2256 binop(
2257 Iop_Or32,
2258 binop(Iop_And32, src, mkU32(3 & mask)),
2259 binop(
2260 Iop_And32,
2261 IRExpr_Get(OFFB_FPROUND,Ity_I32),
2262 mkU32(3 & ~mask)
2263 )
2264 )
2265 )
2266 );
sewardje14bb9f2005-07-22 09:39:02 +00002267 }
2268
cerionedf7fc52005-11-18 20:57:41 +00002269 /* Give EmWarn for attempted writes to:
sewardje14bb9f2005-07-22 09:39:02 +00002270 - Exception Controls
2271 - Non-IEEE Mode
2272 */
2273 if (mask & 0xFC) { // Exception Control, Non-IEE mode
sewardj5ff11dd2006-01-20 14:19:25 +00002274 VexEmWarn ew = EmWarn_PPCexns;
sewardje14bb9f2005-07-22 09:39:02 +00002275
2276 /* If any of the src::exception_control bits are actually set,
2277 side-exit to the next insn, reporting the warning,
2278 so that Valgrind's dispatcher sees the warning. */
ceriond953ebb2005-11-29 13:27:20 +00002279 putGST( PPC_GST_EMWARN, mkU32(ew) );
sewardje14bb9f2005-07-22 09:39:02 +00002280 stmt(
2281 IRStmt_Exit(
2282 binop(Iop_CmpNE32, mkU32(ew), mkU32(EmWarn_NONE)),
2283 Ijk_EmWarn,
cerion2831b002005-11-30 19:55:22 +00002284 mkSzConst( ty, nextInsnAddr()) ));
sewardje14bb9f2005-07-22 09:39:02 +00002285 }
2286
cerionedf7fc52005-11-18 20:57:41 +00002287 /* Ignore all other writes */
sewardje14bb9f2005-07-22 09:39:02 +00002288 break;
cerionedf7fc52005-11-18 20:57:41 +00002289 }
sewardje14bb9f2005-07-22 09:39:02 +00002290
2291 default:
cerion5b2325f2005-12-23 00:55:09 +00002292 vex_printf("putGST_masked(ppc): reg = %u", reg);
2293 vpanic("putGST_masked(ppc)");
sewardje14bb9f2005-07-22 09:39:02 +00002294 }
2295}
2296
cerionedf7fc52005-11-18 20:57:41 +00002297/* Write the least significant nibble of src to the specified
2298 REG[FLD] (as per IBM/hardware notation). */
ceriond953ebb2005-11-29 13:27:20 +00002299static void putGST_field ( PPC_GST reg, IRExpr* src, UInt fld )
cerionedf7fc52005-11-18 20:57:41 +00002300{
sewardj41a7b702005-11-18 22:18:23 +00002301 UInt shft, mask;
2302
cerionedf7fc52005-11-18 20:57:41 +00002303 vassert( typeOfIRExpr(irbb->tyenv,src ) == Ity_I32 );
2304 vassert( fld < 8 );
ceriond953ebb2005-11-29 13:27:20 +00002305 vassert( reg < PPC_GST_MAX );
cerionedf7fc52005-11-18 20:57:41 +00002306
sewardj41a7b702005-11-18 22:18:23 +00002307 shft = 4*(7-fld);
2308 mask = 0xF<<shft;
cerionedf7fc52005-11-18 20:57:41 +00002309
2310 switch (reg) {
ceriond953ebb2005-11-29 13:27:20 +00002311 case PPC_GST_CR:
cerionedf7fc52005-11-18 20:57:41 +00002312 putCR0 (fld, binop(Iop_And8, mkU8(1 ), unop(Iop_32to8, src)));
2313 putCR321(fld, binop(Iop_And8, mkU8(7<<1), unop(Iop_32to8, src)));
2314 break;
2315
2316 default:
2317 if (shft == 0) {
ceriond953ebb2005-11-29 13:27:20 +00002318 putGST_masked( reg, src, mask );
cerionedf7fc52005-11-18 20:57:41 +00002319 } else {
ceriond953ebb2005-11-29 13:27:20 +00002320 putGST_masked( reg,
cerionedf7fc52005-11-18 20:57:41 +00002321 binop(Iop_Shl32, src, mkU8(toUChar(shft))),
2322 mask );
2323 }
2324 }
2325}
cerion62bec572005-02-01 21:29:39 +00002326
cerion76222262005-02-05 13:45:57 +00002327
2328
cerione9d361a2005-03-04 17:35:29 +00002329/*------------------------------------------------------------*/
cerion3d870a32005-03-18 12:23:33 +00002330/*--- Integer Instruction Translation --- */
cerione9d361a2005-03-04 17:35:29 +00002331/*------------------------------------------------------------*/
cerion896a1372005-01-25 12:24:25 +00002332
cerion91ad5362005-01-27 23:02:41 +00002333/*
2334 Integer Arithmetic Instructions
2335*/
cerion645c9302005-01-31 10:09:59 +00002336static Bool dis_int_arith ( UInt theInstr )
cerion91ad5362005-01-27 23:02:41 +00002337{
cerion76de5cf2005-11-18 18:25:12 +00002338 /* D-Form, XO-Form */
2339 UChar opc1 = ifieldOPC(theInstr);
2340 UChar rD_addr = ifieldRegDS(theInstr);
2341 UChar rA_addr = ifieldRegA(theInstr);
ceriond953ebb2005-11-29 13:27:20 +00002342 UInt uimm16 = ifieldUIMM16(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00002343 UChar rB_addr = ifieldRegB(theInstr);
2344 UChar flag_OE = ifieldBIT10(theInstr);
2345 UInt opc2 = ifieldOPClo9(theInstr);
2346 UChar flag_rC = ifieldBIT0(theInstr);
2347
ceriond953ebb2005-11-29 13:27:20 +00002348 Long simm16 = extend_s_16to64(uimm16);
2349 IRType ty = mode64 ? Ity_I64 : Ity_I32;
2350 IRTemp rA = newTemp(ty);
2351 IRTemp rB = newTemp(ty);
2352 IRTemp rD = newTemp(ty);
cerion70e24122005-03-16 00:27:37 +00002353
cerionb85e8bb2005-02-16 08:54:33 +00002354 Bool do_rc = False;
cerion91ad5362005-01-27 23:02:41 +00002355
cerion76de5cf2005-11-18 18:25:12 +00002356 assign( rA, getIReg(rA_addr) );
2357 assign( rB, getIReg(rB_addr) ); // XO-Form: rD, rA, rB
sewardjb51f0f42005-07-18 11:38:02 +00002358
cerionb85e8bb2005-02-16 08:54:33 +00002359 switch (opc1) {
cerionb85e8bb2005-02-16 08:54:33 +00002360 /* D-Form */
cerione9d361a2005-03-04 17:35:29 +00002361 case 0x0C: // addic (Add Immediate Carrying, PPC32 p351
ceriond953ebb2005-11-29 13:27:20 +00002362 DIP("addic r%u,r%u,%d\n", rD_addr, rA_addr, (Int)simm16);
cerion2831b002005-11-30 19:55:22 +00002363 assign( rD, binop( mkSzOp(ty, Iop_Add8), mkexpr(rA),
2364 mkSzExtendS16(ty, uimm16) ) );
cerion5b2325f2005-12-23 00:55:09 +00002365 set_XER_CA( ty, PPCG_FLAG_OP_ADD,
cerion2831b002005-11-30 19:55:22 +00002366 mkexpr(rD), mkexpr(rA), mkSzExtendS16(ty, uimm16),
2367 mkSzImm(ty, 0)/*old xer.ca, which is ignored*/ );
cerion4561acb2005-02-21 14:07:48 +00002368 break;
sewardjb51f0f42005-07-18 11:38:02 +00002369
cerione9d361a2005-03-04 17:35:29 +00002370 case 0x0D: // addic. (Add Immediate Carrying and Record, PPC32 p352)
ceriond953ebb2005-11-29 13:27:20 +00002371 DIP("addic. r%u,r%u,%d\n", rD_addr, rA_addr, (Int)simm16);
cerion2831b002005-11-30 19:55:22 +00002372 assign( rD, binop( mkSzOp(ty, Iop_Add8), mkexpr(rA),
2373 mkSzExtendS16(ty, uimm16) ) );
cerion5b2325f2005-12-23 00:55:09 +00002374 set_XER_CA( ty, PPCG_FLAG_OP_ADD,
cerion2831b002005-11-30 19:55:22 +00002375 mkexpr(rD), mkexpr(rA), mkSzExtendS16(ty, uimm16),
2376 mkSzImm(ty, 0)/*old xer.ca, which is ignored*/ );
cerion70e24122005-03-16 00:27:37 +00002377 do_rc = True; // Always record to CR
cerion76de5cf2005-11-18 18:25:12 +00002378 flag_rC = 1;
cerion4561acb2005-02-21 14:07:48 +00002379 break;
2380
cerione9d361a2005-03-04 17:35:29 +00002381 case 0x0E: // addi (Add Immediate, PPC32 p350)
cerionb85e8bb2005-02-16 08:54:33 +00002382 // li rD,val == addi rD,0,val
2383 // la disp(rA) == addi rD,rA,disp
cerion76de5cf2005-11-18 18:25:12 +00002384 if ( rA_addr == 0 ) {
ceriond953ebb2005-11-29 13:27:20 +00002385 DIP("li r%u,%d\n", rD_addr, (Int)simm16);
cerion2831b002005-11-30 19:55:22 +00002386 assign( rD, mkSzExtendS16(ty, uimm16) );
cerionb85e8bb2005-02-16 08:54:33 +00002387 } else {
ceriond953ebb2005-11-29 13:27:20 +00002388 DIP("addi r%u,r%u,%d\n", rD_addr, rA_addr, (Int)simm16);
cerion2831b002005-11-30 19:55:22 +00002389 assign( rD, binop( mkSzOp(ty, Iop_Add8), mkexpr(rA),
2390 mkSzExtendS16(ty, uimm16) ) );
cerionb85e8bb2005-02-16 08:54:33 +00002391 }
2392 break;
cerion91ad5362005-01-27 23:02:41 +00002393
cerione9d361a2005-03-04 17:35:29 +00002394 case 0x0F: // addis (Add Immediate Shifted, PPC32 p353)
cerionb85e8bb2005-02-16 08:54:33 +00002395 // lis rD,val == addis rD,0,val
cerion76de5cf2005-11-18 18:25:12 +00002396 if ( rA_addr == 0 ) {
ceriond953ebb2005-11-29 13:27:20 +00002397 DIP("lis r%u,%d\n", rD_addr, (Int)simm16);
cerion2831b002005-11-30 19:55:22 +00002398 assign( rD, mkSzExtendS32(ty, uimm16 << 16) );
cerionb85e8bb2005-02-16 08:54:33 +00002399 } else {
ceriond953ebb2005-11-29 13:27:20 +00002400 DIP("addis r%u,r%u,0x%x\n", rD_addr, rA_addr, (Int)simm16);
cerion2831b002005-11-30 19:55:22 +00002401 assign( rD, binop( mkSzOp(ty, Iop_Add8), mkexpr(rA),
2402 mkSzExtendS32(ty, uimm16 << 16) ) );
cerionb85e8bb2005-02-16 08:54:33 +00002403 }
2404 break;
cerion91ad5362005-01-27 23:02:41 +00002405
cerione9d361a2005-03-04 17:35:29 +00002406 case 0x07: // mulli (Multiply Low Immediate, PPC32 p490)
ceriond953ebb2005-11-29 13:27:20 +00002407 DIP("mulli r%u,r%u,%d\n", rD_addr, rA_addr, (Int)simm16);
2408 if (mode64)
2409 assign( rD, unop(Iop_128to64,
2410 binop(Iop_MullS64, mkexpr(rA),
cerion2831b002005-11-30 19:55:22 +00002411 mkSzExtendS16(ty, uimm16))) );
ceriond953ebb2005-11-29 13:27:20 +00002412 else
2413 assign( rD, unop(Iop_64to32,
2414 binop(Iop_MullS32, mkexpr(rA),
cerion2831b002005-11-30 19:55:22 +00002415 mkSzExtendS16(ty, uimm16))) );
cerionb85e8bb2005-02-16 08:54:33 +00002416 break;
cerion38674602005-02-08 02:19:25 +00002417
cerione9d361a2005-03-04 17:35:29 +00002418 case 0x08: // subfic (Subtract from Immediate Carrying, PPC32 p540)
ceriond953ebb2005-11-29 13:27:20 +00002419 DIP("subfic r%u,r%u,%d\n", rD_addr, rA_addr, (Int)simm16);
cerion76de5cf2005-11-18 18:25:12 +00002420 // rD = simm16 - rA
cerion2831b002005-11-30 19:55:22 +00002421 assign( rD, binop( mkSzOp(ty, Iop_Sub8),
2422 mkSzExtendS16(ty, uimm16),
ceriond953ebb2005-11-29 13:27:20 +00002423 mkexpr(rA)) );
cerion5b2325f2005-12-23 00:55:09 +00002424 set_XER_CA( ty, PPCG_FLAG_OP_SUBFI,
cerion2831b002005-11-30 19:55:22 +00002425 mkexpr(rD), mkexpr(rA), mkSzExtendS16(ty, uimm16),
2426 mkSzImm(ty, 0)/*old xer.ca, which is ignored*/ );
cerionb85e8bb2005-02-16 08:54:33 +00002427 break;
cerion38674602005-02-08 02:19:25 +00002428
cerionb85e8bb2005-02-16 08:54:33 +00002429 /* XO-Form */
2430 case 0x1F:
cerionb85e8bb2005-02-16 08:54:33 +00002431 do_rc = True; // All below record to CR
2432
2433 switch (opc2) {
cerione9d361a2005-03-04 17:35:29 +00002434 case 0x10A: // add (Add, PPC32 p347)
ceriond953ebb2005-11-29 13:27:20 +00002435 DIP("add%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002436 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002437 rD_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00002438 assign( rD, binop( mkSzOp(ty, Iop_Add8),
ceriond953ebb2005-11-29 13:27:20 +00002439 mkexpr(rA), mkexpr(rB) ) );
cerion70e24122005-03-16 00:27:37 +00002440 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002441 set_XER_OV( ty, PPCG_FLAG_OP_ADD,
cerion76de5cf2005-11-18 18:25:12 +00002442 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00002443 }
cerionb85e8bb2005-02-16 08:54:33 +00002444 break;
cerion91ad5362005-01-27 23:02:41 +00002445
cerione9d361a2005-03-04 17:35:29 +00002446 case 0x00A: // addc (Add Carrying, PPC32 p348)
ceriond953ebb2005-11-29 13:27:20 +00002447 DIP("addc%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002448 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002449 rD_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00002450 assign( rD, binop( mkSzOp(ty, Iop_Add8),
ceriond953ebb2005-11-29 13:27:20 +00002451 mkexpr(rA), mkexpr(rB)) );
cerion5b2325f2005-12-23 00:55:09 +00002452 set_XER_CA( ty, PPCG_FLAG_OP_ADD,
cerion76de5cf2005-11-18 18:25:12 +00002453 mkexpr(rD), mkexpr(rA), mkexpr(rB),
cerion2831b002005-11-30 19:55:22 +00002454 mkSzImm(ty, 0)/*old xer.ca, which is ignored*/ );
cerion70e24122005-03-16 00:27:37 +00002455 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002456 set_XER_OV( ty, PPCG_FLAG_OP_ADD,
cerion76de5cf2005-11-18 18:25:12 +00002457 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00002458 }
cerionb85e8bb2005-02-16 08:54:33 +00002459 break;
2460
sewardjb51f0f42005-07-18 11:38:02 +00002461 case 0x08A: { // adde (Add Extended, PPC32 p349)
cerion2831b002005-11-30 19:55:22 +00002462 IRTemp old_xer_ca = newTemp(ty);
ceriond953ebb2005-11-29 13:27:20 +00002463 DIP("adde%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002464 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002465 rD_addr, rA_addr, rB_addr);
cerionb85e8bb2005-02-16 08:54:33 +00002466 // rD = rA + rB + XER[CA]
cerion2831b002005-11-30 19:55:22 +00002467 assign( old_xer_ca, mkSzWiden32(ty, getXER_CA32(), False) );
2468 assign( rD, binop( mkSzOp(ty, Iop_Add8), mkexpr(rA),
2469 binop( mkSzOp(ty, Iop_Add8),
2470 mkexpr(rB), mkexpr(old_xer_ca))) );
cerion5b2325f2005-12-23 00:55:09 +00002471 set_XER_CA( ty, PPCG_FLAG_OP_ADDE,
cerion76de5cf2005-11-18 18:25:12 +00002472 mkexpr(rD), mkexpr(rA), mkexpr(rB),
cerion2831b002005-11-30 19:55:22 +00002473 mkexpr(old_xer_ca) );
cerion70e24122005-03-16 00:27:37 +00002474 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002475 set_XER_OV( ty, PPCG_FLAG_OP_ADDE,
cerion76de5cf2005-11-18 18:25:12 +00002476 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00002477 }
cerionb85e8bb2005-02-16 08:54:33 +00002478 break;
sewardjb51f0f42005-07-18 11:38:02 +00002479 }
2480
cerion5b2325f2005-12-23 00:55:09 +00002481 case 0x0EA: { // addme (Add to Minus One Extended, PPC32 p354)
cerion2831b002005-11-30 19:55:22 +00002482 IRTemp old_xer_ca = newTemp(ty);
2483 IRExpr *min_one;
cerion76de5cf2005-11-18 18:25:12 +00002484 if (rB_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002485 vex_printf("dis_int_arith(ppc)(addme,rB_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002486 return False;
2487 }
ceriond953ebb2005-11-29 13:27:20 +00002488 DIP("addme%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002489 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002490 rD_addr, rA_addr, rB_addr);
cerion70e24122005-03-16 00:27:37 +00002491 // rD = rA + (-1) + XER[CA]
2492 // => Just another form of adde
cerion2831b002005-11-30 19:55:22 +00002493 assign( old_xer_ca, mkSzWiden32(ty, getXER_CA32(), False) );
2494 min_one = mkSzImm(ty, (Long)-1);
2495 assign( rD, binop( mkSzOp(ty, Iop_Add8), mkexpr(rA),
2496 binop( mkSzOp(ty, Iop_Add8),
2497 min_one, mkexpr(old_xer_ca)) ));
cerion5b2325f2005-12-23 00:55:09 +00002498 set_XER_CA( ty, PPCG_FLAG_OP_ADDE,
ceriond953ebb2005-11-29 13:27:20 +00002499 mkexpr(rD), mkexpr(rA), min_one,
cerion2831b002005-11-30 19:55:22 +00002500 mkexpr(old_xer_ca) );
cerion70e24122005-03-16 00:27:37 +00002501 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002502 set_XER_OV( ty, PPCG_FLAG_OP_ADDE,
ceriond953ebb2005-11-29 13:27:20 +00002503 mkexpr(rD), mkexpr(rA), min_one );
cerion70e24122005-03-16 00:27:37 +00002504 }
cerionb85e8bb2005-02-16 08:54:33 +00002505 break;
sewardjb51f0f42005-07-18 11:38:02 +00002506 }
2507
2508 case 0x0CA: { // addze (Add to Zero Extended, PPC32 p355)
cerion2831b002005-11-30 19:55:22 +00002509 IRTemp old_xer_ca = newTemp(ty);
cerion76de5cf2005-11-18 18:25:12 +00002510 if (rB_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002511 vex_printf("dis_int_arith(ppc)(addze,rB_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002512 return False;
2513 }
ceriond953ebb2005-11-29 13:27:20 +00002514 DIP("addze%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002515 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002516 rD_addr, rA_addr, rB_addr);
cerion70e24122005-03-16 00:27:37 +00002517 // rD = rA + (0) + XER[CA]
2518 // => Just another form of adde
cerion2831b002005-11-30 19:55:22 +00002519 assign( old_xer_ca, mkSzWiden32(ty, getXER_CA32(), False) );
2520 assign( rD, binop( mkSzOp(ty, Iop_Add8),
2521 mkexpr(rA), mkexpr(old_xer_ca)) );
cerion5b2325f2005-12-23 00:55:09 +00002522 set_XER_CA( ty, PPCG_FLAG_OP_ADDE,
cerion2831b002005-11-30 19:55:22 +00002523 mkexpr(rD), mkexpr(rA), mkSzImm(ty, 0),
2524 mkexpr(old_xer_ca) );
cerion70e24122005-03-16 00:27:37 +00002525 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002526 set_XER_OV( ty, PPCG_FLAG_OP_ADDE,
cerion2831b002005-11-30 19:55:22 +00002527 mkexpr(rD), mkexpr(rA), mkSzImm(ty, 0) );
cerion70e24122005-03-16 00:27:37 +00002528 }
cerionb85e8bb2005-02-16 08:54:33 +00002529 break;
sewardjb51f0f42005-07-18 11:38:02 +00002530 }
cerion91ad5362005-01-27 23:02:41 +00002531
cerione9d361a2005-03-04 17:35:29 +00002532 case 0x1EB: // divw (Divide Word, PPC32 p388)
ceriond953ebb2005-11-29 13:27:20 +00002533 DIP("divw%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002534 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002535 rD_addr, rA_addr, rB_addr);
ceriond953ebb2005-11-29 13:27:20 +00002536 if (mode64) {
cerion2831b002005-11-30 19:55:22 +00002537 /* Note:
2538 XER settings are mode independent, and reflect the
2539 overflow of the low-order 32bit result
2540 CR0[LT|GT|EQ] are undefined if flag_rC && mode64
2541 */
cerionbb01b7c2005-12-16 13:40:18 +00002542 /* rD[hi32] are undefined: setting them to sign of lo32
2543 - makes set_CR0 happy */
2544 IRExpr* dividend = mk64lo32Sto64( mkexpr(rA) );
2545 IRExpr* divisor = mk64lo32Sto64( mkexpr(rB) );
cerion5b2325f2005-12-23 00:55:09 +00002546 assign( rD, mk64lo32Uto64( binop(Iop_DivS64, dividend,
2547 divisor) ) );
ceriond953ebb2005-11-29 13:27:20 +00002548 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002549 set_XER_OV( ty, PPCG_FLAG_OP_DIVW,
cerionf0de28c2005-12-13 20:21:11 +00002550 mkexpr(rD), dividend, divisor );
ceriond953ebb2005-11-29 13:27:20 +00002551 }
2552 } else {
2553 assign( rD, binop(Iop_DivS32, mkexpr(rA), mkexpr(rB)) );
2554 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002555 set_XER_OV( ty, PPCG_FLAG_OP_DIVW,
ceriond953ebb2005-11-29 13:27:20 +00002556 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
2557 }
cerion70e24122005-03-16 00:27:37 +00002558 }
cerionb85e8bb2005-02-16 08:54:33 +00002559 /* Note:
2560 if (0x8000_0000 / -1) or (x / 0)
cerion76de5cf2005-11-18 18:25:12 +00002561 => rD=undef, if(flag_rC) CR7=undef, if(flag_OE) XER_OV=1
cerionb85e8bb2005-02-16 08:54:33 +00002562 => But _no_ exception raised. */
2563 break;
cerion91ad5362005-01-27 23:02:41 +00002564
cerione9d361a2005-03-04 17:35:29 +00002565 case 0x1CB: // divwu (Divide Word Unsigned, PPC32 p389)
ceriond953ebb2005-11-29 13:27:20 +00002566 DIP("divwu%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002567 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002568 rD_addr, rA_addr, rB_addr);
ceriond953ebb2005-11-29 13:27:20 +00002569 if (mode64) {
cerion2831b002005-11-30 19:55:22 +00002570 /* Note:
2571 XER settings are mode independent, and reflect the
2572 overflow of the low-order 32bit result
2573 CR0[LT|GT|EQ] are undefined if flag_rC && mode64
2574 */
cerionbb01b7c2005-12-16 13:40:18 +00002575 IRExpr* dividend = mk64lo32Uto64( mkexpr(rA) );
2576 IRExpr* divisor = mk64lo32Uto64( mkexpr(rB) );
cerion5b2325f2005-12-23 00:55:09 +00002577 assign( rD, mk64lo32Uto64( binop(Iop_DivU64, dividend,
2578 divisor) ) );
ceriond953ebb2005-11-29 13:27:20 +00002579 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002580 set_XER_OV( ty, PPCG_FLAG_OP_DIVWU,
cerionf0de28c2005-12-13 20:21:11 +00002581 mkexpr(rD), dividend, divisor );
ceriond953ebb2005-11-29 13:27:20 +00002582 }
cerion2831b002005-11-30 19:55:22 +00002583 } else {
ceriond953ebb2005-11-29 13:27:20 +00002584 assign( rD, binop(Iop_DivU32, mkexpr(rA), mkexpr(rB)) );
2585 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002586 set_XER_OV( ty, PPCG_FLAG_OP_DIVWU,
ceriond953ebb2005-11-29 13:27:20 +00002587 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
2588 }
cerion70e24122005-03-16 00:27:37 +00002589 }
cerionb85e8bb2005-02-16 08:54:33 +00002590 /* Note: ditto comment divw, for (x / 0) */
2591 break;
cerion91ad5362005-01-27 23:02:41 +00002592
cerione9d361a2005-03-04 17:35:29 +00002593 case 0x04B: // mulhw (Multiply High Word, PPC32 p488)
cerionb85e8bb2005-02-16 08:54:33 +00002594 if (flag_OE != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002595 vex_printf("dis_int_arith(ppc)(mulhw,flag_OE)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002596 return False;
2597 }
cerion5b2325f2005-12-23 00:55:09 +00002598 DIP("mulhw%s r%u,r%u,r%u\n", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002599 rD_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00002600 if (mode64) {
cerionbb01b7c2005-12-16 13:40:18 +00002601 /* rD[hi32] are undefined: setting them to sign of lo32
2602 - makes set_CR0 happy */
2603 assign( rD, binop(Iop_Sar64,
2604 binop(Iop_Mul64,
2605 mk64lo32Sto64( mkexpr(rA) ),
2606 mk64lo32Sto64( mkexpr(rB) )),
2607 mkU8(32)) );
cerion2831b002005-11-30 19:55:22 +00002608 } else {
ceriond953ebb2005-11-29 13:27:20 +00002609 assign( rD, unop(Iop_64HIto32,
2610 binop(Iop_MullS32,
2611 mkexpr(rA), mkexpr(rB))) );
cerion2831b002005-11-30 19:55:22 +00002612 }
cerionb85e8bb2005-02-16 08:54:33 +00002613 break;
cerionc19d5e12005-02-01 15:56:25 +00002614
cerion5b2325f2005-12-23 00:55:09 +00002615 case 0x00B: // mulhwu (Multiply High Word Unsigned, PPC32 p489)
cerionb85e8bb2005-02-16 08:54:33 +00002616 if (flag_OE != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002617 vex_printf("dis_int_arith(ppc)(mulhwu,flag_OE)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002618 return False;
2619 }
cerion5b2325f2005-12-23 00:55:09 +00002620 DIP("mulhwu%s r%u,r%u,r%u\n", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002621 rD_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00002622 if (mode64) {
cerionbb01b7c2005-12-16 13:40:18 +00002623 /* rD[hi32] are undefined: setting them to sign of lo32
2624 - makes set_CR0 happy */
2625 assign( rD, binop(Iop_Sar64,
2626 binop(Iop_Mul64,
2627 mk64lo32Uto64( mkexpr(rA) ),
2628 mk64lo32Uto64( mkexpr(rB) ) ),
2629 mkU8(32)) );
cerion2831b002005-11-30 19:55:22 +00002630 } else {
ceriond953ebb2005-11-29 13:27:20 +00002631 assign( rD, unop(Iop_64HIto32,
2632 binop(Iop_MullU32,
2633 mkexpr(rA), mkexpr(rB))) );
cerion2831b002005-11-30 19:55:22 +00002634 }
cerionb85e8bb2005-02-16 08:54:33 +00002635 break;
2636
cerione9d361a2005-03-04 17:35:29 +00002637 case 0x0EB: // mullw (Multiply Low Word, PPC32 p491)
ceriond953ebb2005-11-29 13:27:20 +00002638 DIP("mullw%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002639 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002640 rD_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00002641 if (mode64) {
cerionbb01b7c2005-12-16 13:40:18 +00002642 /* rD[hi32] are undefined: setting them to sign of lo32
2643 - set_XER_OV() and set_CR0() depend on this */
2644 IRExpr *a = unop(Iop_64to32, mkexpr(rA) );
2645 IRExpr *b = unop(Iop_64to32, mkexpr(rB) );
2646 assign( rD, binop(Iop_MullS32, a, b) );
2647 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002648 set_XER_OV( ty, PPCG_FLAG_OP_MULLW,
cerionbb01b7c2005-12-16 13:40:18 +00002649 mkexpr(rD),
2650 unop(Iop_32Uto64, a), unop(Iop_32Uto64, b) );
2651 }
cerion2831b002005-11-30 19:55:22 +00002652 } else {
ceriond953ebb2005-11-29 13:27:20 +00002653 assign( rD, unop(Iop_64to32,
2654 binop(Iop_MullU32,
2655 mkexpr(rA), mkexpr(rB))) );
cerionbb01b7c2005-12-16 13:40:18 +00002656 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002657 set_XER_OV( ty, PPCG_FLAG_OP_MULLW,
cerionbb01b7c2005-12-16 13:40:18 +00002658 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
2659 }
cerion70e24122005-03-16 00:27:37 +00002660 }
cerionb85e8bb2005-02-16 08:54:33 +00002661 break;
cerionc19d5e12005-02-01 15:56:25 +00002662
cerione9d361a2005-03-04 17:35:29 +00002663 case 0x068: // neg (Negate, PPC32 p493)
cerion76de5cf2005-11-18 18:25:12 +00002664 if (rB_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002665 vex_printf("dis_int_arith(ppc)(neg,rB_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002666 return False;
2667 }
ceriond953ebb2005-11-29 13:27:20 +00002668 DIP("neg%s%s r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002669 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002670 rD_addr, rA_addr);
ceriond953ebb2005-11-29 13:27:20 +00002671 // rD = (~rA) + 1
cerion2831b002005-11-30 19:55:22 +00002672 assign( rD, binop( mkSzOp(ty, Iop_Add8),
2673 unop( mkSzOp(ty, Iop_Not8), mkexpr(rA) ),
2674 mkSzImm(ty, 1)) );
cerion70e24122005-03-16 00:27:37 +00002675 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002676 set_XER_OV( ty, PPCG_FLAG_OP_NEG,
cerion76de5cf2005-11-18 18:25:12 +00002677 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00002678 }
cerionb85e8bb2005-02-16 08:54:33 +00002679 break;
cerion91ad5362005-01-27 23:02:41 +00002680
cerione9d361a2005-03-04 17:35:29 +00002681 case 0x028: // subf (Subtract From, PPC32 p537)
ceriond953ebb2005-11-29 13:27:20 +00002682 DIP("subf%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002683 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002684 rD_addr, rA_addr, rB_addr);
cerion01908472005-02-25 16:43:08 +00002685 // rD = rB - rA
cerion2831b002005-11-30 19:55:22 +00002686 assign( rD, binop( mkSzOp(ty, Iop_Sub8),
ceriond953ebb2005-11-29 13:27:20 +00002687 mkexpr(rB), mkexpr(rA)) );
cerion70e24122005-03-16 00:27:37 +00002688 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002689 set_XER_OV( ty, PPCG_FLAG_OP_SUBF,
cerion76de5cf2005-11-18 18:25:12 +00002690 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00002691 }
cerionb85e8bb2005-02-16 08:54:33 +00002692 break;
cerion38674602005-02-08 02:19:25 +00002693
cerione9d361a2005-03-04 17:35:29 +00002694 case 0x008: // subfc (Subtract from Carrying, PPC32 p538)
ceriond953ebb2005-11-29 13:27:20 +00002695 DIP("subfc%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002696 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002697 rD_addr, rA_addr, rB_addr);
cerion01908472005-02-25 16:43:08 +00002698 // rD = rB - rA
cerion2831b002005-11-30 19:55:22 +00002699 assign( rD, binop( mkSzOp(ty, Iop_Sub8),
ceriond953ebb2005-11-29 13:27:20 +00002700 mkexpr(rB), mkexpr(rA)) );
cerion5b2325f2005-12-23 00:55:09 +00002701 set_XER_CA( ty, PPCG_FLAG_OP_SUBFC,
cerion76de5cf2005-11-18 18:25:12 +00002702 mkexpr(rD), mkexpr(rA), mkexpr(rB),
cerion2831b002005-11-30 19:55:22 +00002703 mkSzImm(ty, 0)/*old xer.ca, which is ignored*/ );
cerion70e24122005-03-16 00:27:37 +00002704 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002705 set_XER_OV( ty, PPCG_FLAG_OP_SUBFC,
cerion76de5cf2005-11-18 18:25:12 +00002706 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00002707 }
cerionb85e8bb2005-02-16 08:54:33 +00002708 break;
2709
sewardjb51f0f42005-07-18 11:38:02 +00002710 case 0x088: {// subfe (Subtract from Extended, PPC32 p539)
cerion2831b002005-11-30 19:55:22 +00002711 IRTemp old_xer_ca = newTemp(ty);
ceriond953ebb2005-11-29 13:27:20 +00002712 DIP("subfe%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002713 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002714 rD_addr, rA_addr, rB_addr);
cerionb85e8bb2005-02-16 08:54:33 +00002715 // rD = (log not)rA + rB + XER[CA]
cerion2831b002005-11-30 19:55:22 +00002716 assign( old_xer_ca, mkSzWiden32(ty, getXER_CA32(), False) );
2717 assign( rD, binop( mkSzOp(ty, Iop_Add8),
2718 unop( mkSzOp(ty, Iop_Not8), mkexpr(rA)),
2719 binop( mkSzOp(ty, Iop_Add8),
2720 mkexpr(rB), mkexpr(old_xer_ca))) );
cerion5b2325f2005-12-23 00:55:09 +00002721 set_XER_CA( ty, PPCG_FLAG_OP_SUBFE,
cerion76de5cf2005-11-18 18:25:12 +00002722 mkexpr(rD), mkexpr(rA), mkexpr(rB),
cerion2831b002005-11-30 19:55:22 +00002723 mkexpr(old_xer_ca) );
cerion70e24122005-03-16 00:27:37 +00002724 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002725 set_XER_OV( ty, PPCG_FLAG_OP_SUBFE,
cerion76de5cf2005-11-18 18:25:12 +00002726 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00002727 }
cerionb85e8bb2005-02-16 08:54:33 +00002728 break;
sewardjb51f0f42005-07-18 11:38:02 +00002729 }
2730
cerion5b2325f2005-12-23 00:55:09 +00002731 case 0x0E8: { // subfme (Subtract from -1 Extended, PPC32 p541)
cerion2831b002005-11-30 19:55:22 +00002732 IRTemp old_xer_ca = newTemp(ty);
2733 IRExpr *min_one;
cerion76de5cf2005-11-18 18:25:12 +00002734 if (rB_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002735 vex_printf("dis_int_arith(ppc)(subfme,rB_addr)\n");
sewardj20ef5472005-07-21 14:48:31 +00002736 return False;
2737 }
ceriond953ebb2005-11-29 13:27:20 +00002738 DIP("subfme%s%s r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002739 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002740 rD_addr, rA_addr);
sewardj20ef5472005-07-21 14:48:31 +00002741 // rD = (log not)rA + (-1) + XER[CA]
2742 // => Just another form of subfe
cerion2831b002005-11-30 19:55:22 +00002743 assign( old_xer_ca, mkSzWiden32(ty, getXER_CA32(), False) );
2744 min_one = mkSzImm(ty, (Long)-1);
2745 assign( rD, binop( mkSzOp(ty, Iop_Add8),
2746 unop( mkSzOp(ty, Iop_Not8), mkexpr(rA)),
2747 binop( mkSzOp(ty, Iop_Add8),
2748 min_one, mkexpr(old_xer_ca))) );
cerion5b2325f2005-12-23 00:55:09 +00002749 set_XER_CA( ty, PPCG_FLAG_OP_SUBFE,
ceriond953ebb2005-11-29 13:27:20 +00002750 mkexpr(rD), mkexpr(rA), min_one,
cerion2831b002005-11-30 19:55:22 +00002751 mkexpr(old_xer_ca) );
sewardj20ef5472005-07-21 14:48:31 +00002752 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002753 set_XER_OV( ty, PPCG_FLAG_OP_SUBFE,
ceriond953ebb2005-11-29 13:27:20 +00002754 mkexpr(rD), mkexpr(rA), min_one );
sewardj20ef5472005-07-21 14:48:31 +00002755 }
2756 break;
2757 }
2758
cerion5b2325f2005-12-23 00:55:09 +00002759 case 0x0C8: { // subfze (Subtract from Zero Extended, PPC32 p542)
cerion2831b002005-11-30 19:55:22 +00002760 IRTemp old_xer_ca = newTemp(ty);
cerion76de5cf2005-11-18 18:25:12 +00002761 if (rB_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002762 vex_printf("dis_int_arith(ppc)(subfze,rB_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002763 return False;
2764 }
ceriond953ebb2005-11-29 13:27:20 +00002765 DIP("subfze%s%s r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002766 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002767 rD_addr, rA_addr);
cerion70e24122005-03-16 00:27:37 +00002768 // rD = (log not)rA + (0) + XER[CA]
2769 // => Just another form of subfe
cerion2831b002005-11-30 19:55:22 +00002770 assign( old_xer_ca, mkSzWiden32(ty, getXER_CA32(), False) );
2771 assign( rD, binop( mkSzOp(ty, Iop_Add8),
2772 unop( mkSzOp(ty, Iop_Not8),
2773 mkexpr(rA)), mkexpr(old_xer_ca)) );
cerion5b2325f2005-12-23 00:55:09 +00002774 set_XER_CA( ty, PPCG_FLAG_OP_SUBFE,
cerion2831b002005-11-30 19:55:22 +00002775 mkexpr(rD), mkexpr(rA), mkSzImm(ty, 0),
2776 mkexpr(old_xer_ca) );
cerion70e24122005-03-16 00:27:37 +00002777 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002778 set_XER_OV( ty, PPCG_FLAG_OP_SUBFE,
cerion2831b002005-11-30 19:55:22 +00002779 mkexpr(rD), mkexpr(rA), mkSzImm(ty, 0) );
cerion70e24122005-03-16 00:27:37 +00002780 }
cerionb85e8bb2005-02-16 08:54:33 +00002781 break;
sewardjb51f0f42005-07-18 11:38:02 +00002782 }
cerionae694622005-01-28 17:52:47 +00002783
cerionf0de28c2005-12-13 20:21:11 +00002784
2785 /* 64bit Arithmetic */
cerion5b2325f2005-12-23 00:55:09 +00002786 case 0x49: // mulhd (Multiply High DWord, PPC64 p539)
cerionf0de28c2005-12-13 20:21:11 +00002787 if (flag_OE != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002788 vex_printf("dis_int_arith(ppc)(mulhd,flagOE)\n");
cerionf0de28c2005-12-13 20:21:11 +00002789 return False;
2790 }
cerion5b2325f2005-12-23 00:55:09 +00002791 DIP("mulhd%s r%u,r%u,r%u\n", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00002792 rD_addr, rA_addr, rB_addr);
cerionf0de28c2005-12-13 20:21:11 +00002793 assign( rD, unop(Iop_128HIto64,
cerion07b07a92005-12-22 14:32:35 +00002794 binop(Iop_MullS64,
cerionf0de28c2005-12-13 20:21:11 +00002795 mkexpr(rA), mkexpr(rB))) );
cerion07b07a92005-12-22 14:32:35 +00002796
2797 break;
cerionf0de28c2005-12-13 20:21:11 +00002798
cerion5b2325f2005-12-23 00:55:09 +00002799 case 0x9: // mulhdu (Multiply High DWord Unsigned, PPC64 p540)
cerionf0de28c2005-12-13 20:21:11 +00002800 if (flag_OE != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002801 vex_printf("dis_int_arith(ppc)(mulhdu,flagOE)\n");
cerionf0de28c2005-12-13 20:21:11 +00002802 return False;
2803 }
cerion5b2325f2005-12-23 00:55:09 +00002804 DIP("mulhdu%s r%u,r%u,r%u\n", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00002805 rD_addr, rA_addr, rB_addr);
cerion07b07a92005-12-22 14:32:35 +00002806 assign( rD, unop(Iop_128HIto64,
2807 binop(Iop_MullU64,
2808 mkexpr(rA), mkexpr(rB))) );
2809 break;
cerionf0de28c2005-12-13 20:21:11 +00002810
cerion5b2325f2005-12-23 00:55:09 +00002811 case 0xE9: // mulld (Multiply Low DWord, PPC64 p543)
cerionf0de28c2005-12-13 20:21:11 +00002812 DIP("mulld%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002813 flag_OE ? "o" : "", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00002814 rD_addr, rA_addr, rB_addr);
2815 assign( rD, binop(Iop_Mul64, mkexpr(rA), mkexpr(rB)) );
2816 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002817 set_XER_OV( ty, PPCG_FLAG_OP_MULLW,
cerionf0de28c2005-12-13 20:21:11 +00002818 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
2819 }
2820 break;
2821
cerion5b2325f2005-12-23 00:55:09 +00002822 case 0x1E9: // divd (Divide DWord, PPC64 p419)
cerionf0de28c2005-12-13 20:21:11 +00002823 DIP("divd%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002824 flag_OE ? "o" : "", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00002825 rD_addr, rA_addr, rB_addr);
cerion07b07a92005-12-22 14:32:35 +00002826 assign( rD, binop(Iop_DivS64, mkexpr(rA), mkexpr(rB)) );
2827 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002828 set_XER_OV( ty, PPCG_FLAG_OP_DIVW,
cerion07b07a92005-12-22 14:32:35 +00002829 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
2830 }
2831 break;
cerionf0de28c2005-12-13 20:21:11 +00002832 /* Note:
cerion07b07a92005-12-22 14:32:35 +00002833 if (0x8000_0000_0000_0000 / -1) or (x / 0)
cerionf0de28c2005-12-13 20:21:11 +00002834 => rD=undef, if(flag_rC) CR7=undef, if(flag_OE) XER_OV=1
2835 => But _no_ exception raised. */
2836
cerion5b2325f2005-12-23 00:55:09 +00002837 case 0x1C9: // divdu (Divide DWord Unsigned, PPC64 p420)
cerionf0de28c2005-12-13 20:21:11 +00002838 DIP("divdu%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002839 flag_OE ? "o" : "", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00002840 rD_addr, rA_addr, rB_addr);
cerion07b07a92005-12-22 14:32:35 +00002841 assign( rD, binop(Iop_DivU64, mkexpr(rA), mkexpr(rB)) );
2842 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002843 set_XER_OV( ty, PPCG_FLAG_OP_DIVWU,
cerion07b07a92005-12-22 14:32:35 +00002844 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
2845 }
2846 break;
cerionf0de28c2005-12-13 20:21:11 +00002847 /* Note: ditto comment divd, for (x / 0) */
2848
cerionb85e8bb2005-02-16 08:54:33 +00002849 default:
cerion5b2325f2005-12-23 00:55:09 +00002850 vex_printf("dis_int_arith(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002851 return False;
2852 }
2853 break;
cerionf0de28c2005-12-13 20:21:11 +00002854
cerionb85e8bb2005-02-16 08:54:33 +00002855 default:
cerion5b2325f2005-12-23 00:55:09 +00002856 vex_printf("dis_int_arith(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002857 return False;
2858 }
cerion91ad5362005-01-27 23:02:41 +00002859
cerion76de5cf2005-11-18 18:25:12 +00002860 putIReg( rD_addr, mkexpr(rD) );
2861
2862 if (do_rc && flag_rC) {
2863 set_CR0( mkexpr(rD) );
cerionb85e8bb2005-02-16 08:54:33 +00002864 }
2865 return True;
cerion91ad5362005-01-27 23:02:41 +00002866}
2867
2868
2869
cerion3d870a32005-03-18 12:23:33 +00002870/*
2871 Integer Compare Instructions
2872*/
cerion7aa4bbc2005-01-29 09:32:07 +00002873static Bool dis_int_cmp ( UInt theInstr )
2874{
cerion76de5cf2005-11-18 18:25:12 +00002875 /* D-Form, X-Form */
2876 UChar opc1 = ifieldOPC(theInstr);
2877 UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) );
2878 UChar b22 = toUChar( IFIELD( theInstr, 22, 1 ) );
2879 UChar flag_L = toUChar( IFIELD( theInstr, 21, 1 ) );
2880 UChar rA_addr = ifieldRegA(theInstr);
ceriond953ebb2005-11-29 13:27:20 +00002881 UInt uimm16 = ifieldUIMM16(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00002882 UChar rB_addr = ifieldRegB(theInstr);
2883 UInt opc2 = ifieldOPClo10(theInstr);
2884 UChar b0 = ifieldBIT0(theInstr);
cerion7aa4bbc2005-01-29 09:32:07 +00002885
cerionbb01b7c2005-12-16 13:40:18 +00002886 IRType ty = mode64 ? Ity_I64 : Ity_I32;
2887 IRExpr *a = getIReg(rA_addr);
2888 IRExpr *b;
cerion76de5cf2005-11-18 18:25:12 +00002889
ceriond953ebb2005-11-29 13:27:20 +00002890 if (!mode64 && flag_L==1) { // L==1 invalid for 32 bit.
cerion5b2325f2005-12-23 00:55:09 +00002891 vex_printf("dis_int_cmp(ppc)(flag_L)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002892 return False;
2893 }
2894
cerion76de5cf2005-11-18 18:25:12 +00002895 if (b22 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002896 vex_printf("dis_int_cmp(ppc)(b22)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002897 return False;
2898 }
2899
2900 switch (opc1) {
ceriond953ebb2005-11-29 13:27:20 +00002901 case 0x0B: // cmpi (Compare Immediate, PPC32 p368)
2902 DIP("cmpi cr%u,%u,r%u,%d\n", crfD, flag_L, rA_addr,
2903 (Int)extend_s_16to32(uimm16));
cerion2831b002005-11-30 19:55:22 +00002904 b = mkSzExtendS16( ty, uimm16 );
cerionbb01b7c2005-12-16 13:40:18 +00002905 if (flag_L == 1) {
cerion2831b002005-11-30 19:55:22 +00002906 putCR321(crfD, unop(Iop_64to8, binop(Iop_CmpORD64S, a, b)));
ceriond953ebb2005-11-29 13:27:20 +00002907 } else {
cerionbb01b7c2005-12-16 13:40:18 +00002908 a = mkSzNarrow32( ty, a );
2909 b = mkSzNarrow32( ty, b );
ceriond953ebb2005-11-29 13:27:20 +00002910 putCR321(crfD, unop(Iop_32to8, binop(Iop_CmpORD32S, a, b)));
2911 }
2912 putCR0( crfD, getXER_SO() );
2913 break;
2914
2915 case 0x0A: // cmpli (Compare Logical Immediate, PPC32 p370)
2916 DIP("cmpli cr%u,%u,r%u,0x%x\n", crfD, flag_L, rA_addr, uimm16);
cerion2831b002005-11-30 19:55:22 +00002917 b = mkSzImm( ty, uimm16 );
cerionbb01b7c2005-12-16 13:40:18 +00002918 if (flag_L == 1) {
cerion2831b002005-11-30 19:55:22 +00002919 putCR321(crfD, unop(Iop_64to8, binop(Iop_CmpORD64U, a, b)));
ceriond953ebb2005-11-29 13:27:20 +00002920 } else {
cerionbb01b7c2005-12-16 13:40:18 +00002921 a = mkSzNarrow32( ty, a );
2922 b = mkSzNarrow32( ty, b );
ceriond953ebb2005-11-29 13:27:20 +00002923 putCR321(crfD, unop(Iop_32to8, binop(Iop_CmpORD32U, a, b)));
2924 }
2925 putCR0( crfD, getXER_SO() );
2926 break;
cerionb85e8bb2005-02-16 08:54:33 +00002927
2928 /* X Form */
2929 case 0x1F:
2930 if (b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002931 vex_printf("dis_int_cmp(ppc)(0x1F,b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002932 return False;
2933 }
cerionbb01b7c2005-12-16 13:40:18 +00002934 b = getIReg(rB_addr);
cerion7aa4bbc2005-01-29 09:32:07 +00002935
cerionb85e8bb2005-02-16 08:54:33 +00002936 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00002937 case 0x000: // cmp (Compare, PPC32 p367)
2938 DIP("cmp cr%u,%u,r%u,r%u\n", crfD, flag_L, rA_addr, rB_addr);
cerionbb01b7c2005-12-16 13:40:18 +00002939 if (flag_L == 1) {
2940 putCR321(crfD, unop(Iop_64to8, binop(Iop_CmpORD64S, a, b)));
ceriond953ebb2005-11-29 13:27:20 +00002941 } else {
cerionbb01b7c2005-12-16 13:40:18 +00002942 a = mkSzNarrow32( ty, a );
2943 b = mkSzNarrow32( ty, b );
2944 putCR321(crfD, unop(Iop_32to8,binop(Iop_CmpORD32S, a, b)));
ceriond953ebb2005-11-29 13:27:20 +00002945 }
2946 putCR0( crfD, getXER_SO() );
2947 break;
cerionb85e8bb2005-02-16 08:54:33 +00002948
ceriond953ebb2005-11-29 13:27:20 +00002949 case 0x020: // cmpl (Compare Logical, PPC32 p369)
2950 DIP("cmpl cr%u,%u,r%u,r%u\n", crfD, flag_L, rA_addr, rB_addr);
cerionbb01b7c2005-12-16 13:40:18 +00002951 if (flag_L == 1) {
2952 putCR321(crfD, unop(Iop_64to8, binop(Iop_CmpORD64U, a, b)));
ceriond953ebb2005-11-29 13:27:20 +00002953 } else {
cerionbb01b7c2005-12-16 13:40:18 +00002954 a = mkSzNarrow32( ty, a );
2955 b = mkSzNarrow32( ty, b );
2956 putCR321(crfD, unop(Iop_32to8, binop(Iop_CmpORD32U, a, b)));
ceriond953ebb2005-11-29 13:27:20 +00002957 }
2958 putCR0( crfD, getXER_SO() );
2959 break;
cerion7aa4bbc2005-01-29 09:32:07 +00002960
ceriond953ebb2005-11-29 13:27:20 +00002961 default:
cerion5b2325f2005-12-23 00:55:09 +00002962 vex_printf("dis_int_cmp(ppc)(opc2)\n");
ceriond953ebb2005-11-29 13:27:20 +00002963 return False;
cerionb85e8bb2005-02-16 08:54:33 +00002964 }
2965 break;
ceriond953ebb2005-11-29 13:27:20 +00002966
cerionb85e8bb2005-02-16 08:54:33 +00002967 default:
cerion5b2325f2005-12-23 00:55:09 +00002968 vex_printf("dis_int_cmp(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002969 return False;
2970 }
2971
cerionb85e8bb2005-02-16 08:54:33 +00002972 return True;
cerion7aa4bbc2005-01-29 09:32:07 +00002973}
2974
2975
cerion3d870a32005-03-18 12:23:33 +00002976/*
2977 Integer Logical Instructions
2978*/
cerion7aa4bbc2005-01-29 09:32:07 +00002979static Bool dis_int_logic ( UInt theInstr )
2980{
cerion76de5cf2005-11-18 18:25:12 +00002981 /* D-Form, X-Form */
2982 UChar opc1 = ifieldOPC(theInstr);
2983 UChar rS_addr = ifieldRegDS(theInstr);
2984 UChar rA_addr = ifieldRegA(theInstr);
ceriond953ebb2005-11-29 13:27:20 +00002985 UInt uimm16 = ifieldUIMM16(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00002986 UChar rB_addr = ifieldRegB(theInstr);
2987 UInt opc2 = ifieldOPClo10(theInstr);
2988 UChar flag_rC = ifieldBIT0(theInstr);
cerionb85e8bb2005-02-16 08:54:33 +00002989
ceriond953ebb2005-11-29 13:27:20 +00002990 IRType ty = mode64 ? Ity_I64 : Ity_I32;
2991 IRTemp rS = newTemp(ty);
2992 IRTemp rA = newTemp(ty);
2993 IRTemp rB = newTemp(ty);
cerione9d361a2005-03-04 17:35:29 +00002994 IRExpr* irx;
ceriond953ebb2005-11-29 13:27:20 +00002995 Bool do_rc = False;
2996
cerion76de5cf2005-11-18 18:25:12 +00002997 assign( rS, getIReg(rS_addr) );
2998 assign( rB, getIReg(rB_addr) );
cerionb85e8bb2005-02-16 08:54:33 +00002999
3000 switch (opc1) {
cerione9d361a2005-03-04 17:35:29 +00003001 case 0x1C: // andi. (AND Immediate, PPC32 p358)
ceriond953ebb2005-11-29 13:27:20 +00003002 DIP("andi. r%u,r%u,0x%x\n", rA_addr, rS_addr, uimm16);
cerion2831b002005-11-30 19:55:22 +00003003 assign( rA, binop( mkSzOp(ty, Iop_And8), mkexpr(rS),
3004 mkSzImm(ty, uimm16)) );
cerion70e24122005-03-16 00:27:37 +00003005 do_rc = True; // Always record to CR
cerion76de5cf2005-11-18 18:25:12 +00003006 flag_rC = 1;
cerionb85e8bb2005-02-16 08:54:33 +00003007 break;
3008
cerione9d361a2005-03-04 17:35:29 +00003009 case 0x1D: // andis. (AND Immediate Shifted, PPC32 p359)
ceriond953ebb2005-11-29 13:27:20 +00003010 DIP("andis r%u,r%u,0x%x\n", rA_addr, rS_addr, uimm16);
cerion2831b002005-11-30 19:55:22 +00003011 assign( rA, binop( mkSzOp(ty, Iop_And8), mkexpr(rS),
3012 mkSzImm(ty, uimm16 << 16)) );
cerion70e24122005-03-16 00:27:37 +00003013 do_rc = True; // Always record to CR
cerion76de5cf2005-11-18 18:25:12 +00003014 flag_rC = 1;
cerionb85e8bb2005-02-16 08:54:33 +00003015 break;
cerion7aa4bbc2005-01-29 09:32:07 +00003016
cerione9d361a2005-03-04 17:35:29 +00003017 case 0x18: // ori (OR Immediate, PPC32 p497)
ceriond953ebb2005-11-29 13:27:20 +00003018 DIP("ori r%u,r%u,0x%x\n", rA_addr, rS_addr, uimm16);
cerion2831b002005-11-30 19:55:22 +00003019 assign( rA, binop( mkSzOp(ty, Iop_Or8), mkexpr(rS),
3020 mkSzImm(ty, uimm16)) );
cerionb85e8bb2005-02-16 08:54:33 +00003021 break;
cerion7aa4bbc2005-01-29 09:32:07 +00003022
cerione9d361a2005-03-04 17:35:29 +00003023 case 0x19: // oris (OR Immediate Shifted, PPC32 p498)
ceriond953ebb2005-11-29 13:27:20 +00003024 DIP("oris r%u,r%u,0x%x\n", rA_addr, rS_addr, uimm16);
cerion2831b002005-11-30 19:55:22 +00003025 assign( rA, binop( mkSzOp(ty, Iop_Or8), mkexpr(rS),
3026 mkSzImm(ty, uimm16 << 16)) );
cerionb85e8bb2005-02-16 08:54:33 +00003027 break;
cerionaabdfbf2005-01-29 12:56:15 +00003028
cerione9d361a2005-03-04 17:35:29 +00003029 case 0x1A: // xori (XOR Immediate, PPC32 p550)
ceriond953ebb2005-11-29 13:27:20 +00003030 DIP("xori r%u,r%u,0x%x\n", rA_addr, rS_addr, uimm16);
cerion2831b002005-11-30 19:55:22 +00003031 assign( rA, binop( mkSzOp(ty, Iop_Xor8), mkexpr(rS),
3032 mkSzImm(ty, uimm16)) );
cerionb85e8bb2005-02-16 08:54:33 +00003033 break;
cerion38674602005-02-08 02:19:25 +00003034
cerione9d361a2005-03-04 17:35:29 +00003035 case 0x1B: // xoris (XOR Immediate Shifted, PPC32 p551)
ceriond953ebb2005-11-29 13:27:20 +00003036 DIP("xoris r%u,r%u,0x%x\n", rA_addr, rS_addr, uimm16);
cerion2831b002005-11-30 19:55:22 +00003037 assign( rA, binop( mkSzOp(ty, Iop_Xor8), mkexpr(rS),
3038 mkSzImm(ty, uimm16 << 16)) );
cerionb85e8bb2005-02-16 08:54:33 +00003039 break;
cerionaabdfbf2005-01-29 12:56:15 +00003040
cerionb85e8bb2005-02-16 08:54:33 +00003041 /* X Form */
3042 case 0x1F:
cerion70e24122005-03-16 00:27:37 +00003043 do_rc = True; // All below record to CR
3044
cerionb85e8bb2005-02-16 08:54:33 +00003045 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00003046 case 0x01C: // and (AND, PPC32 p356)
3047 DIP("and%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003048 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003049 assign(rA, binop( mkSzOp(ty, Iop_And8),
ceriond953ebb2005-11-29 13:27:20 +00003050 mkexpr(rS), mkexpr(rB)));
3051 break;
3052
3053 case 0x03C: // andc (AND with Complement, PPC32 p357)
3054 DIP("andc%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003055 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003056 assign(rA, binop( mkSzOp(ty, Iop_And8), mkexpr(rS),
3057 unop( mkSzOp(ty, Iop_Not8),
ceriond953ebb2005-11-29 13:27:20 +00003058 mkexpr(rB))));
3059 break;
3060
3061 case 0x01A: { // cntlzw (Count Leading Zeros Word, PPC32 p371)
3062 IRExpr* lo32;
3063 if (rB_addr!=0) {
cerion5b2325f2005-12-23 00:55:09 +00003064 vex_printf("dis_int_logic(ppc)(cntlzw,rB_addr)\n");
ceriond953ebb2005-11-29 13:27:20 +00003065 return False;
3066 }
3067 DIP("cntlzw%s r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003068 flag_rC ? ".":"", rA_addr, rS_addr);
ceriond953ebb2005-11-29 13:27:20 +00003069
3070 // mode64: count in low word only
3071 lo32 = mode64 ? unop(Iop_64to32, mkexpr(rS)) : mkexpr(rS);
3072
3073 // Iop_Clz32 undefined for arg==0, so deal with that case:
3074 irx = binop(Iop_CmpNE32, lo32, mkU32(0));
cerion5b2325f2005-12-23 00:55:09 +00003075 assign(rA, mkSzWiden32(ty,
3076 IRExpr_Mux0X( unop(Iop_1Uto8, irx),
3077 mkU32(32),
3078 unop(Iop_Clz32, lo32)),
3079 False));
3080
ceriond953ebb2005-11-29 13:27:20 +00003081 // TODO: alternatively: assign(rA, verbose_Clz32(rS));
3082 break;
3083 }
3084
3085 case 0x11C: // eqv (Equivalent, PPC32 p396)
3086 DIP("eqv%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003087 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003088 assign( rA, unop( mkSzOp(ty, Iop_Not8),
3089 binop( mkSzOp(ty, Iop_Xor8),
ceriond953ebb2005-11-29 13:27:20 +00003090 mkexpr(rS), mkexpr(rB))) );
sewardj20ef5472005-07-21 14:48:31 +00003091 break;
cerion7aa4bbc2005-01-29 09:32:07 +00003092
cerione9d361a2005-03-04 17:35:29 +00003093 case 0x3BA: // extsb (Extend Sign Byte, PPC32 p397
cerion76de5cf2005-11-18 18:25:12 +00003094 if (rB_addr!=0) {
cerion5b2325f2005-12-23 00:55:09 +00003095 vex_printf("dis_int_logic(ppc)(extsb,rB_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003096 return False;
3097 }
ceriond953ebb2005-11-29 13:27:20 +00003098 DIP("extsb%s r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003099 flag_rC ? ".":"", rA_addr, rS_addr);
ceriond953ebb2005-11-29 13:27:20 +00003100 if (mode64)
3101 assign( rA, unop(Iop_8Sto64, unop(Iop_64to8, mkexpr(rS))) );
3102 else
3103 assign( rA, unop(Iop_8Sto32, unop(Iop_32to8, mkexpr(rS))) );
cerionb85e8bb2005-02-16 08:54:33 +00003104 break;
cerion7aa4bbc2005-01-29 09:32:07 +00003105
cerione9d361a2005-03-04 17:35:29 +00003106 case 0x39A: // extsh (Extend Sign Half Word, PPC32 p398)
cerion76de5cf2005-11-18 18:25:12 +00003107 if (rB_addr!=0) {
cerion5b2325f2005-12-23 00:55:09 +00003108 vex_printf("dis_int_logic(ppc)(extsh,rB_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003109 return False;
3110 }
ceriond953ebb2005-11-29 13:27:20 +00003111 DIP("extsh%s r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003112 flag_rC ? ".":"", rA_addr, rS_addr);
ceriond953ebb2005-11-29 13:27:20 +00003113 if (mode64)
cerion5b2325f2005-12-23 00:55:09 +00003114 assign( rA, unop(Iop_16Sto64,
3115 unop(Iop_64to16, mkexpr(rS))) );
ceriond953ebb2005-11-29 13:27:20 +00003116 else
cerion5b2325f2005-12-23 00:55:09 +00003117 assign( rA, unop(Iop_16Sto32,
3118 unop(Iop_32to16, mkexpr(rS))) );
cerionb85e8bb2005-02-16 08:54:33 +00003119 break;
cerion7aa4bbc2005-01-29 09:32:07 +00003120
cerione9d361a2005-03-04 17:35:29 +00003121 case 0x1DC: // nand (NAND, PPC32 p492)
ceriond953ebb2005-11-29 13:27:20 +00003122 DIP("nand%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003123 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003124 assign( rA, unop( mkSzOp(ty, Iop_Not8),
3125 binop( mkSzOp(ty, Iop_And8),
ceriond953ebb2005-11-29 13:27:20 +00003126 mkexpr(rS), mkexpr(rB))) );
cerionb85e8bb2005-02-16 08:54:33 +00003127 break;
3128
cerione9d361a2005-03-04 17:35:29 +00003129 case 0x07C: // nor (NOR, PPC32 p494)
ceriond953ebb2005-11-29 13:27:20 +00003130 DIP("nor%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003131 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003132 assign( rA, unop( mkSzOp(ty, Iop_Not8),
3133 binop( mkSzOp(ty, Iop_Or8),
ceriond953ebb2005-11-29 13:27:20 +00003134 mkexpr(rS), mkexpr(rB))) );
cerionb85e8bb2005-02-16 08:54:33 +00003135 break;
cerion7aa4bbc2005-01-29 09:32:07 +00003136
cerione9d361a2005-03-04 17:35:29 +00003137 case 0x1BC: // or (OR, PPC32 p495)
cerion76de5cf2005-11-18 18:25:12 +00003138 if ((!flag_rC) && rS_addr == rB_addr) {
ceriond953ebb2005-11-29 13:27:20 +00003139 DIP("mr r%u,r%u\n", rA_addr, rS_addr);
cerion76de5cf2005-11-18 18:25:12 +00003140 assign( rA, mkexpr(rS) );
sewardjb51f0f42005-07-18 11:38:02 +00003141 } else {
ceriond953ebb2005-11-29 13:27:20 +00003142 DIP("or%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003143 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003144 assign( rA, binop( mkSzOp(ty, Iop_Or8),
ceriond953ebb2005-11-29 13:27:20 +00003145 mkexpr(rS), mkexpr(rB)) );
sewardjb51f0f42005-07-18 11:38:02 +00003146 }
cerionb85e8bb2005-02-16 08:54:33 +00003147 break;
cerion7aa4bbc2005-01-29 09:32:07 +00003148
cerione9d361a2005-03-04 17:35:29 +00003149 case 0x19C: // orc (OR with Complement, PPC32 p496)
ceriond953ebb2005-11-29 13:27:20 +00003150 DIP("orc%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003151 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003152 assign( rA, binop( mkSzOp(ty, Iop_Or8), mkexpr(rS),
3153 unop(mkSzOp(ty, Iop_Not8), mkexpr(rB))));
cerionb85e8bb2005-02-16 08:54:33 +00003154 break;
3155
cerione9d361a2005-03-04 17:35:29 +00003156 case 0x13C: // xor (XOR, PPC32 p549)
ceriond953ebb2005-11-29 13:27:20 +00003157 DIP("xor%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003158 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003159 assign( rA, binop( mkSzOp(ty, Iop_Xor8),
ceriond953ebb2005-11-29 13:27:20 +00003160 mkexpr(rS), mkexpr(rB)) );
cerionb85e8bb2005-02-16 08:54:33 +00003161 break;
cerion7aa4bbc2005-01-29 09:32:07 +00003162
cerionf0de28c2005-12-13 20:21:11 +00003163
3164 /* 64bit Integer Logical Instructions */
3165 case 0x3DA: // extsw (Extend Sign Word, PPC64 p430)
3166 if (rB_addr!=0) {
cerion5b2325f2005-12-23 00:55:09 +00003167 vex_printf("dis_int_logic(ppc)(extsw,rB_addr)\n");
cerionf0de28c2005-12-13 20:21:11 +00003168 return False;
3169 }
cerion5b2325f2005-12-23 00:55:09 +00003170 DIP("extsw%s r%u,r%u\n", flag_rC ? ".":"", rA_addr, rS_addr);
cerionf0de28c2005-12-13 20:21:11 +00003171 assign(rA, unop(Iop_32Sto64, unop(Iop_64to32, mkexpr(rS))));
3172 break;
3173
cerion5b2325f2005-12-23 00:55:09 +00003174 case 0x03A: // cntlzd (Count Leading Zeros DWord, PPC64 p401)
cerionf0de28c2005-12-13 20:21:11 +00003175 if (rB_addr!=0) {
cerion5b2325f2005-12-23 00:55:09 +00003176 vex_printf("dis_int_logic(ppc)(cntlzd,rB_addr)\n");
cerionf0de28c2005-12-13 20:21:11 +00003177 return False;
3178 }
cerion5b2325f2005-12-23 00:55:09 +00003179 DIP("cntlzd%s r%u,r%u\n",
3180 flag_rC ? ".":"", rA_addr, rS_addr);
cerion07b07a92005-12-22 14:32:35 +00003181 // Iop_Clz64 undefined for arg==0, so deal with that case:
3182 irx = binop(Iop_CmpNE64, mkexpr(rS), mkU64(0));
3183 assign(rA, IRExpr_Mux0X( unop(Iop_1Uto8, irx),
3184 mkU64(64),
3185 unop(Iop_Clz64, mkexpr(rS)) ));
cerion5b2325f2005-12-23 00:55:09 +00003186 // TODO: alternatively: assign(rA, verbose_Clz64(rS));
cerion07b07a92005-12-22 14:32:35 +00003187 break;
cerionf0de28c2005-12-13 20:21:11 +00003188
cerionb85e8bb2005-02-16 08:54:33 +00003189 default:
cerion5b2325f2005-12-23 00:55:09 +00003190 vex_printf("dis_int_logic(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003191 return False;
3192 }
cerionb85e8bb2005-02-16 08:54:33 +00003193 break;
3194
3195 default:
cerion5b2325f2005-12-23 00:55:09 +00003196 vex_printf("dis_int_logic(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003197 return False;
3198 }
cerion70e24122005-03-16 00:27:37 +00003199
cerion76de5cf2005-11-18 18:25:12 +00003200 putIReg( rA_addr, mkexpr(rA) );
3201
3202 if (do_rc && flag_rC) {
3203 set_CR0( mkexpr(rA) );
cerionb85e8bb2005-02-16 08:54:33 +00003204 }
3205 return True;
cerion645c9302005-01-31 10:09:59 +00003206}
3207
3208
3209
cerion3d870a32005-03-18 12:23:33 +00003210/*
3211 Integer Rotate Instructions
3212*/
cerion645c9302005-01-31 10:09:59 +00003213static Bool dis_int_rot ( UInt theInstr )
3214{
cerionf0de28c2005-12-13 20:21:11 +00003215 /* M-Form, MDS-Form */
ceriond953ebb2005-11-29 13:27:20 +00003216 UChar opc1 = ifieldOPC(theInstr);
3217 UChar rS_addr = ifieldRegDS(theInstr);
3218 UChar rA_addr = ifieldRegA(theInstr);
3219 UChar rB_addr = ifieldRegB(theInstr);
3220 UChar sh_imm = rB_addr;
3221 UChar MaskBeg = toUChar( IFIELD( theInstr, 6, 5 ) );
3222 UChar MaskEnd = toUChar( IFIELD( theInstr, 1, 5 ) );
cerionf0de28c2005-12-13 20:21:11 +00003223 UChar msk_imm = toUChar( IFIELD( theInstr, 5, 6 ) );
3224 UChar opc2 = toUChar( IFIELD( theInstr, 2, 3 ) );
3225 UChar b1 = ifieldBIT1(theInstr);
ceriond953ebb2005-11-29 13:27:20 +00003226 UChar flag_rC = ifieldBIT0(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00003227
ceriond953ebb2005-11-29 13:27:20 +00003228 IRType ty = mode64 ? Ity_I64 : Ity_I32;
3229 IRTemp rS = newTemp(ty);
3230 IRTemp rA = newTemp(ty);
3231 IRTemp rB = newTemp(ty);
cerionbb01b7c2005-12-16 13:40:18 +00003232 IRTemp rot = newTemp(ty);
ceriond953ebb2005-11-29 13:27:20 +00003233 IRExpr *r;
cerionf0de28c2005-12-13 20:21:11 +00003234 UInt mask32;
3235 ULong mask64;
ceriond953ebb2005-11-29 13:27:20 +00003236
cerion76de5cf2005-11-18 18:25:12 +00003237 assign( rS, getIReg(rS_addr) );
3238 assign( rB, getIReg(rB_addr) );
ceriond953ebb2005-11-29 13:27:20 +00003239
cerionb85e8bb2005-02-16 08:54:33 +00003240 switch (opc1) {
ceriond953ebb2005-11-29 13:27:20 +00003241 case 0x14: {
cerion5b2325f2005-12-23 00:55:09 +00003242 // rlwimi (Rotate Left Word Imm then Mask Insert, PPC32 p500)
3243 DIP("rlwimi%s r%u,r%u,%d,%d,%d\n", flag_rC ? ".":"",
ceriond953ebb2005-11-29 13:27:20 +00003244 rA_addr, rS_addr, sh_imm, MaskBeg, MaskEnd);
3245 if (mode64) {
3246 // tmp32 = (ROTL(rS_Lo32, Imm)
3247 // rA = ((tmp32 || tmp32) & mask64) | (rA & ~mask64)
cerionf0de28c2005-12-13 20:21:11 +00003248 mask64 = MASK64(31-MaskEnd, 31-MaskBeg);
ceriond953ebb2005-11-29 13:27:20 +00003249 r = ROTL( unop(Iop_64to32, mkexpr(rS) ), mkU8(sh_imm) );
3250 r = unop(Iop_32Uto64, r);
cerion5b2325f2005-12-23 00:55:09 +00003251 assign( rot, binop(Iop_Or64, r,
3252 binop(Iop_Shl64, r, mkU8(32))) );
ceriond953ebb2005-11-29 13:27:20 +00003253 assign( rA,
3254 binop(Iop_Or64,
cerionbb01b7c2005-12-16 13:40:18 +00003255 binop(Iop_And64, mkexpr(rot), mkU64(mask64)),
cerionf0de28c2005-12-13 20:21:11 +00003256 binop(Iop_And64, getIReg(rA_addr), mkU64(~mask64))) );
sewardj26b33202005-10-07 09:45:16 +00003257 }
3258 else {
ceriond953ebb2005-11-29 13:27:20 +00003259 // rA = (ROTL(rS, Imm) & mask) | (rA & ~mask);
cerionf0de28c2005-12-13 20:21:11 +00003260 mask32 = MASK32(31-MaskEnd, 31-MaskBeg);
3261 r = ROTL(mkexpr(rS), mkU8(sh_imm));
ceriond953ebb2005-11-29 13:27:20 +00003262 assign( rA,
3263 binop(Iop_Or32,
cerionf0de28c2005-12-13 20:21:11 +00003264 binop(Iop_And32, mkU32(mask32), r),
3265 binop(Iop_And32, getIReg(rA_addr), mkU32(~mask32))) );
sewardj26b33202005-10-07 09:45:16 +00003266 }
cerionb85e8bb2005-02-16 08:54:33 +00003267 break;
ceriond953ebb2005-11-29 13:27:20 +00003268 }
cerion45b70ff2005-01-31 17:03:25 +00003269
ceriond953ebb2005-11-29 13:27:20 +00003270 case 0x15: {
cerion5b2325f2005-12-23 00:55:09 +00003271 // rlwinm (Rotate Left Word Imm then AND with Mask, PPC32 p501)
ceriond953ebb2005-11-29 13:27:20 +00003272 vassert(MaskBeg < 32);
3273 vassert(MaskEnd < 32);
3274 vassert(sh_imm < 32);
3275
3276 if (mode64) {
cerionf0de28c2005-12-13 20:21:11 +00003277 mask64 = MASK64(31-MaskEnd, 31-MaskBeg);
cerion5b2325f2005-12-23 00:55:09 +00003278 DIP("rlwinm%s r%u,r%u,%d,%d,%d\n", flag_rC ? ".":"",
ceriond953ebb2005-11-29 13:27:20 +00003279 rA_addr, rS_addr, sh_imm, MaskBeg, MaskEnd);
3280 // tmp32 = (ROTL(rS_Lo32, Imm)
3281 // rA = ((tmp32 || tmp32) & mask64)
3282 r = ROTL( unop(Iop_64to32, mkexpr(rS) ), mkU8(sh_imm) );
3283 r = unop(Iop_32Uto64, r);
cerion5b2325f2005-12-23 00:55:09 +00003284 assign( rot, binop(Iop_Or64, r,
3285 binop(Iop_Shl64, r, mkU8(32))) );
cerionbb01b7c2005-12-16 13:40:18 +00003286 assign( rA, binop(Iop_And64, mkexpr(rot), mkU64(mask64)) );
ceriond953ebb2005-11-29 13:27:20 +00003287 }
3288 else {
3289 if (MaskBeg == 0 && sh_imm+MaskEnd == 31) {
3290 /* Special-case the ,n,0,31-n form as that is just n-bit
cerion5b2325f2005-12-23 00:55:09 +00003291 shift left, PPC32 p501 */
3292 DIP("slwi%s r%u,r%u,%d\n", flag_rC ? ".":"",
ceriond953ebb2005-11-29 13:27:20 +00003293 rA_addr, rS_addr, sh_imm);
3294 assign( rA, binop(Iop_Shl32, mkexpr(rS), mkU8(sh_imm)) );
3295 }
cerion2831b002005-11-30 19:55:22 +00003296 else if (MaskEnd == 31 && sh_imm+MaskBeg == 32) {
3297 /* Special-case the ,32-n,n,31 form as that is just n-bit
cerion5b2325f2005-12-23 00:55:09 +00003298 unsigned shift right, PPC32 p501 */
3299 DIP("srwi%s r%u,r%u,%d\n", flag_rC ? ".":"",
cerion2831b002005-11-30 19:55:22 +00003300 rA_addr, rS_addr, sh_imm);
3301 assign( rA, binop(Iop_Shr32, mkexpr(rS), mkU8(MaskBeg)) );
3302 }
3303 else {
3304 /* General case. */
cerionf0de28c2005-12-13 20:21:11 +00003305 mask32 = MASK32(31-MaskEnd, 31-MaskBeg);
cerion5b2325f2005-12-23 00:55:09 +00003306 DIP("rlwinm%s r%u,r%u,%d,%d,%d\n", flag_rC ? ".":"",
cerion2831b002005-11-30 19:55:22 +00003307 rA_addr, rS_addr, sh_imm, MaskBeg, MaskEnd);
3308 // rA = ROTL(rS, Imm) & mask
cerion5b2325f2005-12-23 00:55:09 +00003309 assign( rA, binop(Iop_And32,
3310 ROTL(mkexpr(rS), mkU8(sh_imm)),
cerionf0de28c2005-12-13 20:21:11 +00003311 mkU32(mask32)) );
cerion2831b002005-11-30 19:55:22 +00003312 }
ceriond953ebb2005-11-29 13:27:20 +00003313 }
sewardjc9659532005-07-21 21:33:57 +00003314 break;
ceriond953ebb2005-11-29 13:27:20 +00003315 }
3316
3317 case 0x17: {
3318 // rlwnm (Rotate Left Word then AND with Mask, PPC32 p503
cerion5b2325f2005-12-23 00:55:09 +00003319 DIP("rlwnm%s r%u,r%u,r%u,%d,%d\n", flag_rC ? ".":"",
ceriond953ebb2005-11-29 13:27:20 +00003320 rA_addr, rS_addr, rB_addr, MaskBeg, MaskEnd);
3321 if (mode64) {
cerionf0de28c2005-12-13 20:21:11 +00003322 mask64 = MASK64(31-MaskEnd, 31-MaskBeg);
cerionbb01b7c2005-12-16 13:40:18 +00003323 /* weird insn alert!
3324 tmp32 = (ROTL(rS_Lo32, rB[0-4])
3325 rA = ((tmp32 || tmp32) & mask64)
3326 */
ceriond953ebb2005-11-29 13:27:20 +00003327 // note, ROTL does the masking, so we don't do it here
3328 r = ROTL( unop(Iop_64to32, mkexpr(rS)),
cerionbb01b7c2005-12-16 13:40:18 +00003329 unop(Iop_64to8, mkexpr(rB)) );
ceriond953ebb2005-11-29 13:27:20 +00003330 r = unop(Iop_32Uto64, r);
cerionbb01b7c2005-12-16 13:40:18 +00003331 assign(rot, binop(Iop_Or64, r, binop(Iop_Shl64, r, mkU8(32))));
3332 assign( rA, binop(Iop_And64, mkexpr(rot), mkU64(mask64)) );
ceriond953ebb2005-11-29 13:27:20 +00003333 } else {
cerionf0de28c2005-12-13 20:21:11 +00003334 mask32 = MASK32(31-MaskEnd, 31-MaskBeg);
ceriond953ebb2005-11-29 13:27:20 +00003335 // rA = ROTL(rS, rB[0-4]) & mask
3336 // note, ROTL does the masking, so we don't do it here
3337 assign( rA, binop(Iop_And32,
cerion5b2325f2005-12-23 00:55:09 +00003338 ROTL(mkexpr(rS),
3339 unop(Iop_32to8, mkexpr(rB))),
cerionf0de28c2005-12-13 20:21:11 +00003340 mkU32(mask32)) );
ceriond953ebb2005-11-29 13:27:20 +00003341 }
3342 break;
3343 }
cerion45b70ff2005-01-31 17:03:25 +00003344
cerionf0de28c2005-12-13 20:21:11 +00003345
3346 /* 64bit Integer Rotates */
3347 case 0x1E: {
3348 msk_imm = ((msk_imm & 1) << 5) | (msk_imm >> 1);
3349 sh_imm |= b1 << 5;
3350
3351 vassert( msk_imm < 64 );
3352 vassert( sh_imm < 64 );
3353
3354 switch (opc2) {
cerion07b07a92005-12-22 14:32:35 +00003355 case 0x4: {
3356 /* r = ROTL64( rS, rB_lo6) */
3357 r = ROTL( mkexpr(rS), unop(Iop_64to8, mkexpr(rB)) );
3358
cerion5b2325f2005-12-23 00:55:09 +00003359 if (b1 == 0) { // rldcl (Rotl DWord, Clear Left, PPC64 p555)
3360 DIP("rldcl%s r%u,r%u,r%u,%u\n", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00003361 rA_addr, rS_addr, rB_addr, msk_imm);
cerion07b07a92005-12-22 14:32:35 +00003362 // note, ROTL does the masking, so we don't do it here
cerionf0de28c2005-12-13 20:21:11 +00003363 mask64 = MASK64(0, 63-msk_imm);
3364 assign( rA, binop(Iop_And64, r, mkU64(mask64)) );
3365 break;
cerion5b2325f2005-12-23 00:55:09 +00003366 } else { // rldcr (Rotl DWord, Clear Right, PPC64 p556)
3367 DIP("rldcr%s r%u,r%u,r%u,%u\n", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00003368 rA_addr, rS_addr, rB_addr, msk_imm);
cerionf0de28c2005-12-13 20:21:11 +00003369 mask64 = MASK64(63-msk_imm, 63);
3370 assign( rA, binop(Iop_And64, r, mkU64(mask64)) );
3371 break;
3372 }
3373 break;
cerion07b07a92005-12-22 14:32:35 +00003374 }
cerion5b2325f2005-12-23 00:55:09 +00003375 case 0x2: // rldic (Rotl DWord Imm, Clear, PPC64 p557)
3376 DIP("rldic%s r%u,r%u,%u,%u\n", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00003377 rA_addr, rS_addr, sh_imm, msk_imm);
3378 r = ROTL(mkexpr(rS), mkU8(sh_imm));
3379 mask64 = MASK64(sh_imm, 63-msk_imm);
3380 assign( rA, binop(Iop_And64, r, mkU64(mask64)) );
3381 break;
3382 // later: deal with special case: (msk_imm==0) => SHL(sh_imm)
3383 /*
3384 Hmm... looks like this'll do the job more simply:
3385 r = SHL(rS, sh_imm)
3386 m = ~(1 << (63-msk_imm))
3387 assign(rA, r & m);
3388 */
3389
cerion5b2325f2005-12-23 00:55:09 +00003390 case 0x0: // rldicl (Rotl DWord Imm, Clear Left, PPC64 p558)
3391 DIP("rldicl%s r%u,r%u,%u,%u\n", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00003392 rA_addr, rS_addr, sh_imm, msk_imm);
3393 r = ROTL(mkexpr(rS), mkU8(sh_imm));
3394 mask64 = MASK64(0, 63-msk_imm);
3395 assign( rA, binop(Iop_And64, r, mkU64(mask64)) );
3396 break;
cerion5b2325f2005-12-23 00:55:09 +00003397 /* later: deal with special case:
3398 (msk_imm + sh_imm == 63) => SHR(63 - sh_imm) */
cerionf0de28c2005-12-13 20:21:11 +00003399
cerion5b2325f2005-12-23 00:55:09 +00003400 case 0x1: // rldicr (Rotl DWord Imm, Clear Right, PPC64 p559)
3401 DIP("rldicr%s r%u,r%u,%u,%u\n", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00003402 rA_addr, rS_addr, sh_imm, msk_imm);
3403 r = ROTL(mkexpr(rS), mkU8(sh_imm));
3404 mask64 = MASK64(63-msk_imm, 63);
3405 assign( rA, binop(Iop_And64, r, mkU64(mask64)) );
3406 break;
cerion5b2325f2005-12-23 00:55:09 +00003407 /* later: deal with special case:
3408 (msk_imm == sh_imm) => SHL(sh_imm) */
cerionf0de28c2005-12-13 20:21:11 +00003409
cerion5b2325f2005-12-23 00:55:09 +00003410 case 0x3: { // rldimi (Rotl DWord Imm, Mask Insert, PPC64 p560)
cerion07b07a92005-12-22 14:32:35 +00003411 IRTemp rA_orig = newTemp(ty);
cerion5b2325f2005-12-23 00:55:09 +00003412 DIP("rldimi%s r%u,r%u,%u,%u\n", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00003413 rA_addr, rS_addr, sh_imm, msk_imm);
3414 r = ROTL(mkexpr(rS), mkU8(sh_imm));
3415 mask64 = MASK64(sh_imm, 63-msk_imm);
cerion07b07a92005-12-22 14:32:35 +00003416 assign( rA_orig, getIReg(rA_addr) );
cerionf0de28c2005-12-13 20:21:11 +00003417 assign( rA, binop(Iop_Or64,
3418 binop(Iop_And64, mkU64(mask64), r),
cerion5b2325f2005-12-23 00:55:09 +00003419 binop(Iop_And64, mkU64(~mask64),
3420 mkexpr(rA_orig))) );
cerionf0de28c2005-12-13 20:21:11 +00003421 break;
cerion07b07a92005-12-22 14:32:35 +00003422 }
cerionf0de28c2005-12-13 20:21:11 +00003423 default:
cerion5b2325f2005-12-23 00:55:09 +00003424 vex_printf("dis_int_rot(ppc)(opc2)\n");
cerionf0de28c2005-12-13 20:21:11 +00003425 return False;
3426 }
3427 break;
3428 }
3429
cerionb85e8bb2005-02-16 08:54:33 +00003430 default:
cerion5b2325f2005-12-23 00:55:09 +00003431 vex_printf("dis_int_rot(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003432 return False;
3433 }
cerion645c9302005-01-31 10:09:59 +00003434
cerion76de5cf2005-11-18 18:25:12 +00003435 putIReg( rA_addr, mkexpr(rA) );
3436
3437 if (flag_rC) {
3438 set_CR0( mkexpr(rA) );
cerionb85e8bb2005-02-16 08:54:33 +00003439 }
3440 return True;
cerion645c9302005-01-31 10:09:59 +00003441}
3442
3443
cerion3d870a32005-03-18 12:23:33 +00003444/*
3445 Integer Load Instructions
3446*/
cerion645c9302005-01-31 10:09:59 +00003447static Bool dis_int_load ( UInt theInstr )
3448{
cerionf0de28c2005-12-13 20:21:11 +00003449 /* D-Form, X-Form, DS-Form */
cerion76de5cf2005-11-18 18:25:12 +00003450 UChar opc1 = ifieldOPC(theInstr);
3451 UChar rD_addr = ifieldRegDS(theInstr);
3452 UChar rA_addr = ifieldRegA(theInstr);
ceriond953ebb2005-11-29 13:27:20 +00003453 UInt uimm16 = ifieldUIMM16(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00003454 UChar rB_addr = ifieldRegB(theInstr);
3455 UInt opc2 = ifieldOPClo10(theInstr);
cerionf0de28c2005-12-13 20:21:11 +00003456 UChar b1 = ifieldBIT1(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00003457 UChar b0 = ifieldBIT0(theInstr);
3458
ceriond953ebb2005-11-29 13:27:20 +00003459 Int simm16 = extend_s_16to32(uimm16);
3460 IRType ty = mode64 ? Ity_I64 : Ity_I32;
ceriond953ebb2005-11-29 13:27:20 +00003461 IRTemp EA = newTemp(ty);
3462 IRExpr* val;
cerionedf7fc52005-11-18 20:57:41 +00003463
cerionf0de28c2005-12-13 20:21:11 +00003464 switch (opc1) {
3465 case 0x1F: // register offset
ceriond953ebb2005-11-29 13:27:20 +00003466 assign( EA, ea_rAor0_idxd( rA_addr, rB_addr ) );
cerionf0de28c2005-12-13 20:21:11 +00003467 break;
3468 case 0x3A: // immediate offset: 64bit
3469 simm16 = simm16 & 0xFFFFFFFC;
3470 default: // immediate offset
3471 assign( EA, ea_rAor0_simm( rA_addr, simm16 ) );
3472 break;
ceriond953ebb2005-11-29 13:27:20 +00003473 }
cerione9d361a2005-03-04 17:35:29 +00003474
cerionb85e8bb2005-02-16 08:54:33 +00003475 switch (opc1) {
cerione9d361a2005-03-04 17:35:29 +00003476 case 0x22: // lbz (Load B & Zero, PPC32 p433)
ceriond953ebb2005-11-29 13:27:20 +00003477 DIP("lbz r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3478 val = loadBE(Ity_I8, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003479 putIReg( rD_addr, mkSzWiden8(ty, val, False) );
cerionb85e8bb2005-02-16 08:54:33 +00003480 break;
3481
cerion5b2325f2005-12-23 00:55:09 +00003482 case 0x23: // lbzu (Load B & Zero, Update, PPC32 p434)
cerion76de5cf2005-11-18 18:25:12 +00003483 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003484 vex_printf("dis_int_load(ppc)(lbzu,rA_addr|rD_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003485 return False;
3486 }
ceriond953ebb2005-11-29 13:27:20 +00003487 DIP("lbzu r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3488 val = loadBE(Ity_I8, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003489 putIReg( rD_addr, mkSzWiden8(ty, val, False) );
ceriond953ebb2005-11-29 13:27:20 +00003490 putIReg( rA_addr, mkexpr(EA) );
cerionb85e8bb2005-02-16 08:54:33 +00003491 break;
3492
cerion5b2325f2005-12-23 00:55:09 +00003493 case 0x2A: // lha (Load HW Alg, PPC32 p445)
ceriond953ebb2005-11-29 13:27:20 +00003494 DIP("lha r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3495 val = loadBE(Ity_I16, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003496 putIReg( rD_addr, mkSzWiden16(ty, val, True) );
cerionb85e8bb2005-02-16 08:54:33 +00003497 break;
cerion645c9302005-01-31 10:09:59 +00003498
cerion5b2325f2005-12-23 00:55:09 +00003499 case 0x2B: // lhau (Load HW Alg, Update, PPC32 p446)
cerion76de5cf2005-11-18 18:25:12 +00003500 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003501 vex_printf("dis_int_load(ppc)(lhau,rA_addr|rD_addr)\n");
cerioncb14e732005-09-09 16:38:19 +00003502 return False;
3503 }
ceriond953ebb2005-11-29 13:27:20 +00003504 DIP("lhau r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3505 val = loadBE(Ity_I16, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003506 putIReg( rD_addr, mkSzWiden16(ty, val, True) );
ceriond953ebb2005-11-29 13:27:20 +00003507 putIReg( rA_addr, mkexpr(EA) );
cerioncb14e732005-09-09 16:38:19 +00003508 break;
cerionb85e8bb2005-02-16 08:54:33 +00003509
cerione9d361a2005-03-04 17:35:29 +00003510 case 0x28: // lhz (Load HW & Zero, PPC32 p450)
ceriond953ebb2005-11-29 13:27:20 +00003511 DIP("lhz r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3512 val = loadBE(Ity_I16, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003513 putIReg( rD_addr, mkSzWiden16(ty, val, False) );
cerionb85e8bb2005-02-16 08:54:33 +00003514 break;
3515
cerion5b2325f2005-12-23 00:55:09 +00003516 case 0x29: // lhzu (Load HW & and Zero, Update, PPC32 p451)
cerion76de5cf2005-11-18 18:25:12 +00003517 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003518 vex_printf("dis_int_load(ppc)(lhzu,rA_addr|rD_addr)\n");
sewardj0e2cc672005-07-29 21:58:51 +00003519 return False;
3520 }
ceriond953ebb2005-11-29 13:27:20 +00003521 DIP("lhzu r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3522 val = loadBE(Ity_I16, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003523 putIReg( rD_addr, mkSzWiden16(ty, val, False) );
ceriond953ebb2005-11-29 13:27:20 +00003524 putIReg( rA_addr, mkexpr(EA) );
sewardj0e2cc672005-07-29 21:58:51 +00003525 break;
cerion645c9302005-01-31 10:09:59 +00003526
cerione9d361a2005-03-04 17:35:29 +00003527 case 0x20: // lwz (Load W & Zero, PPC32 p460)
ceriond953ebb2005-11-29 13:27:20 +00003528 DIP("lwz r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3529 val = loadBE(Ity_I32, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003530 putIReg( rD_addr, mkSzWiden32(ty, val, False) );
cerionb85e8bb2005-02-16 08:54:33 +00003531 break;
3532
cerion5b2325f2005-12-23 00:55:09 +00003533 case 0x21: // lwzu (Load W & Zero, Update, PPC32 p461))
cerion76de5cf2005-11-18 18:25:12 +00003534 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003535 vex_printf("dis_int_load(ppc)(lwzu,rA_addr|rD_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003536 return False;
3537 }
ceriond953ebb2005-11-29 13:27:20 +00003538 DIP("lwzu r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3539 val = loadBE(Ity_I32, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003540 putIReg( rD_addr, mkSzWiden32(ty, val, False) );
ceriond953ebb2005-11-29 13:27:20 +00003541 putIReg( rA_addr, mkexpr(EA) );
cerionb85e8bb2005-02-16 08:54:33 +00003542 break;
3543
3544 /* X Form */
3545 case 0x1F:
3546 if (b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00003547 vex_printf("dis_int_load(ppc)(Ox1F,b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003548 return False;
3549 }
cerion645c9302005-01-31 10:09:59 +00003550
cerionb85e8bb2005-02-16 08:54:33 +00003551 switch (opc2) {
cerion5b2325f2005-12-23 00:55:09 +00003552 case 0x077: // lbzux (Load B & Zero, Update Indexed, PPC32 p435)
ceriond953ebb2005-11-29 13:27:20 +00003553 DIP("lbzux r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
cerion76de5cf2005-11-18 18:25:12 +00003554 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003555 vex_printf("dis_int_load(ppc)(lwzux,rA_addr|rD_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003556 return False;
3557 }
ceriond953ebb2005-11-29 13:27:20 +00003558 val = loadBE(Ity_I8, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003559 putIReg( rD_addr, mkSzWiden8(ty, val, False) );
ceriond953ebb2005-11-29 13:27:20 +00003560 putIReg( rA_addr, mkexpr(EA) );
cerionb85e8bb2005-02-16 08:54:33 +00003561 break;
3562
cerion5b2325f2005-12-23 00:55:09 +00003563 case 0x057: // lbzx (Load B & Zero, Indexed, PPC32 p436)
ceriond953ebb2005-11-29 13:27:20 +00003564 DIP("lbzx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3565 val = loadBE(Ity_I8, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003566 putIReg( rD_addr, mkSzWiden8(ty, val, False) );
cerionb85e8bb2005-02-16 08:54:33 +00003567 break;
3568
cerion5b2325f2005-12-23 00:55:09 +00003569 case 0x177: // lhaux (Load HW Alg, Update Indexed, PPC32 p447)
cerion76de5cf2005-11-18 18:25:12 +00003570 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003571 vex_printf("dis_int_load(ppc)(lhaux,rA_addr|rD_addr)\n");
cerioncb14e732005-09-09 16:38:19 +00003572 return False;
3573 }
ceriond953ebb2005-11-29 13:27:20 +00003574 DIP("lhaux r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3575 val = loadBE(Ity_I16, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003576 putIReg( rD_addr, mkSzWiden16(ty, val, True) );
ceriond953ebb2005-11-29 13:27:20 +00003577 putIReg( rA_addr, mkexpr(EA) );
cerioncb14e732005-09-09 16:38:19 +00003578 break;
cerionb85e8bb2005-02-16 08:54:33 +00003579
cerion5b2325f2005-12-23 00:55:09 +00003580 case 0x157: // lhax (Load HW Alg, Indexed, PPC32 p448)
ceriond953ebb2005-11-29 13:27:20 +00003581 DIP("lhax r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3582 val = loadBE(Ity_I16, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003583 putIReg( rD_addr, mkSzWiden16(ty, val, True) );
cerionb85e8bb2005-02-16 08:54:33 +00003584 break;
3585
cerion5b2325f2005-12-23 00:55:09 +00003586 case 0x137: // lhzux (Load HW & Zero, Update Indexed, PPC32 p452)
cerion76de5cf2005-11-18 18:25:12 +00003587 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003588 vex_printf("dis_int_load(ppc)(lhzux,rA_addr|rD_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003589 return False;
3590 }
ceriond953ebb2005-11-29 13:27:20 +00003591 DIP("lhzux r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3592 val = loadBE(Ity_I16, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003593 putIReg( rD_addr, mkSzWiden16(ty, val, False) );
ceriond953ebb2005-11-29 13:27:20 +00003594 putIReg( rA_addr, mkexpr(EA) );
cerionb85e8bb2005-02-16 08:54:33 +00003595 break;
3596
cerion5b2325f2005-12-23 00:55:09 +00003597 case 0x117: // lhzx (Load HW & Zero, Indexed, PPC32 p453)
ceriond953ebb2005-11-29 13:27:20 +00003598 DIP("lhzx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3599 val = loadBE(Ity_I16, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003600 putIReg( rD_addr, mkSzWiden16(ty, val, False) );
cerionb85e8bb2005-02-16 08:54:33 +00003601 break;
cerion44997f22005-01-31 18:45:59 +00003602
cerion5b2325f2005-12-23 00:55:09 +00003603 case 0x037: // lwzux (Load W & Zero, Update Indexed, PPC32 p462)
cerion76de5cf2005-11-18 18:25:12 +00003604 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003605 vex_printf("dis_int_load(ppc)(lwzux,rA_addr|rD_addr)\n");
sewardj7787af42005-08-04 18:32:19 +00003606 return False;
3607 }
ceriond953ebb2005-11-29 13:27:20 +00003608 DIP("lwzux r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3609 val = loadBE(Ity_I32, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003610 putIReg( rD_addr, mkSzWiden32(ty, val, False) );
ceriond953ebb2005-11-29 13:27:20 +00003611 putIReg( rA_addr, mkexpr(EA) );
sewardj7787af42005-08-04 18:32:19 +00003612 break;
cerionb85e8bb2005-02-16 08:54:33 +00003613
cerion5b2325f2005-12-23 00:55:09 +00003614 case 0x017: // lwzx (Load W & Zero, Indexed, PPC32 p463)
ceriond953ebb2005-11-29 13:27:20 +00003615 DIP("lwzx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3616 val = loadBE(Ity_I32, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003617 putIReg( rD_addr, mkSzWiden32(ty, val, False) );
cerionb85e8bb2005-02-16 08:54:33 +00003618 break;
cerion44997f22005-01-31 18:45:59 +00003619
cerionf0de28c2005-12-13 20:21:11 +00003620
3621 /* 64bit Loads */
cerion5b2325f2005-12-23 00:55:09 +00003622 case 0x035: // ldux (Load DWord, Update Indexed, PPC64 p475)
cerionf0de28c2005-12-13 20:21:11 +00003623 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003624 vex_printf("dis_int_load(ppc)(ldux,rA_addr|rD_addr)\n");
cerionf0de28c2005-12-13 20:21:11 +00003625 return False;
3626 }
3627 DIP("ldux r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3628 putIReg( rD_addr, loadBE(Ity_I64, mkexpr(EA)) );
3629 putIReg( rA_addr, mkexpr(EA) );
3630 break;
3631
cerion5b2325f2005-12-23 00:55:09 +00003632 case 0x015: // ldx (Load DWord, Indexed, PPC64 p476)
cerionf0de28c2005-12-13 20:21:11 +00003633 DIP("ldx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3634 putIReg( rD_addr, loadBE(Ity_I64, mkexpr(EA)) );
3635 break;
3636
cerion5b2325f2005-12-23 00:55:09 +00003637 case 0x175: // lwaux (Load W Alg, Update Indexed, PPC64 p501)
cerionf0de28c2005-12-13 20:21:11 +00003638 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003639 vex_printf("dis_int_load(ppc)(lwaux,rA_addr|rD_addr)\n");
cerionf0de28c2005-12-13 20:21:11 +00003640 return False;
3641 }
3642 DIP("lwaux r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
cerion5b2325f2005-12-23 00:55:09 +00003643 putIReg( rD_addr,
3644 unop(Iop_32Sto64, loadBE(Ity_I32, mkexpr(EA))) );
cerionf0de28c2005-12-13 20:21:11 +00003645 putIReg( rA_addr, mkexpr(EA) );
3646 break;
3647
cerion5b2325f2005-12-23 00:55:09 +00003648 case 0x155: // lwax (Load W Alg, Indexed, PPC64 p502)
cerionf0de28c2005-12-13 20:21:11 +00003649 DIP("lwax r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
cerion5b2325f2005-12-23 00:55:09 +00003650 putIReg( rD_addr,
3651 unop(Iop_32Sto64, loadBE(Ity_I32, mkexpr(EA))) );
cerionf0de28c2005-12-13 20:21:11 +00003652 break;
3653
cerionb85e8bb2005-02-16 08:54:33 +00003654 default:
cerion5b2325f2005-12-23 00:55:09 +00003655 vex_printf("dis_int_load(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003656 return False;
3657 }
3658 break;
cerionf0de28c2005-12-13 20:21:11 +00003659
3660 /* DS Form - 64bit Loads */
3661 case 0x3A:
3662 switch (b1<<1 | b0) {
cerion5b2325f2005-12-23 00:55:09 +00003663 case 0x0: // ld (Load DWord, PPC64 p472)
cerionf0de28c2005-12-13 20:21:11 +00003664 DIP("ld r%u,%d(r%u)\n", rD_addr, simm16, rA_addr);
3665 putIReg( rD_addr, loadBE(Ity_I64, mkexpr(EA)) );
3666 break;
3667
cerion5b2325f2005-12-23 00:55:09 +00003668 case 0x1: // ldu (Load DWord, Update, PPC64 p474)
cerionf0de28c2005-12-13 20:21:11 +00003669 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003670 vex_printf("dis_int_load(ppc)(ldu,rA_addr|rD_addr)\n");
cerionf0de28c2005-12-13 20:21:11 +00003671 return False;
3672 }
3673 DIP("ldu r%u,%d(r%u)\n", rD_addr, simm16, rA_addr);
3674 simm16 = simm16 & ~0x3;
3675 putIReg( rD_addr, loadBE(Ity_I64, mkexpr(EA)) );
3676 putIReg( rA_addr, mkexpr(EA) );
3677 break;
3678
cerion5b2325f2005-12-23 00:55:09 +00003679 case 0x2: // lwa (Load Word Alg, PPC64 p499)
cerionf0de28c2005-12-13 20:21:11 +00003680 DIP("lwa r%u,%d(r%u)\n", rD_addr, simm16, rA_addr);
cerion5b2325f2005-12-23 00:55:09 +00003681 putIReg( rD_addr,
3682 unop(Iop_32Sto64, loadBE(Ity_I32, mkexpr(EA))) );
cerionf0de28c2005-12-13 20:21:11 +00003683 break;
3684
3685 default:
cerion5b2325f2005-12-23 00:55:09 +00003686 vex_printf("dis_int_load(ppc)(0x3A, opc2)\n");
cerionf0de28c2005-12-13 20:21:11 +00003687 return False;
3688 }
3689 break;
3690
cerionb85e8bb2005-02-16 08:54:33 +00003691 default:
cerion5b2325f2005-12-23 00:55:09 +00003692 vex_printf("dis_int_load(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003693 return False;
3694 }
3695 return True;
cerion7aa4bbc2005-01-29 09:32:07 +00003696}
3697
3698
3699
cerion3d870a32005-03-18 12:23:33 +00003700/*
3701 Integer Store Instructions
3702*/
ceriond23be4e2005-01-31 07:23:07 +00003703static Bool dis_int_store ( UInt theInstr )
3704{
cerionf0de28c2005-12-13 20:21:11 +00003705 /* D-Form, X-Form, DS-Form */
cerionedf7fc52005-11-18 20:57:41 +00003706 UChar opc1 = ifieldOPC(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00003707 UInt rS_addr = ifieldRegDS(theInstr);
3708 UInt rA_addr = ifieldRegA(theInstr);
ceriond953ebb2005-11-29 13:27:20 +00003709 UInt uimm16 = ifieldUIMM16(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00003710 UInt rB_addr = ifieldRegB(theInstr);
3711 UInt opc2 = ifieldOPClo10(theInstr);
cerionf0de28c2005-12-13 20:21:11 +00003712 UChar b1 = ifieldBIT1(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00003713 UChar b0 = ifieldBIT0(theInstr);
3714
ceriond953ebb2005-11-29 13:27:20 +00003715 Int simm16 = extend_s_16to32(uimm16);
3716 IRType ty = mode64 ? Ity_I64 : Ity_I32;
3717 IRTemp rS = newTemp(ty);
3718 IRTemp rB = newTemp(ty);
3719 IRTemp EA = newTemp(ty);
cerionb85e8bb2005-02-16 08:54:33 +00003720
cerion76de5cf2005-11-18 18:25:12 +00003721 assign( rB, getIReg(rB_addr) );
3722 assign( rS, getIReg(rS_addr) );
cerionb85e8bb2005-02-16 08:54:33 +00003723
cerionf0de28c2005-12-13 20:21:11 +00003724 switch (opc1) {
3725 case 0x1F: // register offset
ceriond953ebb2005-11-29 13:27:20 +00003726 assign( EA, ea_rAor0_idxd( rA_addr, rB_addr ) );
cerionf0de28c2005-12-13 20:21:11 +00003727 break;
3728 case 0x3E: // immediate offset: 64bit
3729 simm16 = simm16 & 0xFFFFFFFC;
3730 default: // immediate offset
3731 assign( EA, ea_rAor0_simm( rA_addr, simm16 ) );
3732 break;
ceriond953ebb2005-11-29 13:27:20 +00003733 }
3734
cerionb85e8bb2005-02-16 08:54:33 +00003735 switch (opc1) {
sewardjafe85832005-09-09 10:25:39 +00003736 case 0x26: // stb (Store B, PPC32 p509)
cerion76de5cf2005-11-18 18:25:12 +00003737 DIP("stb r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
cerion2831b002005-11-30 19:55:22 +00003738 storeBE( mkexpr(EA), mkSzNarrow8(ty, mkexpr(rS)) );
sewardjafe85832005-09-09 10:25:39 +00003739 break;
sewardjb51f0f42005-07-18 11:38:02 +00003740
cerion5b2325f2005-12-23 00:55:09 +00003741 case 0x27: // stbu (Store B, Update, PPC32 p510)
cerion76de5cf2005-11-18 18:25:12 +00003742 if (rA_addr == 0 ) {
cerion5b2325f2005-12-23 00:55:09 +00003743 vex_printf("dis_int_store(ppc)(stbu,rA_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003744 return False;
3745 }
cerion76de5cf2005-11-18 18:25:12 +00003746 DIP("stbu r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
ceriond953ebb2005-11-29 13:27:20 +00003747 putIReg( rA_addr, mkexpr(EA) );
cerion2831b002005-11-30 19:55:22 +00003748 storeBE( mkexpr(EA), mkSzNarrow8(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003749 break;
ceriond23be4e2005-01-31 07:23:07 +00003750
cerione9d361a2005-03-04 17:35:29 +00003751 case 0x2C: // sth (Store HW, PPC32 p522)
cerion76de5cf2005-11-18 18:25:12 +00003752 DIP("sth r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
cerion2831b002005-11-30 19:55:22 +00003753 storeBE( mkexpr(EA), mkSzNarrow16(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003754 break;
3755
cerion5b2325f2005-12-23 00:55:09 +00003756 case 0x2D: // sthu (Store HW, Update, PPC32 p524)
cerion76de5cf2005-11-18 18:25:12 +00003757 if (rA_addr == 0) {
cerion5b2325f2005-12-23 00:55:09 +00003758 vex_printf("dis_int_store(ppc)(sthu,rA_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003759 return False;
3760 }
cerion76de5cf2005-11-18 18:25:12 +00003761 DIP("sthu r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
ceriond953ebb2005-11-29 13:27:20 +00003762 putIReg( rA_addr, mkexpr(EA) );
cerion2831b002005-11-30 19:55:22 +00003763 storeBE( mkexpr(EA), mkSzNarrow16(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003764 break;
ceriond23be4e2005-01-31 07:23:07 +00003765
cerione9d361a2005-03-04 17:35:29 +00003766 case 0x24: // stw (Store W, PPC32 p530)
cerion76de5cf2005-11-18 18:25:12 +00003767 DIP("stw r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
cerion2831b002005-11-30 19:55:22 +00003768 storeBE( mkexpr(EA), mkSzNarrow32(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003769 break;
ceriond23be4e2005-01-31 07:23:07 +00003770
cerion5b2325f2005-12-23 00:55:09 +00003771 case 0x25: // stwu (Store W, Update, PPC32 p534)
cerion76de5cf2005-11-18 18:25:12 +00003772 if (rA_addr == 0) {
cerion5b2325f2005-12-23 00:55:09 +00003773 vex_printf("dis_int_store(ppc)(stwu,rA_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003774 return False;
3775 }
cerion76de5cf2005-11-18 18:25:12 +00003776 DIP("stwu r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
ceriond953ebb2005-11-29 13:27:20 +00003777 putIReg( rA_addr, mkexpr(EA) );
cerion2831b002005-11-30 19:55:22 +00003778 storeBE( mkexpr(EA), mkSzNarrow32(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003779 break;
3780
cerionf0de28c2005-12-13 20:21:11 +00003781 /* X Form : all these use EA_indexed */
cerionb85e8bb2005-02-16 08:54:33 +00003782 case 0x1F:
3783 if (b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00003784 vex_printf("dis_int_store(ppc)(0x1F,b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003785 return False;
3786 }
cerion44997f22005-01-31 18:45:59 +00003787
cerionb85e8bb2005-02-16 08:54:33 +00003788 switch (opc2) {
cerion5b2325f2005-12-23 00:55:09 +00003789 case 0x0F7: // stbux (Store B, Update Indexed, PPC32 p511)
cerion76de5cf2005-11-18 18:25:12 +00003790 if (rA_addr == 0) {
cerion5b2325f2005-12-23 00:55:09 +00003791 vex_printf("dis_int_store(ppc)(stbux,rA_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003792 return False;
3793 }
cerion76de5cf2005-11-18 18:25:12 +00003794 DIP("stbux r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
ceriond953ebb2005-11-29 13:27:20 +00003795 putIReg( rA_addr, mkexpr(EA) );
cerion2831b002005-11-30 19:55:22 +00003796 storeBE( mkexpr(EA), mkSzNarrow8(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003797 break;
3798
cerione9d361a2005-03-04 17:35:29 +00003799 case 0x0D7: // stbx (Store B Indexed, PPC32 p512)
cerion76de5cf2005-11-18 18:25:12 +00003800 DIP("stbx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003801 storeBE( mkexpr(EA), mkSzNarrow8(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003802 break;
3803
cerion5b2325f2005-12-23 00:55:09 +00003804 case 0x1B7: // sthux (Store HW, Update Indexed, PPC32 p525)
cerion76de5cf2005-11-18 18:25:12 +00003805 if (rA_addr == 0) {
cerion5b2325f2005-12-23 00:55:09 +00003806 vex_printf("dis_int_store(ppc)(sthux,rA_addr)\n");
cerioncb14e732005-09-09 16:38:19 +00003807 return False;
3808 }
cerion76de5cf2005-11-18 18:25:12 +00003809 DIP("sthux r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
ceriond953ebb2005-11-29 13:27:20 +00003810 putIReg( rA_addr, mkexpr(EA) );
cerion2831b002005-11-30 19:55:22 +00003811 storeBE( mkexpr(EA), mkSzNarrow16(ty, mkexpr(rS)) );
cerioncb14e732005-09-09 16:38:19 +00003812 break;
cerionb85e8bb2005-02-16 08:54:33 +00003813
cerione9d361a2005-03-04 17:35:29 +00003814 case 0x197: // sthx (Store HW Indexed, PPC32 p526)
cerion76de5cf2005-11-18 18:25:12 +00003815 DIP("sthx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003816 storeBE( mkexpr(EA), mkSzNarrow16(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003817 break;
3818
cerion5b2325f2005-12-23 00:55:09 +00003819 case 0x0B7: // stwux (Store W, Update Indexed, PPC32 p535)
cerion76de5cf2005-11-18 18:25:12 +00003820 if (rA_addr == 0) {
cerion5b2325f2005-12-23 00:55:09 +00003821 vex_printf("dis_int_store(ppc)(stwux,rA_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003822 return False;
3823 }
cerion76de5cf2005-11-18 18:25:12 +00003824 DIP("stwux r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
ceriond953ebb2005-11-29 13:27:20 +00003825 putIReg( rA_addr, mkexpr(EA) );
cerion2831b002005-11-30 19:55:22 +00003826 storeBE( mkexpr(EA), mkSzNarrow32(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003827 break;
cerion44997f22005-01-31 18:45:59 +00003828
cerione9d361a2005-03-04 17:35:29 +00003829 case 0x097: // stwx (Store W Indexed, PPC32 p536)
cerion76de5cf2005-11-18 18:25:12 +00003830 DIP("stwx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003831 storeBE( mkexpr(EA), mkSzNarrow32(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003832 break;
3833
cerionf0de28c2005-12-13 20:21:11 +00003834
3835 /* 64bit Stores */
cerion5b2325f2005-12-23 00:55:09 +00003836 case 0x0B5: // stdux (Store DWord, Update Indexed, PPC64 p584)
cerionf0de28c2005-12-13 20:21:11 +00003837 if (rA_addr == 0) {
cerion5b2325f2005-12-23 00:55:09 +00003838 vex_printf("dis_int_store(ppc)(stdux,rA_addr)\n");
cerionf0de28c2005-12-13 20:21:11 +00003839 return False;
3840 }
3841 DIP("stdux r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
3842 putIReg( rA_addr, mkexpr(EA) );
3843 storeBE( mkexpr(EA), mkexpr(rS) );
3844 break;
3845
cerion5b2325f2005-12-23 00:55:09 +00003846 case 0x095: // stdx (Store DWord Indexed, PPC64 p585)
cerionf0de28c2005-12-13 20:21:11 +00003847 DIP("stdx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
3848 storeBE( mkexpr(EA), mkexpr(rS) );
3849 break;
3850
cerionb85e8bb2005-02-16 08:54:33 +00003851 default:
cerion5b2325f2005-12-23 00:55:09 +00003852 vex_printf("dis_int_store(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003853 return False;
3854 }
3855 break;
cerionf0de28c2005-12-13 20:21:11 +00003856
3857 /* DS Form - 64bit Stores */
3858 case 0x3E:
3859 switch (b1<<1 | b0) {
cerion5b2325f2005-12-23 00:55:09 +00003860 case 0x0: // std (Store DWord, PPC64 p580)
cerionf0de28c2005-12-13 20:21:11 +00003861 DIP("std r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
3862 storeBE( mkexpr(EA), mkexpr(rS) );
3863 break;
3864
cerion5b2325f2005-12-23 00:55:09 +00003865 case 0x1: // stdu (Store DWord, Update, PPC64 p583)
cerionf0de28c2005-12-13 20:21:11 +00003866 DIP("stdu r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
3867 putIReg( rA_addr, mkexpr(EA) );
3868 storeBE( mkexpr(EA), mkexpr(rS) );
3869 break;
3870
3871 default:
cerion5b2325f2005-12-23 00:55:09 +00003872 vex_printf("dis_int_load(ppc)(0x3A, opc2)\n");
cerionf0de28c2005-12-13 20:21:11 +00003873 return False;
3874 }
3875 break;
3876
cerionb85e8bb2005-02-16 08:54:33 +00003877 default:
cerion5b2325f2005-12-23 00:55:09 +00003878 vex_printf("dis_int_store(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003879 return False;
3880 }
3881 return True;
ceriond23be4e2005-01-31 07:23:07 +00003882}
3883
3884
3885
sewardj7787af42005-08-04 18:32:19 +00003886/*
3887 Integer Load/Store Multiple Instructions
3888*/
3889static Bool dis_int_ldst_mult ( UInt theInstr )
3890{
3891 /* D-Form */
cerion76de5cf2005-11-18 18:25:12 +00003892 UChar opc1 = ifieldOPC(theInstr);
3893 UChar rD_addr = ifieldRegDS(theInstr);
3894 UChar rS_addr = rD_addr;
3895 UChar rA_addr = ifieldRegA(theInstr);
cerion2831b002005-11-30 19:55:22 +00003896 UInt uimm16 = ifieldUIMM16(theInstr);
cerionedf7fc52005-11-18 20:57:41 +00003897
ceriond953ebb2005-11-29 13:27:20 +00003898 Int simm16 = extend_s_16to32(uimm16);
3899 IRType ty = mode64 ? Ity_I64 : Ity_I32;
3900 IRTemp EA = newTemp(ty);
3901 UInt r = 0;
3902 UInt ea_off = 0;
sewardj7787af42005-08-04 18:32:19 +00003903 IRExpr* irx_addr;
cerionedf7fc52005-11-18 20:57:41 +00003904
ceriond953ebb2005-11-29 13:27:20 +00003905 assign( EA, ea_rAor0_simm( rA_addr, simm16 ) );
3906
sewardj7787af42005-08-04 18:32:19 +00003907 switch (opc1) {
ceriond953ebb2005-11-29 13:27:20 +00003908 case 0x2E: // lmw (Load Multiple Word, PPC32 p454)
3909 if (rA_addr >= rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003910 vex_printf("dis_int_ldst_mult(ppc)(lmw,rA_addr)\n");
sewardj7787af42005-08-04 18:32:19 +00003911 return False;
ceriond953ebb2005-11-29 13:27:20 +00003912 }
3913 DIP("lmw r%u,%d(r%u)\n", rD_addr, simm16, rA_addr);
3914 for (r = rD_addr; r <= 31; r++) {
3915 irx_addr = binop(Iop_Add32, mkexpr(EA), mkU32(ea_off));
cerion5b2325f2005-12-23 00:55:09 +00003916 putIReg( r, mkSzWiden32(ty, loadBE(Ity_I32, irx_addr ),
3917 False) );
ceriond953ebb2005-11-29 13:27:20 +00003918 ea_off += 4;
3919 }
3920 break;
3921
3922 case 0x2F: // stmw (Store Multiple Word, PPC32 p527)
3923 DIP("stmw r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
3924 for (r = rS_addr; r <= 31; r++) {
3925 irx_addr = binop(Iop_Add32, mkexpr(EA), mkU32(ea_off));
cerion2831b002005-11-30 19:55:22 +00003926 storeBE( irx_addr, mkSzNarrow32(ty, getIReg(r)) );
ceriond953ebb2005-11-29 13:27:20 +00003927 ea_off += 4;
3928 }
3929 break;
3930
3931 default:
cerion5b2325f2005-12-23 00:55:09 +00003932 vex_printf("dis_int_ldst_mult(ppc)(opc1)\n");
ceriond953ebb2005-11-29 13:27:20 +00003933 return False;
sewardj7787af42005-08-04 18:32:19 +00003934 }
3935 return True;
3936}
3937
3938
3939
sewardj87e651f2005-09-09 08:31:18 +00003940/*
3941 Integer Load/Store String Instructions
3942*/
3943static
3944void generate_lsw_sequence ( IRTemp tNBytes, // # bytes, :: Ity_I32
3945 IRTemp EA, // EA
3946 Int rD, // first dst register
ceriond953ebb2005-11-29 13:27:20 +00003947 Int maxBytes ) // 32 or 128
sewardj87e651f2005-09-09 08:31:18 +00003948{
3949 Int i, shift = 24;
3950 IRExpr* e_nbytes = mkexpr(tNBytes);
ceriond953ebb2005-11-29 13:27:20 +00003951 IRExpr* e_EA = mkexpr(EA);
3952 IRType ty = mode64 ? Ity_I64 : Ity_I32;
sewardj87e651f2005-09-09 08:31:18 +00003953
sewardj5876fa12005-09-09 09:35:29 +00003954 vassert(rD >= 0 && rD < 32);
sewardj87e651f2005-09-09 08:31:18 +00003955 rD--; if (rD < 0) rD = 31;
3956
3957 for (i = 0; i < maxBytes; i++) {
sewardj87e651f2005-09-09 08:31:18 +00003958 /* if (nBytes < (i+1)) goto NIA; */
3959 stmt( IRStmt_Exit( binop(Iop_CmpLT32U, e_nbytes, mkU32(i+1)),
3960 Ijk_Boring,
cerion2831b002005-11-30 19:55:22 +00003961 mkSzConst( ty, nextInsnAddr()) ));
sewardj87e651f2005-09-09 08:31:18 +00003962 /* when crossing into a new dest register, set it to zero. */
3963 if ((i % 4) == 0) {
3964 rD++; if (rD == 32) rD = 0;
cerion2831b002005-11-30 19:55:22 +00003965 putIReg(rD, mkSzImm(ty, 0));
sewardj87e651f2005-09-09 08:31:18 +00003966 shift = 24;
3967 }
3968 /* rD |= (8Uto32(*(EA+i))) << shift */
3969 vassert(shift == 0 || shift == 8 || shift == 16 || shift == 24);
sewardj2ef8a372006-01-28 17:07:19 +00003970 putIReg(
3971 rD,
3972 mkSzWiden32(
3973 ty,
3974 binop(
3975 Iop_Or32,
3976 mkSzNarrow32(ty, getIReg(rD)),
3977 binop(
3978 Iop_Shl32,
3979 unop(
3980 Iop_8Uto32,
3981 loadBE(Ity_I8,
3982 binop(mkSzOp(ty,Iop_Add8), e_EA, mkSzImm(ty,i)))
3983 ),
3984 mkU8(toUChar(shift))
3985 )
3986 ),
3987 /*Signed*/False
3988 )
3989 );
sewardj87e651f2005-09-09 08:31:18 +00003990 shift -= 8;
3991 }
3992}
3993
sewardj5876fa12005-09-09 09:35:29 +00003994static
3995void generate_stsw_sequence ( IRTemp tNBytes, // # bytes, :: Ity_I32
3996 IRTemp EA, // EA
3997 Int rS, // first src register
ceriond953ebb2005-11-29 13:27:20 +00003998 Int maxBytes ) // 32 or 128
sewardj5876fa12005-09-09 09:35:29 +00003999{
4000 Int i, shift = 24;
4001 IRExpr* e_nbytes = mkexpr(tNBytes);
ceriond953ebb2005-11-29 13:27:20 +00004002 IRExpr* e_EA = mkexpr(EA);
4003 IRType ty = mode64 ? Ity_I64 : Ity_I32;
sewardj5876fa12005-09-09 09:35:29 +00004004
4005 vassert(rS >= 0 && rS < 32);
4006 rS--; if (rS < 0) rS = 31;
4007
4008 for (i = 0; i < maxBytes; i++) {
4009 /* if (nBytes < (i+1)) goto NIA; */
4010 stmt( IRStmt_Exit( binop(Iop_CmpLT32U, e_nbytes, mkU32(i+1)),
4011 Ijk_Boring,
cerion2831b002005-11-30 19:55:22 +00004012 mkSzConst( ty, nextInsnAddr() ) ));
sewardj5876fa12005-09-09 09:35:29 +00004013 /* check for crossing into a new src register. */
4014 if ((i % 4) == 0) {
4015 rS++; if (rS == 32) rS = 0;
4016 shift = 24;
4017 }
4018 /* *(EA+i) = 32to8(rS >> shift) */
4019 vassert(shift == 0 || shift == 8 || shift == 16 || shift == 24);
4020 storeBE(
cerion2831b002005-11-30 19:55:22 +00004021 binop(mkSzOp(ty,Iop_Add8), e_EA, mkSzImm(ty,i)),
sewardj5876fa12005-09-09 09:35:29 +00004022 unop(Iop_32to8,
cerion2831b002005-11-30 19:55:22 +00004023 binop(Iop_Shr32,
4024 mkSzNarrow32(ty, getIReg(rS)),
4025 mkU8(toUChar(shift))))
sewardj5876fa12005-09-09 09:35:29 +00004026 );
4027 shift -= 8;
4028 }
4029}
4030
sewardj87e651f2005-09-09 08:31:18 +00004031static Bool dis_int_ldst_str ( UInt theInstr, /*OUT*/Bool* stopHere )
4032{
4033 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00004034 UChar opc1 = ifieldOPC(theInstr);
4035 UChar rD_addr = ifieldRegDS(theInstr);
4036 UChar rS_addr = rD_addr;
4037 UChar rA_addr = ifieldRegA(theInstr);
4038 UChar rB_addr = ifieldRegB(theInstr);
4039 UChar NumBytes = rB_addr;
4040 UInt opc2 = ifieldOPClo10(theInstr);
4041 UChar b0 = ifieldBIT0(theInstr);
cerionedf7fc52005-11-18 20:57:41 +00004042
ceriond953ebb2005-11-29 13:27:20 +00004043 IRType ty = mode64 ? Ity_I64 : Ity_I32;
4044 IRTemp t_EA = newTemp(ty);
sewardj87e651f2005-09-09 08:31:18 +00004045 IRTemp t_nbytes = IRTemp_INVALID;
cerionedf7fc52005-11-18 20:57:41 +00004046
sewardj87e651f2005-09-09 08:31:18 +00004047 *stopHere = False;
cerionedf7fc52005-11-18 20:57:41 +00004048
sewardj87e651f2005-09-09 08:31:18 +00004049 if (opc1 != 0x1F || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00004050 vex_printf("dis_int_ldst_str(ppc)(opc1)\n");
sewardj87e651f2005-09-09 08:31:18 +00004051 return False;
4052 }
4053
4054 switch (opc2) {
4055 case 0x255: // lswi (Load String Word Immediate, PPC32 p455)
4056 /* NB: does not reject the case where RA is in the range of
4057 registers to be loaded. It should. */
ceriond953ebb2005-11-29 13:27:20 +00004058 DIP("lswi r%u,r%u,%d\n", rD_addr, rA_addr, NumBytes);
4059 assign( t_EA, ea_rAor0(rA_addr) );
sewardj2ef8a372006-01-28 17:07:19 +00004060 if (NumBytes == 8 && !mode64) {
sewardj87e651f2005-09-09 08:31:18 +00004061 /* Special case hack */
cerion76de5cf2005-11-18 18:25:12 +00004062 /* rD = Mem[EA]; (rD+1)%32 = Mem[EA+4] */
4063 putIReg( rD_addr,
sewardj87e651f2005-09-09 08:31:18 +00004064 loadBE(Ity_I32, mkexpr(t_EA)) );
cerion76de5cf2005-11-18 18:25:12 +00004065 putIReg( (rD_addr+1) % 32,
ceriond953ebb2005-11-29 13:27:20 +00004066 loadBE(Ity_I32,
4067 binop(Iop_Add32, mkexpr(t_EA), mkU32(4))) );
sewardj87e651f2005-09-09 08:31:18 +00004068 } else {
4069 t_nbytes = newTemp(Ity_I32);
cerion76de5cf2005-11-18 18:25:12 +00004070 assign( t_nbytes, mkU32(NumBytes==0 ? 32 : NumBytes) );
ceriond953ebb2005-11-29 13:27:20 +00004071 generate_lsw_sequence( t_nbytes, t_EA, rD_addr, 32 );
sewardj87e651f2005-09-09 08:31:18 +00004072 *stopHere = True;
4073 }
4074 return True;
4075
4076 case 0x215: // lswx (Load String Word Indexed, PPC32 p456)
4077 /* NB: does not reject the case where RA is in the range of
4078 registers to be loaded. It should. Although considering
4079 that that can only be detected at run time, it's not easy to
4080 do so. */
cerion76de5cf2005-11-18 18:25:12 +00004081 if (rD_addr == rA_addr || rD_addr == rB_addr)
sewardj87e651f2005-09-09 08:31:18 +00004082 return False;
cerion76de5cf2005-11-18 18:25:12 +00004083 if (rD_addr == 0 && rA_addr == 0)
sewardj87e651f2005-09-09 08:31:18 +00004084 return False;
ceriond953ebb2005-11-29 13:27:20 +00004085 DIP("lswx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
sewardj87e651f2005-09-09 08:31:18 +00004086 t_nbytes = newTemp(Ity_I32);
ceriond953ebb2005-11-29 13:27:20 +00004087 assign( t_EA, ea_rAor0_idxd(rA_addr,rB_addr) );
cerionedf7fc52005-11-18 20:57:41 +00004088 assign( t_nbytes, unop( Iop_8Uto32, getXER_BC() ) );
ceriond953ebb2005-11-29 13:27:20 +00004089 generate_lsw_sequence( t_nbytes, t_EA, rD_addr, 128 );
sewardj87e651f2005-09-09 08:31:18 +00004090 *stopHere = True;
4091 return True;
4092
sewardj5876fa12005-09-09 09:35:29 +00004093 case 0x2D5: // stswi (Store String Word Immediate, PPC32 p528)
ceriond953ebb2005-11-29 13:27:20 +00004094 DIP("stswi r%u,r%u,%d\n", rS_addr, rA_addr, NumBytes);
4095 assign( t_EA, ea_rAor0(rA_addr) );
sewardj2ef8a372006-01-28 17:07:19 +00004096 if (NumBytes == 8 && !mode64) {
sewardj5876fa12005-09-09 09:35:29 +00004097 /* Special case hack */
cerion76de5cf2005-11-18 18:25:12 +00004098 /* Mem[EA] = rD; Mem[EA+4] = (rD+1)%32 */
4099 storeBE( mkexpr(t_EA),
4100 getIReg(rD_addr) );
4101 storeBE( binop(Iop_Add32, mkexpr(t_EA), mkU32(4)),
4102 getIReg((rD_addr+1) % 32) );
sewardj5876fa12005-09-09 09:35:29 +00004103 } else {
4104 t_nbytes = newTemp(Ity_I32);
cerion76de5cf2005-11-18 18:25:12 +00004105 assign( t_nbytes, mkU32(NumBytes==0 ? 32 : NumBytes) );
ceriond953ebb2005-11-29 13:27:20 +00004106 generate_stsw_sequence( t_nbytes, t_EA, rD_addr, 32 );
sewardj5876fa12005-09-09 09:35:29 +00004107 *stopHere = True;
4108 }
4109 return True;
4110
sewardj5876fa12005-09-09 09:35:29 +00004111 case 0x295: // stswx (Store String Word Indexed, PPC32 p529)
ceriond953ebb2005-11-29 13:27:20 +00004112 DIP("stswx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
sewardj5876fa12005-09-09 09:35:29 +00004113 t_nbytes = newTemp(Ity_I32);
ceriond953ebb2005-11-29 13:27:20 +00004114 assign( t_EA, ea_rAor0_idxd(rA_addr,rB_addr) );
cerionedf7fc52005-11-18 20:57:41 +00004115 assign( t_nbytes, unop( Iop_8Uto32, getXER_BC() ) );
ceriond953ebb2005-11-29 13:27:20 +00004116 generate_stsw_sequence( t_nbytes, t_EA, rS_addr, 128 );
sewardj5876fa12005-09-09 09:35:29 +00004117 *stopHere = True;
4118 return True;
sewardj87e651f2005-09-09 08:31:18 +00004119
4120 default:
cerion5b2325f2005-12-23 00:55:09 +00004121 vex_printf("dis_int_ldst_str(ppc)(opc2)\n");
sewardj87e651f2005-09-09 08:31:18 +00004122 return False;
4123 }
4124 return True;
4125}
4126
cerion094d1392005-06-20 13:45:57 +00004127
sewardjb51f0f42005-07-18 11:38:02 +00004128/* ------------------------------------------------------------------
4129 Integer Branch Instructions
4130 ------------------------------------------------------------------ */
cerion645c9302005-01-31 10:09:59 +00004131
cerion45552a92005-02-03 18:20:22 +00004132/*
4133 Branch helper function
4134 ok = BO[2] | ((CTR[0] != 0) ^ BO[1])
sewardjb51f0f42005-07-18 11:38:02 +00004135 Returns an I32 which is 0x00000000 if the ctr condition failed
4136 and 0xFFFFFFFF otherwise.
cerion45552a92005-02-03 18:20:22 +00004137*/
sewardjb51f0f42005-07-18 11:38:02 +00004138static IRExpr* /* :: Ity_I32 */ branch_ctr_ok( UInt BO )
cerion45552a92005-02-03 18:20:22 +00004139{
ceriond953ebb2005-11-29 13:27:20 +00004140 IRType ty = mode64 ? Ity_I64 : Ity_I32;
sewardjb51f0f42005-07-18 11:38:02 +00004141 IRTemp ok = newTemp(Ity_I32);
cerioned623db2005-06-20 12:42:04 +00004142
cerionf0de28c2005-12-13 20:21:11 +00004143 if ((BO >> 2) & 1) { // independent of ctr
sewardjb51f0f42005-07-18 11:38:02 +00004144 assign( ok, mkU32(0xFFFFFFFF) );
cerionb85e8bb2005-02-16 08:54:33 +00004145 } else {
cerionf0de28c2005-12-13 20:21:11 +00004146 if ((BO >> 1) & 1) { // ctr == 0 ?
ceriond953ebb2005-11-29 13:27:20 +00004147 assign( ok, unop( Iop_1Sto32,
cerion4e2c2b32006-01-02 13:35:51 +00004148 binop( mkSzOp(ty, Iop_CmpEQ8),
4149 getGST( PPC_GST_CTR ),
4150 mkSzImm(ty,0))) );
cerionf0de28c2005-12-13 20:21:11 +00004151 } else { // ctr != 0 ?
sewardjb51f0f42005-07-18 11:38:02 +00004152 assign( ok, unop( Iop_1Sto32,
cerion4e2c2b32006-01-02 13:35:51 +00004153 binop( mkSzOp(ty, Iop_CmpNE8),
4154 getGST( PPC_GST_CTR ),
4155 mkSzImm(ty,0))) );
cerionb85e8bb2005-02-16 08:54:33 +00004156 }
4157 }
4158 return mkexpr(ok);
cerion45552a92005-02-03 18:20:22 +00004159}
4160
sewardjb51f0f42005-07-18 11:38:02 +00004161
cerion45552a92005-02-03 18:20:22 +00004162/*
sewardjb51f0f42005-07-18 11:38:02 +00004163 Branch helper function cond_ok = BO[4] | (CR[BI] == BO[3])
4164 Returns an I32 which is either 0 if the condition failed or
4165 some arbitrary nonzero value otherwise. */
4166
4167static IRExpr* /* :: Ity_I32 */ branch_cond_ok( UInt BO, UInt BI )
cerion45552a92005-02-03 18:20:22 +00004168{
sewardjb51f0f42005-07-18 11:38:02 +00004169 Int where;
4170 IRTemp res = newTemp(Ity_I32);
cerionb85e8bb2005-02-16 08:54:33 +00004171 IRTemp cr_bi = newTemp(Ity_I32);
4172
sewardjb51f0f42005-07-18 11:38:02 +00004173 if ((BO >> 4) & 1) {
4174 assign( res, mkU32(1) );
cerionb85e8bb2005-02-16 08:54:33 +00004175 } else {
sewardjb51f0f42005-07-18 11:38:02 +00004176 // ok = (CR[BI] == BO[3]) Note, the following relies on
4177 // getCRbit_anywhere returning a value which
4178 // is either zero or has exactly 1 bit set.
4179 assign( cr_bi, getCRbit_anywhere( BI, &where ) );
cerione9d361a2005-03-04 17:35:29 +00004180
4181 if ((BO >> 3) & 1) {
sewardjb51f0f42005-07-18 11:38:02 +00004182 /* We can use cr_bi as-is. */
4183 assign( res, mkexpr(cr_bi) );
cerione9d361a2005-03-04 17:35:29 +00004184 } else {
sewardjb51f0f42005-07-18 11:38:02 +00004185 /* We have to invert the sense of the information held in
4186 cr_bi. For that we need to know which bit
cerion76de5cf2005-11-18 18:25:12 +00004187 getCRbit_anywhere regards as significant. */
cerion5b2325f2005-12-23 00:55:09 +00004188 assign( res, binop(Iop_Xor32, mkexpr(cr_bi),
4189 mkU32(1<<where)) );
cerionb85e8bb2005-02-16 08:54:33 +00004190 }
4191 }
sewardjb51f0f42005-07-18 11:38:02 +00004192 return mkexpr(res);
cerion45552a92005-02-03 18:20:22 +00004193}
4194
4195
cerion3d870a32005-03-18 12:23:33 +00004196/*
4197 Integer Branch Instructions
4198*/
sewardj9d540e52005-10-08 11:28:16 +00004199static Bool dis_branch ( UInt theInstr,
4200 /*OUT*/DisResult* dres,
sewardjc716aea2006-01-17 01:48:46 +00004201 Bool (*resteerOkFn)(void*,Addr64),
4202 void* callback_opaque )
cerion91ad5362005-01-27 23:02:41 +00004203{
ceriond953ebb2005-11-29 13:27:20 +00004204 UChar opc1 = ifieldOPC(theInstr);
4205 UChar BO = ifieldRegDS(theInstr);
4206 UChar BI = ifieldRegA(theInstr);
4207 UInt BD_u16 = ifieldUIMM16(theInstr) & 0xFFFFFFFC; /* mask off */
4208 UChar b11to15 = ifieldRegB(theInstr);
4209 UInt opc2 = ifieldOPClo10(theInstr);
4210 UInt LI_u26 = ifieldUIMM26(theInstr) & 0xFFFFFFFC; /* mask off */
4211 UChar flag_AA = ifieldBIT1(theInstr);
4212 UChar flag_LK = ifieldBIT0(theInstr);
4213
cerion2831b002005-11-30 19:55:22 +00004214 IRType ty = mode64 ? Ity_I64 : Ity_I32;
ceriond953ebb2005-11-29 13:27:20 +00004215 Addr64 tgt = 0;
4216 Int BD = extend_s_16to32(BD_u16);
cerion2831b002005-11-30 19:55:22 +00004217 IRTemp do_branch = newTemp(Ity_I32);
4218 IRTemp ctr_ok = newTemp(Ity_I32);
4219 IRTemp cond_ok = newTemp(Ity_I32);
4220 IRExpr* e_nia = mkSzImm(ty, nextInsnAddr());
4221 IRConst* c_nia = mkSzConst(ty, nextInsnAddr());
sewardjdf07b882005-11-29 18:19:11 +00004222 IRTemp lr_old = newTemp(ty);
ceriond953ebb2005-11-29 13:27:20 +00004223
cerionb85e8bb2005-02-16 08:54:33 +00004224 /* Hack to pass through code that just wants to read the PC */
4225 if (theInstr == 0x429F0005) {
sewardjb51f0f42005-07-18 11:38:02 +00004226 DIP("bcl 0x%x, 0x%x (a.k.a mr lr,cia+4)\n", BO, BI);
ceriond953ebb2005-11-29 13:27:20 +00004227 putGST( PPC_GST_LR, e_nia );
cerionb85e8bb2005-02-16 08:54:33 +00004228 return True;
sewardjb51f0f42005-07-18 11:38:02 +00004229 }
sewardj9d540e52005-10-08 11:28:16 +00004230
4231 /* The default what-next. Individual cases can override it. */
4232 dres->whatNext = Dis_StopHere;
4233
cerionb85e8bb2005-02-16 08:54:33 +00004234 switch (opc1) {
cerione9d361a2005-03-04 17:35:29 +00004235 case 0x12: // b (Branch, PPC32 p360)
cerion4561acb2005-02-21 14:07:48 +00004236 if (flag_AA) {
cerion2831b002005-11-30 19:55:22 +00004237 tgt = mkSzAddr( ty, extend_s_26to64(LI_u26) );
cerion4561acb2005-02-21 14:07:48 +00004238 } else {
cerion2831b002005-11-30 19:55:22 +00004239 tgt = mkSzAddr( ty, guest_CIA_curr_instr +
4240 (Long)extend_s_26to64(LI_u26) );
cerionb85e8bb2005-02-16 08:54:33 +00004241 }
ceriond953ebb2005-11-29 13:27:20 +00004242 if (mode64) {
4243 DIP("b%s%s 0x%llx\n",
4244 flag_LK ? "l" : "", flag_AA ? "a" : "", tgt);
4245 } else {
4246 DIP("b%s%s 0x%x\n",
4247 flag_LK ? "l" : "", flag_AA ? "a" : "", (Addr32)tgt);
sewardj9d540e52005-10-08 11:28:16 +00004248 }
4249
sewardjcf8986c2006-01-18 04:14:52 +00004250 if (flag_LK) {
ceriond953ebb2005-11-29 13:27:20 +00004251 putGST( PPC_GST_LR, e_nia );
sewardjcf8986c2006-01-18 04:14:52 +00004252 if (mode64)
4253 make_redzone_AbiHint( "branch-and-link (unconditional call)" );
4254 }
ceriond953ebb2005-11-29 13:27:20 +00004255
sewardjc716aea2006-01-17 01:48:46 +00004256 if (resteerOkFn( callback_opaque, tgt )) {
ceriond953ebb2005-11-29 13:27:20 +00004257 dres->whatNext = Dis_Resteer;
4258 dres->continueAt = tgt;
sewardj9d540e52005-10-08 11:28:16 +00004259 } else {
4260 irbb->jumpkind = flag_LK ? Ijk_Call : Ijk_Boring;
cerion2831b002005-11-30 19:55:22 +00004261 irbb->next = mkSzImm(ty, tgt);
sewardj9d540e52005-10-08 11:28:16 +00004262 }
cerionb85e8bb2005-02-16 08:54:33 +00004263 break;
4264
cerione9d361a2005-03-04 17:35:29 +00004265 case 0x10: // bc (Branch Conditional, PPC32 p361)
cerionb85e8bb2005-02-16 08:54:33 +00004266 DIP("bc%s%s 0x%x, 0x%x, 0x%x\n",
ceriond953ebb2005-11-29 13:27:20 +00004267 flag_LK ? "l" : "", flag_AA ? "a" : "", BO, BI, BD);
cerionb85e8bb2005-02-16 08:54:33 +00004268
4269 if (!(BO & 0x4)) {
ceriond953ebb2005-11-29 13:27:20 +00004270 putGST( PPC_GST_CTR,
cerion2831b002005-11-30 19:55:22 +00004271 binop(mkSzOp(ty, Iop_Sub8),
4272 getGST( PPC_GST_CTR ), mkSzImm(ty, 1)) );
cerionb85e8bb2005-02-16 08:54:33 +00004273 }
sewardjb51f0f42005-07-18 11:38:02 +00004274
4275 /* This is a bit subtle. ctr_ok is either all 0s or all 1s.
cerion76de5cf2005-11-18 18:25:12 +00004276 cond_ok is either zero or nonzero, since that's the cheapest
4277 way to compute it. Anding them together gives a value which
4278 is either zero or non zero and so that's what we must test
4279 for in the IRStmt_Exit. */
sewardjb51f0f42005-07-18 11:38:02 +00004280 assign( ctr_ok, branch_ctr_ok( BO ) );
cerionb85e8bb2005-02-16 08:54:33 +00004281 assign( cond_ok, branch_cond_ok( BO, BI ) );
sewardjb51f0f42005-07-18 11:38:02 +00004282 assign( do_branch,
4283 binop(Iop_And32, mkexpr(cond_ok), mkexpr(ctr_ok)) );
4284
cerion4561acb2005-02-21 14:07:48 +00004285 if (flag_AA) {
cerion2831b002005-11-30 19:55:22 +00004286 tgt = mkSzAddr(ty, extend_s_16to64(BD_u16));
cerion4561acb2005-02-21 14:07:48 +00004287 } else {
cerion2831b002005-11-30 19:55:22 +00004288 tgt = mkSzAddr(ty, guest_CIA_curr_instr +
4289 (Long)extend_s_16to64(BD_u16));
cerionb85e8bb2005-02-16 08:54:33 +00004290 }
ceriond953ebb2005-11-29 13:27:20 +00004291 if (flag_LK)
4292 putGST( PPC_GST_LR, e_nia );
cerionb85e8bb2005-02-16 08:54:33 +00004293
ceriond953ebb2005-11-29 13:27:20 +00004294 stmt( IRStmt_Exit(
4295 binop(Iop_CmpNE32, mkexpr(do_branch), mkU32(0)),
4296 flag_LK ? Ijk_Call : Ijk_Boring,
cerion2831b002005-11-30 19:55:22 +00004297 mkSzConst(ty, tgt) ) );
cerionb85e8bb2005-02-16 08:54:33 +00004298
4299 irbb->jumpkind = Ijk_Boring;
ceriond953ebb2005-11-29 13:27:20 +00004300 irbb->next = e_nia;
cerionb85e8bb2005-02-16 08:54:33 +00004301 break;
4302
4303 case 0x13:
sewardj6be67232006-01-24 19:00:05 +00004304 /* For bclr and bcctr, it appears that the lowest two bits of
4305 b11to15 are a branch hint, and so we only need to ensure it's
4306 of the form 000XX. */
4307 if ((b11to15 & ~3) != 0) {
4308 vex_printf("dis_int_branch(ppc)(0x13,b11to15)(%d)\n", (Int)b11to15);
cerionb85e8bb2005-02-16 08:54:33 +00004309 return False;
4310 }
cerion91ad5362005-01-27 23:02:41 +00004311
cerionb85e8bb2005-02-16 08:54:33 +00004312 switch (opc2) {
cerione9d361a2005-03-04 17:35:29 +00004313 case 0x210: // bcctr (Branch Cond. to Count Register, PPC32 p363)
cerion5b2325f2005-12-23 00:55:09 +00004314 if ((BO & 0x4) == 0) { // "decr and test CTR" option invalid
4315 vex_printf("dis_int_branch(ppc)(bcctr,BO)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004316 return False;
4317 }
ceriona31e8b52005-02-21 16:30:45 +00004318 DIP("bcctr%s 0x%x, 0x%x\n", flag_LK ? "l" : "", BO, BI);
cerionb85e8bb2005-02-16 08:54:33 +00004319
4320 assign( cond_ok, branch_cond_ok( BO, BI ) );
ceriond953ebb2005-11-29 13:27:20 +00004321
cerion2831b002005-11-30 19:55:22 +00004322 assign( lr_old, addr_align( getGST( PPC_GST_CTR ), 4 ));
sewardjdf07b882005-11-29 18:19:11 +00004323
ceriond953ebb2005-11-29 13:27:20 +00004324 if (flag_LK)
4325 putGST( PPC_GST_LR, e_nia );
cerionb85e8bb2005-02-16 08:54:33 +00004326
sewardjb51f0f42005-07-18 11:38:02 +00004327 stmt( IRStmt_Exit(
4328 binop(Iop_CmpEQ32, mkexpr(cond_ok), mkU32(0)),
4329 Ijk_Boring,
ceriond953ebb2005-11-29 13:27:20 +00004330 c_nia ));
cerionb85e8bb2005-02-16 08:54:33 +00004331
4332 irbb->jumpkind = flag_LK ? Ijk_Call : Ijk_Boring;
sewardjdf07b882005-11-29 18:19:11 +00004333 irbb->next = mkexpr(lr_old);
cerionb85e8bb2005-02-16 08:54:33 +00004334 break;
4335
sewardjcf8986c2006-01-18 04:14:52 +00004336 case 0x010: { // bclr (Branch Cond. to Link Register, PPC32 p365)
4337 Bool vanilla_return = False;
sewardjb51f0f42005-07-18 11:38:02 +00004338 if ((BO & 0x14 /* 1z1zz */) == 0x14 && flag_LK == 0) {
cerion225a0342005-09-12 20:49:09 +00004339 DIP("blr\n");
sewardjcf8986c2006-01-18 04:14:52 +00004340 vanilla_return = True;
sewardjb51f0f42005-07-18 11:38:02 +00004341 } else {
4342 DIP("bclr%s 0x%x, 0x%x\n", flag_LK ? "l" : "", BO, BI);
4343 }
cerion91ad5362005-01-27 23:02:41 +00004344
cerionb85e8bb2005-02-16 08:54:33 +00004345 if (!(BO & 0x4)) {
ceriond953ebb2005-11-29 13:27:20 +00004346 putGST( PPC_GST_CTR,
cerion2831b002005-11-30 19:55:22 +00004347 binop(mkSzOp(ty, Iop_Sub8),
4348 getGST( PPC_GST_CTR ), mkSzImm(ty, 1)) );
cerionb85e8bb2005-02-16 08:54:33 +00004349 }
4350
sewardjb51f0f42005-07-18 11:38:02 +00004351 /* See comments above for 'bc' about this */
4352 assign( ctr_ok, branch_ctr_ok( BO ) );
4353 assign( cond_ok, branch_cond_ok( BO, BI ) );
4354 assign( do_branch,
4355 binop(Iop_And32, mkexpr(cond_ok), mkexpr(ctr_ok)) );
cerion2831b002005-11-30 19:55:22 +00004356
sewardjdf07b882005-11-29 18:19:11 +00004357 assign( lr_old, addr_align( getGST( PPC_GST_LR ), 4 ));
4358
ceriond953ebb2005-11-29 13:27:20 +00004359 if (flag_LK)
4360 putGST( PPC_GST_LR, e_nia );
sewardjb51f0f42005-07-18 11:38:02 +00004361
4362 stmt( IRStmt_Exit(
4363 binop(Iop_CmpEQ32, mkexpr(do_branch), mkU32(0)),
4364 Ijk_Boring,
ceriond953ebb2005-11-29 13:27:20 +00004365 c_nia ));
sewardjb51f0f42005-07-18 11:38:02 +00004366
sewardjcf8986c2006-01-18 04:14:52 +00004367 if (vanilla_return && mode64)
4368 make_redzone_AbiHint( "branch-to-lr (unconditional return)" );
4369
sewardjd37be032005-11-12 12:56:31 +00004370 /* blrl is pretty strange; it's like a return that sets the
4371 return address of its caller to the insn following this
4372 one. Mark it as a return. */
4373 irbb->jumpkind = Ijk_Ret; /* was flag_LK ? Ijk_Call : Ijk_Ret; */
sewardjdf07b882005-11-29 18:19:11 +00004374 irbb->next = mkexpr(lr_old);
cerionb85e8bb2005-02-16 08:54:33 +00004375 break;
sewardjcf8986c2006-01-18 04:14:52 +00004376 }
cerionb85e8bb2005-02-16 08:54:33 +00004377 default:
cerion5b2325f2005-12-23 00:55:09 +00004378 vex_printf("dis_int_branch(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004379 return False;
4380 }
4381 break;
cerion2831b002005-11-30 19:55:22 +00004382
cerionb85e8bb2005-02-16 08:54:33 +00004383 default:
cerion5b2325f2005-12-23 00:55:09 +00004384 vex_printf("dis_int_branch(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004385 return False;
4386 }
cerion2831b002005-11-30 19:55:22 +00004387
cerionb85e8bb2005-02-16 08:54:33 +00004388 return True;
cerion91ad5362005-01-27 23:02:41 +00004389}
4390
4391
4392
cerion3d870a32005-03-18 12:23:33 +00004393/*
4394 Condition Register Logical Instructions
4395*/
cerion3007c7f2005-02-23 23:13:29 +00004396static Bool dis_cond_logic ( UInt theInstr )
4397{
4398 /* XL-Form */
cerion76de5cf2005-11-18 18:25:12 +00004399 UChar opc1 = ifieldOPC(theInstr);
4400 UChar crbD_addr = ifieldRegDS(theInstr);
4401 UChar crfD_addr = toUChar( IFIELD(theInstr, 23, 3) );
4402 UChar crbA_addr = ifieldRegA(theInstr);
4403 UChar crfS_addr = toUChar( IFIELD(theInstr, 18, 3) );
4404 UChar crbB_addr = ifieldRegB(theInstr);
4405 UInt opc2 = ifieldOPClo10(theInstr);
4406 UChar b0 = ifieldBIT0(theInstr);
cerion3007c7f2005-02-23 23:13:29 +00004407
cerionf0de28c2005-12-13 20:21:11 +00004408 IRTemp crbD = newTemp(Ity_I32);
4409 IRTemp crbA = newTemp(Ity_I32);
4410 IRTemp crbB = newTemp(Ity_I32);
cerion3007c7f2005-02-23 23:13:29 +00004411
4412 if (opc1 != 19 || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00004413 vex_printf("dis_cond_logic(ppc)(opc1)\n");
cerion3007c7f2005-02-23 23:13:29 +00004414 return False;
4415 }
4416
cerione9d361a2005-03-04 17:35:29 +00004417 if (opc2 == 0) { // mcrf (Move Cond Reg Field, PPC32 p464)
cerion3007c7f2005-02-23 23:13:29 +00004418 if (((crbD_addr & 0x3) != 0) ||
cerion76de5cf2005-11-18 18:25:12 +00004419 ((crbA_addr & 0x3) != 0) || (crbB_addr != 0)) {
cerion5b2325f2005-12-23 00:55:09 +00004420 vex_printf("dis_cond_logic(ppc)(crbD|crbA|crbB != 0)\n");
cerion3007c7f2005-02-23 23:13:29 +00004421 return False;
cerion76de5cf2005-11-18 18:25:12 +00004422 }
ceriond953ebb2005-11-29 13:27:20 +00004423 DIP("mcrf cr%u,cr%u\n", crfD_addr, crfS_addr);
sewardjb51f0f42005-07-18 11:38:02 +00004424 putCR0( crfD_addr, getCR0( crfS_addr) );
4425 putCR321( crfD_addr, getCR321(crfS_addr) );
cerion3007c7f2005-02-23 23:13:29 +00004426 } else {
sewardjb51f0f42005-07-18 11:38:02 +00004427 assign( crbA, getCRbit(crbA_addr) );
ceriona50fde52005-07-01 21:16:10 +00004428 if (crbA_addr == crbB_addr)
sewardjb51f0f42005-07-18 11:38:02 +00004429 crbB = crbA;
ceriona50fde52005-07-01 21:16:10 +00004430 else
sewardjb51f0f42005-07-18 11:38:02 +00004431 assign( crbB, getCRbit(crbB_addr) );
cerion3007c7f2005-02-23 23:13:29 +00004432
4433 switch (opc2) {
sewardj7c2dc712005-09-08 17:33:27 +00004434 case 0x101: // crand (Cond Reg AND, PPC32 p372)
4435 DIP("crand crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4436 assign( crbD, binop(Iop_And32, mkexpr(crbA), mkexpr(crbB)) );
4437 break;
sewardj7787af42005-08-04 18:32:19 +00004438 case 0x081: // crandc (Cond Reg AND w. Complement, PPC32 p373)
4439 DIP("crandc crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4440 assign( crbD, binop(Iop_And32,
4441 mkexpr(crbA),
4442 unop(Iop_Not32, mkexpr(crbB))) );
4443 break;
sewardje14bb9f2005-07-22 09:39:02 +00004444 case 0x121: // creqv (Cond Reg Equivalent, PPC32 p374)
4445 DIP("creqv crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4446 assign( crbD, unop(Iop_Not32,
4447 binop(Iop_Xor32, mkexpr(crbA), mkexpr(crbB))) );
4448 break;
sewardj7c2dc712005-09-08 17:33:27 +00004449 case 0x0E1: // crnand (Cond Reg NAND, PPC32 p375)
4450 DIP("crnand crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4451 assign( crbD, unop(Iop_Not32,
4452 binop(Iop_And32, mkexpr(crbA), mkexpr(crbB))) );
4453 break;
cerione9d361a2005-03-04 17:35:29 +00004454 case 0x021: // crnor (Cond Reg NOR, PPC32 p376)
cerion3007c7f2005-02-23 23:13:29 +00004455 DIP("crnor crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4456 assign( crbD, unop(Iop_Not32,
4457 binop(Iop_Or32, mkexpr(crbA), mkexpr(crbB))) );
4458 break;
cerione9d361a2005-03-04 17:35:29 +00004459 case 0x1C1: // cror (Cond Reg OR, PPC32 p377)
cerion3007c7f2005-02-23 23:13:29 +00004460 DIP("cror crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4461 assign( crbD, binop(Iop_Or32, mkexpr(crbA), mkexpr(crbB)) );
4462 break;
sewardj7c2dc712005-09-08 17:33:27 +00004463 case 0x1A1: // crorc (Cond Reg OR w. Complement, PPC32 p378)
4464 DIP("crorc crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4465 assign( crbD, binop(Iop_Or32,
4466 mkexpr(crbA),
4467 unop(Iop_Not32, mkexpr(crbB))) );
4468 break;
cerione9d361a2005-03-04 17:35:29 +00004469 case 0x0C1: // crxor (Cond Reg XOR, PPC32 p379)
cerion3007c7f2005-02-23 23:13:29 +00004470 DIP("crxor crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4471 assign( crbD, binop(Iop_Xor32, mkexpr(crbA), mkexpr(crbB)) );
4472 break;
cerion3007c7f2005-02-23 23:13:29 +00004473 default:
cerion5b2325f2005-12-23 00:55:09 +00004474 vex_printf("dis_cond_logic(ppc)(opc2)\n");
cerion3007c7f2005-02-23 23:13:29 +00004475 return False;
4476 }
4477
sewardjb51f0f42005-07-18 11:38:02 +00004478 putCRbit( crbD_addr, mkexpr(crbD) );
cerion3007c7f2005-02-23 23:13:29 +00004479 }
4480 return True;
4481}
4482
4483
sewardj334870d2006-02-07 16:42:39 +00004484/*
4485 Trap instructions
4486*/
4487
4488/* Do the code generation for a trap. Returned Bool is true iff
4489 this is an unconditional trap. */
sewardj2d19fe32006-02-07 20:55:08 +00004490static Bool do_trap ( Bool is_twi, UChar TO,
sewardj334870d2006-02-07 16:42:39 +00004491 IRExpr* argL0, ULong argR0, Addr64 cia )
4492{
4493 IRTemp argL, argR;
4494 IRExpr *argLe, *argRe, *cond, *tmp;
4495
sewardj2d19fe32006-02-07 20:55:08 +00004496 IROp opAND = is_twi ? Iop_And32 : Iop_And64;
4497 IROp opOR = is_twi ? Iop_Or32 : Iop_Or64;
4498 IROp opCMPORDS = is_twi ? Iop_CmpORD32S : Iop_CmpORD64S;
4499 IROp opCMPORDU = is_twi ? Iop_CmpORD32U : Iop_CmpORD64U;
4500 IROp opCMPNE = is_twi ? Iop_CmpNE32 : Iop_CmpNE64;
4501 IROp opCMPEQ = is_twi ? Iop_CmpEQ32 : Iop_CmpEQ64;
4502 IRExpr* const0 = is_twi ? mkU32(0) : mkU64(0);
4503 IRExpr* const2 = is_twi ? mkU32(2) : mkU64(2);
4504 IRExpr* const4 = is_twi ? mkU32(4) : mkU64(4);
4505 IRExpr* const8 = is_twi ? mkU32(8) : mkU64(8);
sewardj334870d2006-02-07 16:42:39 +00004506
4507 const UChar b11100 = 0x1C;
4508 const UChar b00111 = 0x07;
4509
4510 if ((TO & b11100) == b11100 || (TO & b00111) == b00111) {
4511 /* Unconditional trap. Just do the exit without
4512 testing the arguments. */
4513 stmt( IRStmt_Exit(
4514 binop(opCMPEQ, const0, const0),
4515 Ijk_Trap,
4516 mode64 ? IRConst_U64(cia) : IRConst_U32((UInt)cia)
4517 ));
4518 return True; /* unconditional trap */
4519 }
4520
sewardj2d19fe32006-02-07 20:55:08 +00004521 if (is_twi) {
4522 argL = newTemp(Ity_I32);
4523 argR = newTemp(Ity_I32);
4524 assign( argL, mode64 ? mkSzNarrow32(Ity_I64,argL0)
4525 : argL0 );
sewardj334870d2006-02-07 16:42:39 +00004526 assign( argR, mkU32( (UInt)argR0 ));
4527 } else {
4528 vassert(mode64);
sewardj2d19fe32006-02-07 20:55:08 +00004529 argL = newTemp(Ity_I64);
4530 argR = newTemp(Ity_I64);
sewardj334870d2006-02-07 16:42:39 +00004531 assign( argL, argL0 );
4532 assign( argR, mkU64( argR0 ));
4533 }
4534 argLe = mkexpr(argL);
4535 argRe = mkexpr(argR);
4536 cond = const0;
4537 if (TO & 16) { // L <s R
4538 tmp = binop(opAND, binop(opCMPORDS, argLe, argRe), const8);
4539 cond = binop(opOR, tmp, cond);
4540 }
4541 if (TO & 8) { // L >s R
4542 tmp = binop(opAND, binop(opCMPORDS, argLe, argRe), const4);
4543 cond = binop(opOR, tmp, cond);
4544 }
4545 if (TO & 4) { // L == R
4546 tmp = binop(opAND, binop(opCMPORDS, argLe, argRe), const2);
4547 cond = binop(opOR, tmp, cond);
4548 }
4549 if (TO & 2) { // L <u R
4550 tmp = binop(opAND, binop(opCMPORDU, argLe, argRe), const8);
4551 cond = binop(opOR, tmp, cond);
4552 }
4553 if (TO & 1) { // L >u R
4554 tmp = binop(opAND, binop(opCMPORDU, argLe, argRe), const4);
4555 cond = binop(opOR, tmp, cond);
4556 }
4557 stmt( IRStmt_Exit(
4558 binop(opCMPNE, cond, const0),
4559 Ijk_Trap,
4560 mode64 ? IRConst_U64(cia) : IRConst_U32((UInt)cia)
4561 ));
4562 return False; /* not an unconditional trap */
4563}
4564
4565static Bool dis_trapi ( UInt theInstr,
4566 /*OUT*/DisResult* dres )
4567{
4568 /* D-Form */
4569 UChar opc1 = ifieldOPC(theInstr);
4570 UChar TO = ifieldRegDS(theInstr);
4571 UChar rA_addr = ifieldRegA(theInstr);
4572 UInt uimm16 = ifieldUIMM16(theInstr);
4573 ULong simm16 = extend_s_16to64(uimm16);
4574 Addr64 cia = guest_CIA_curr_instr;
4575 IRType ty = mode64 ? Ity_I64 : Ity_I32;
4576 Bool uncond = False;
4577
4578 switch (opc1) {
4579 case 0x03: // twi (Trap Word Immediate, PPC32 p548)
sewardj2d19fe32006-02-07 20:55:08 +00004580 uncond = do_trap( True/*is_twi*/, TO, getIReg(rA_addr), simm16, cia );
sewardj334870d2006-02-07 16:42:39 +00004581 if (TO == 4) {
4582 DIP("tweqi r%u,%d\n", (UInt)rA_addr, (Int)simm16);
4583 } else {
4584 DIP("tw%di r%u,%d\n", (Int)TO, (UInt)rA_addr, (Int)simm16);
4585 }
4586 break;
4587 case 0x02: // tdi
4588 if (!mode64)
4589 return False;
sewardj2d19fe32006-02-07 20:55:08 +00004590 uncond = do_trap( False/*!is_twi*/, TO, getIReg(rA_addr), simm16, cia );
4591 if (TO == 4) {
4592 DIP("tdeqi r%u,%d\n", (UInt)rA_addr, (Int)simm16);
4593 } else {
4594 DIP("td%di r%u,%d\n", (Int)TO, (UInt)rA_addr, (Int)simm16);
4595 }
sewardj334870d2006-02-07 16:42:39 +00004596 break;
4597 default:
4598 return False;
4599 }
4600
4601 if (uncond) {
4602 /* If the trap shows signs of being unconditional, don't
4603 continue decoding past it. */
4604 irbb->next = mkSzImm( ty, nextInsnAddr() );
4605 irbb->jumpkind = Ijk_Boring;
4606 dres->whatNext = Dis_StopHere;
4607 }
4608
4609 return True;
4610}
4611
4612
cerion3d870a32005-03-18 12:23:33 +00004613/*
4614 System Linkage Instructions
4615*/
sewardj9e6491a2005-07-02 19:24:10 +00004616static Bool dis_syslink ( UInt theInstr, DisResult* dres )
cerion8c3adda2005-01-31 11:54:05 +00004617{
ceriond953ebb2005-11-29 13:27:20 +00004618 IRType ty = mode64 ? Ity_I64 : Ity_I32;
4619
cerionb85e8bb2005-02-16 08:54:33 +00004620 if (theInstr != 0x44000002) {
cerion5b2325f2005-12-23 00:55:09 +00004621 vex_printf("dis_syslink(ppc)(theInstr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004622 return False;
4623 }
cerione1d857b2005-02-04 18:29:05 +00004624
cerione9d361a2005-03-04 17:35:29 +00004625 // sc (System Call, PPC32 p504)
cerionb85e8bb2005-02-16 08:54:33 +00004626 DIP("sc\n");
4627
4628 /* It's important that all ArchRegs carry their up-to-date value
4629 at this point. So we declare an end-of-block here, which
4630 forces any TempRegs caching ArchRegs to be flushed. */
cerion2831b002005-11-30 19:55:22 +00004631 irbb->next = mkSzImm( ty, nextInsnAddr() );
sewardj4fa325a2005-11-03 13:27:24 +00004632 irbb->jumpkind = Ijk_Sys_syscall;
ceriond953ebb2005-11-29 13:27:20 +00004633
sewardj9e6491a2005-07-02 19:24:10 +00004634 dres->whatNext = Dis_StopHere;
cerionb85e8bb2005-02-16 08:54:33 +00004635 return True;
cerion8c3adda2005-01-31 11:54:05 +00004636}
4637
cerion3d870a32005-03-18 12:23:33 +00004638
4639/*
4640 Memory Synchronization Instructions
cerion07b07a92005-12-22 14:32:35 +00004641
4642 Note on Reservations:
4643 We rely on the assumption that V will in fact only allow one thread at
4644 once to run. In effect, a thread can make a reservation, but we don't
4645 check any stores it does. Instead, the reservation is cancelled when
4646 the scheduler switches to another thread (run_thread_for_a_while()).
cerion3d870a32005-03-18 12:23:33 +00004647*/
cerion8c3adda2005-01-31 11:54:05 +00004648static Bool dis_memsync ( UInt theInstr )
4649{
cerionb85e8bb2005-02-16 08:54:33 +00004650 /* X-Form, XL-Form */
ceriond953ebb2005-11-29 13:27:20 +00004651 UChar opc1 = ifieldOPC(theInstr);
4652 UInt b11to25 = IFIELD(theInstr, 11, 15);
cerione43bc882006-01-05 13:11:59 +00004653 UChar flag_L = ifieldRegDS(theInstr);
4654 UInt b11to20 = IFIELD(theInstr, 11, 10);
ceriond953ebb2005-11-29 13:27:20 +00004655 UChar rD_addr = ifieldRegDS(theInstr);
4656 UChar rS_addr = rD_addr;
4657 UChar rA_addr = ifieldRegA(theInstr);
4658 UChar rB_addr = ifieldRegB(theInstr);
4659 UInt opc2 = ifieldOPClo10(theInstr);
4660 UChar b0 = ifieldBIT0(theInstr);
cerionedf7fc52005-11-18 20:57:41 +00004661
ceriond953ebb2005-11-29 13:27:20 +00004662 IRType ty = mode64 ? Ity_I64 : Ity_I32;
4663 IRTemp EA = newTemp(ty);
4664 IRTemp rS = newTemp(ty);
4665
4666 assign( EA, ea_rAor0_idxd( rA_addr, rB_addr ) );
4667
cerionb85e8bb2005-02-16 08:54:33 +00004668 switch (opc1) {
sewardjafe85832005-09-09 10:25:39 +00004669 /* XL-Form */
cerione9d361a2005-03-04 17:35:29 +00004670 case 0x13: // isync (Instruction Synchronize, PPC32 p432)
cerionb85e8bb2005-02-16 08:54:33 +00004671 if (opc2 != 0x096) {
cerion5b2325f2005-12-23 00:55:09 +00004672 vex_printf("dis_memsync(ppc)(0x13,opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004673 return False;
4674 }
4675 if (b11to25 != 0 || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00004676 vex_printf("dis_memsync(ppc)(0x13,b11to25|b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004677 return False;
4678 }
4679 DIP("isync\n");
cerionb85e8bb2005-02-16 08:54:33 +00004680 stmt( IRStmt_MFence() );
4681 break;
cerion8c3adda2005-01-31 11:54:05 +00004682
cerionb85e8bb2005-02-16 08:54:33 +00004683 /* X-Form */
4684 case 0x1F:
4685 switch (opc2) {
cerion5b2325f2005-12-23 00:55:09 +00004686 case 0x356: // eieio (Enforce In-Order Exec of I/O, PPC32 p394)
sewardj7787af42005-08-04 18:32:19 +00004687 if (b11to25 != 0 || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00004688 vex_printf("dis_memsync(ppc)(eiei0,b11to25|b0)\n");
sewardj7787af42005-08-04 18:32:19 +00004689 return False;
4690 }
4691 DIP("eieio\n");
4692 /* Insert a memory fence, just to be on the safe side. */
4693 stmt( IRStmt_MFence() );
4694 break;
cerion8c3adda2005-01-31 11:54:05 +00004695
cerione9d361a2005-03-04 17:35:29 +00004696 case 0x014: // lwarx (Load Word and Reserve Indexed, PPC32 p458)
cerionb85e8bb2005-02-16 08:54:33 +00004697 if (b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00004698 vex_printf("dis_memsync(ppc)(lwarx,b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004699 return False;
4700 }
ceriond953ebb2005-11-29 13:27:20 +00004701 DIP("lwarx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
cerion5b2325f2005-12-23 00:55:09 +00004702 putIReg( rD_addr, mkSzWiden32(ty, loadBE(Ity_I32, mkexpr(EA)),
4703 False) );
cerion76de5cf2005-11-18 18:25:12 +00004704 /* Take a reservation */
ceriond953ebb2005-11-29 13:27:20 +00004705 putGST( PPC_GST_RESVN, mkexpr(EA) );
cerionb85e8bb2005-02-16 08:54:33 +00004706 break;
4707
sewardj7787af42005-08-04 18:32:19 +00004708 case 0x096: {
4709 // stwcx. (Store Word Conditional Indexed, PPC32 p532)
cerionf0de28c2005-12-13 20:21:11 +00004710 IRTemp resaddr = newTemp(ty);
cerionb85e8bb2005-02-16 08:54:33 +00004711 if (b0 != 1) {
cerion5b2325f2005-12-23 00:55:09 +00004712 vex_printf("dis_memsync(ppc)(stwcx.,b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004713 return False;
4714 }
ceriond953ebb2005-11-29 13:27:20 +00004715 DIP("stwcx. r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
cerion76de5cf2005-11-18 18:25:12 +00004716 assign( rS, getIReg(rS_addr) );
sewardjafe85832005-09-09 10:25:39 +00004717
cerion76de5cf2005-11-18 18:25:12 +00004718 /* First set up as if the reservation failed */
sewardj7787af42005-08-04 18:32:19 +00004719 // Set CR0[LT GT EQ S0] = 0b000 || XER[SO]
4720 putCR321(0, mkU8(0<<1));
4721 putCR0(0, getXER_SO());
4722
cerion76de5cf2005-11-18 18:25:12 +00004723 /* Get the reservation address into a temporary, then
4724 clear it. */
ceriond953ebb2005-11-29 13:27:20 +00004725 assign( resaddr, getGST(PPC_GST_RESVN) );
cerion2831b002005-11-30 19:55:22 +00004726 putGST( PPC_GST_RESVN, mkSzImm(ty, 0) );
sewardj7787af42005-08-04 18:32:19 +00004727
cerion76de5cf2005-11-18 18:25:12 +00004728 /* Skip the rest if the reservation really did fail. */
sewardj7787af42005-08-04 18:32:19 +00004729 stmt( IRStmt_Exit(
ceriond953ebb2005-11-29 13:27:20 +00004730 ( mode64 ?
4731 binop(Iop_CmpNE64, mkexpr(resaddr), mkexpr(EA)) :
4732 binop(Iop_CmpNE32, mkexpr(resaddr), mkexpr(EA)) ),
sewardj7787af42005-08-04 18:32:19 +00004733 Ijk_Boring,
cerion2831b002005-11-30 19:55:22 +00004734 mkSzConst( ty, nextInsnAddr()) ));
ceriond953ebb2005-11-29 13:27:20 +00004735
4736 /* Note for mode64:
4737 If resaddr != lwarx_resaddr, CR0[EQ] is undefined, and
4738 whether rS is stored is dependent on that value. */
sewardj7787af42005-08-04 18:32:19 +00004739
cerion4e2c2b32006-01-02 13:35:51 +00004740 /* Success? Do the (32bit) store */
4741 storeBE( mkexpr(EA), mkSzNarrow32(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00004742
sewardjb51f0f42005-07-18 11:38:02 +00004743 // Set CR0[LT GT EQ S0] = 0b001 || XER[SO]
4744 putCR321(0, mkU8(1<<1));
cerionb85e8bb2005-02-16 08:54:33 +00004745 break;
sewardj7787af42005-08-04 18:32:19 +00004746 }
4747
sewardjb029a612005-12-30 15:04:29 +00004748 case 0x256: // sync (Synchronize, PPC32 p543),
cerione43bc882006-01-05 13:11:59 +00004749 // also lwsync (L==1), ptesync (L==2)
sewardjb029a612005-12-30 15:04:29 +00004750 /* http://sources.redhat.com/ml/binutils/2000-12/msg00311.html
4751
4752 The PowerPC architecture used in IBM chips has expanded
4753 the sync instruction into two variants: lightweight sync
4754 and heavyweight sync. The original sync instruction is
4755 the new heavyweight sync and lightweight sync is a strict
4756 subset of the heavyweight sync functionality. This allows
4757 the programmer to specify a less expensive operation on
4758 high-end systems when the full sync functionality is not
4759 necessary.
4760
4761 The basic "sync" mnemonic now utilizes an operand. "sync"
4762 without an operand now becomes a extended mnemonic for
4763 heavyweight sync. Processors without the lwsync
4764 instruction will not decode the L field and will perform a
4765 heavyweight sync. Everything is backward compatible.
4766
4767 sync = sync 0
4768 lwsync = sync 1
cerione43bc882006-01-05 13:11:59 +00004769 ptesync = sync 2 *** TODO - not implemented ***
cerion4e2c2b32006-01-02 13:35:51 +00004770 */
cerione43bc882006-01-05 13:11:59 +00004771 if (b11to20 != 0 || b0 != 0) {
4772 vex_printf("dis_memsync(ppc)(sync/lwsync,b11to20|b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004773 return False;
4774 }
cerione43bc882006-01-05 13:11:59 +00004775 if (flag_L != 0/*sync*/ && flag_L != 1/*lwsync*/) {
4776 vex_printf("dis_memsync(ppc)(sync/lwsync,flag_L)\n");
4777 return False;
4778 }
4779 DIP("%ssync\n", flag_L == 1 ? "lw" : "");
cerionb85e8bb2005-02-16 08:54:33 +00004780 /* Insert a memory fence. It's sometimes important that these
4781 are carried through to the generated code. */
4782 stmt( IRStmt_MFence() );
4783 break;
cerionf0de28c2005-12-13 20:21:11 +00004784
cerionf0de28c2005-12-13 20:21:11 +00004785 /* 64bit Memsync */
cerion5b2325f2005-12-23 00:55:09 +00004786 case 0x054: // ldarx (Load DWord and Reserve Indexed, PPC64 p473)
cerionf0de28c2005-12-13 20:21:11 +00004787 if (b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00004788 vex_printf("dis_memsync(ppc)(ldarx,b0)\n");
cerionf0de28c2005-12-13 20:21:11 +00004789 return False;
4790 }
4791 DIP("ldarx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
cerion07b07a92005-12-22 14:32:35 +00004792 putIReg( rD_addr, loadBE(Ity_I64, mkexpr(EA)) );
4793 // Take a reservation
4794 putGST( PPC_GST_RESVN, mkexpr(EA) );
4795 break;
4796
cerion5b2325f2005-12-23 00:55:09 +00004797 case 0x0D6: { // stdcx. (Store DWord Condition Indexd, PPC64 p581)
cerion07b07a92005-12-22 14:32:35 +00004798 IRTemp resaddr = newTemp(ty);
cerionf0de28c2005-12-13 20:21:11 +00004799 if (b0 != 1) {
cerion5b2325f2005-12-23 00:55:09 +00004800 vex_printf("dis_memsync(ppc)(stdcx.,b0)\n");
cerionf0de28c2005-12-13 20:21:11 +00004801 return False;
4802 }
4803 DIP("stdcx. r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
cerion07b07a92005-12-22 14:32:35 +00004804 assign( rS, getIReg(rS_addr) );
cerionf0de28c2005-12-13 20:21:11 +00004805
cerion07b07a92005-12-22 14:32:35 +00004806 // First set up as if the reservation failed
4807 // Set CR0[LT GT EQ S0] = 0b000 || XER[SO]
4808 putCR321(0, mkU8(0<<1));
4809 putCR0(0, getXER_SO());
cerionf0de28c2005-12-13 20:21:11 +00004810
cerion07b07a92005-12-22 14:32:35 +00004811 // Get the reservation address into a temporary, then clear it.
4812 assign( resaddr, getGST(PPC_GST_RESVN) );
4813 putGST( PPC_GST_RESVN, mkSzImm(ty, 0) );
cerionf0de28c2005-12-13 20:21:11 +00004814
cerion07b07a92005-12-22 14:32:35 +00004815 // Skip the rest if the reservation really did fail.
4816 stmt( IRStmt_Exit( binop(Iop_CmpNE64, mkexpr(resaddr),
4817 mkexpr(EA)),
4818 Ijk_Boring,
4819 IRConst_U64(nextInsnAddr())) );
cerionf0de28c2005-12-13 20:21:11 +00004820
cerion07b07a92005-12-22 14:32:35 +00004821 // Success? Do the store
4822 storeBE( mkexpr(EA), mkexpr(rS) );
cerionb85e8bb2005-02-16 08:54:33 +00004823
cerion07b07a92005-12-22 14:32:35 +00004824 // Set CR0[LT GT EQ S0] = 0b001 || XER[SO]
4825 putCR321(0, mkU8(1<<1));
4826 break;
4827 }
4828
cerionb85e8bb2005-02-16 08:54:33 +00004829 default:
cerion5b2325f2005-12-23 00:55:09 +00004830 vex_printf("dis_memsync(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004831 return False;
4832 }
4833 break;
cerion8c3adda2005-01-31 11:54:05 +00004834
cerionb85e8bb2005-02-16 08:54:33 +00004835 default:
cerion5b2325f2005-12-23 00:55:09 +00004836 vex_printf("dis_memsync(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004837 return False;
4838 }
4839 return True;
cerion8c3adda2005-01-31 11:54:05 +00004840}
4841
4842
4843
cerion3d870a32005-03-18 12:23:33 +00004844/*
4845 Integer Shift Instructions
4846*/
cerion645c9302005-01-31 10:09:59 +00004847static Bool dis_int_shift ( UInt theInstr )
4848{
cerionf0de28c2005-12-13 20:21:11 +00004849 /* X-Form, XS-Form */
cerion76de5cf2005-11-18 18:25:12 +00004850 UChar opc1 = ifieldOPC(theInstr);
4851 UChar rS_addr = ifieldRegDS(theInstr);
4852 UChar rA_addr = ifieldRegA(theInstr);
4853 UChar rB_addr = ifieldRegB(theInstr);
4854 UChar sh_imm = rB_addr;
4855 UInt opc2 = ifieldOPClo10(theInstr);
cerionf0de28c2005-12-13 20:21:11 +00004856 UChar b1 = ifieldBIT1(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00004857 UChar flag_rC = ifieldBIT0(theInstr);
4858
ceriond953ebb2005-11-29 13:27:20 +00004859 IRType ty = mode64 ? Ity_I64 : Ity_I32;
4860 IRTemp rA = newTemp(ty);
cerion07b07a92005-12-22 14:32:35 +00004861 IRTemp rS = newTemp(ty);
4862 IRTemp rB = newTemp(ty);
ceriond953ebb2005-11-29 13:27:20 +00004863 IRTemp outofrange = newTemp(Ity_I8);
ceriond953ebb2005-11-29 13:27:20 +00004864 IRTemp rS_lo32 = newTemp(Ity_I32);
4865 IRTemp rB_lo32 = newTemp(Ity_I32);
4866 IRExpr* e_tmp;
4867
cerion07b07a92005-12-22 14:32:35 +00004868 assign( rS, getIReg(rS_addr) );
4869 assign( rB, getIReg(rB_addr) );
4870 assign( rS_lo32, mkSzNarrow32(ty, mkexpr(rS)) );
4871 assign( rB_lo32, mkSzNarrow32(ty, mkexpr(rB)) );
cerionb85e8bb2005-02-16 08:54:33 +00004872
4873 if (opc1 == 0x1F) {
4874 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00004875 case 0x018: { // slw (Shift Left Word, PPC32 p505)
cerion5b2325f2005-12-23 00:55:09 +00004876 DIP("slw%s r%u,r%u,r%u\n", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00004877 rA_addr, rS_addr, rB_addr);
4878 /* rA = rS << rB */
4879 /* ppc32 semantics are:
sewardjdfb11442005-10-08 19:58:48 +00004880 slw(x,y) = (x << (y & 31)) -- primary result
4881 & ~((y << 26) >>s 31) -- make result 0
4882 for y in 32 .. 63
4883 */
ceriond953ebb2005-11-29 13:27:20 +00004884 e_tmp =
4885 binop( Iop_And32,
4886 binop( Iop_Shl32,
4887 mkexpr(rS_lo32),
4888 unop( Iop_32to8,
4889 binop(Iop_And32,
4890 mkexpr(rB_lo32), mkU32(31)))),
4891 unop( Iop_Not32,
4892 binop( Iop_Sar32,
4893 binop(Iop_Shl32, mkexpr(rB_lo32), mkU8(26)),
4894 mkU8(31))) );
cerion2831b002005-11-30 19:55:22 +00004895 assign( rA, mkSzWiden32(ty, e_tmp, /* Signed */False) );
cerionb85e8bb2005-02-16 08:54:33 +00004896 break;
ceriond953ebb2005-11-29 13:27:20 +00004897 }
4898
cerion5b2325f2005-12-23 00:55:09 +00004899 case 0x318: { // sraw (Shift Right Alg Word, PPC32 p506)
cerion07b07a92005-12-22 14:32:35 +00004900 IRTemp sh_amt = newTemp(Ity_I32);
cerion5b2325f2005-12-23 00:55:09 +00004901 DIP("sraw%s r%u,r%u,r%u\n", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00004902 rA_addr, rS_addr, rB_addr);
cerion76de5cf2005-11-18 18:25:12 +00004903 /* JRS: my reading of the (poorly worded) PPC32 doc p506 is:
4904 amt = rB & 63
4905 rA = Sar32( rS, amt > 31 ? 31 : amt )
4906 XER.CA = amt > 31 ? sign-of-rS : (computation as per srawi)
4907 */
cerion5b2325f2005-12-23 00:55:09 +00004908 assign( sh_amt, binop(Iop_And32, mkU32(0x3F),
4909 mkexpr(rB_lo32)) );
cerion76de5cf2005-11-18 18:25:12 +00004910 assign( outofrange,
sewardj20ef5472005-07-21 14:48:31 +00004911 unop( Iop_1Uto8,
cerion5b2325f2005-12-23 00:55:09 +00004912 binop(Iop_CmpLT32U, mkU32(31),
4913 mkexpr(sh_amt)) ));
ceriond953ebb2005-11-29 13:27:20 +00004914 e_tmp = binop( Iop_Sar32,
4915 mkexpr(rS_lo32),
sewardj20ef5472005-07-21 14:48:31 +00004916 unop( Iop_32to8,
4917 IRExpr_Mux0X( mkexpr(outofrange),
cerion07b07a92005-12-22 14:32:35 +00004918 mkexpr(sh_amt),
ceriond953ebb2005-11-29 13:27:20 +00004919 mkU32(31)) ) );
cerion2831b002005-11-30 19:55:22 +00004920 assign( rA, mkSzWiden32(ty, e_tmp, /* Signed */True) );
4921
cerion5b2325f2005-12-23 00:55:09 +00004922 set_XER_CA( ty, PPCG_FLAG_OP_SRAW,
cerionf0de28c2005-12-13 20:21:11 +00004923 mkexpr(rA),
4924 mkSzWiden32(ty, mkexpr(rS_lo32), True),
cerion07b07a92005-12-22 14:32:35 +00004925 mkSzWiden32(ty, mkexpr(sh_amt), True ),
cerionf0de28c2005-12-13 20:21:11 +00004926 mkSzWiden32(ty, getXER_CA32(), True) );
cerionb85e8bb2005-02-16 08:54:33 +00004927 break;
cerion07b07a92005-12-22 14:32:35 +00004928 }
cerionb85e8bb2005-02-16 08:54:33 +00004929
cerion5b2325f2005-12-23 00:55:09 +00004930 case 0x338: // srawi (Shift Right Alg Word Immediate, PPC32 p507)
4931 DIP("srawi%s r%u,r%u,%d\n", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00004932 rA_addr, rS_addr, sh_imm);
4933 vassert(sh_imm < 32);
cerionf0de28c2005-12-13 20:21:11 +00004934 if (mode64) {
4935 assign( rA, binop(Iop_Sar64,
cerion5b2325f2005-12-23 00:55:09 +00004936 binop(Iop_Shl64, getIReg(rS_addr),
4937 mkU8(32)),
cerionf0de28c2005-12-13 20:21:11 +00004938 mkU8(32 + sh_imm)) );
4939 } else {
cerion5b2325f2005-12-23 00:55:09 +00004940 assign( rA, binop(Iop_Sar32, mkexpr(rS_lo32),
4941 mkU8(sh_imm)) );
cerionf0de28c2005-12-13 20:21:11 +00004942 }
cerion2831b002005-11-30 19:55:22 +00004943
cerion5b2325f2005-12-23 00:55:09 +00004944 set_XER_CA( ty, PPCG_FLAG_OP_SRAWI,
cerionf0de28c2005-12-13 20:21:11 +00004945 mkexpr(rA),
cerion5b2325f2005-12-23 00:55:09 +00004946 mkSzWiden32(ty, mkexpr(rS_lo32), /* Syned */True),
cerionf0de28c2005-12-13 20:21:11 +00004947 mkSzImm(ty, sh_imm),
cerion5b2325f2005-12-23 00:55:09 +00004948 mkSzWiden32(ty, getXER_CA32(), /* Syned */False) );
cerionb85e8bb2005-02-16 08:54:33 +00004949 break;
4950
cerione9d361a2005-03-04 17:35:29 +00004951 case 0x218: // srw (Shift Right Word, PPC32 p508)
cerion5b2325f2005-12-23 00:55:09 +00004952 DIP("srw%s r%u,r%u,r%u\n", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00004953 rA_addr, rS_addr, rB_addr);
4954 /* rA = rS >>u rB */
4955 /* ppc32 semantics are:
cerionf0de28c2005-12-13 20:21:11 +00004956 srw(x,y) = (x >>u (y & 31)) -- primary result
sewardjdfb11442005-10-08 19:58:48 +00004957 & ~((y << 26) >>s 31) -- make result 0
4958 for y in 32 .. 63
4959 */
ceriond953ebb2005-11-29 13:27:20 +00004960 e_tmp =
sewardjdfb11442005-10-08 19:58:48 +00004961 binop(
4962 Iop_And32,
4963 binop( Iop_Shr32,
ceriond953ebb2005-11-29 13:27:20 +00004964 mkexpr(rS_lo32),
sewardjdfb11442005-10-08 19:58:48 +00004965 unop( Iop_32to8,
cerion5b2325f2005-12-23 00:55:09 +00004966 binop(Iop_And32, mkexpr(rB_lo32),
4967 mkU32(31)))),
sewardjdfb11442005-10-08 19:58:48 +00004968 unop( Iop_Not32,
4969 binop( Iop_Sar32,
cerion5b2325f2005-12-23 00:55:09 +00004970 binop(Iop_Shl32, mkexpr(rB_lo32),
4971 mkU8(26)),
ceriond953ebb2005-11-29 13:27:20 +00004972 mkU8(31))));
cerion2831b002005-11-30 19:55:22 +00004973 assign( rA, mkSzWiden32(ty, e_tmp, /* Signed */False) );
cerionb85e8bb2005-02-16 08:54:33 +00004974 break;
cerionf0de28c2005-12-13 20:21:11 +00004975
4976
4977 /* 64bit Shifts */
cerion5b2325f2005-12-23 00:55:09 +00004978 case 0x01B: // sld (Shift Left DWord, PPC64 p568)
4979 DIP("sld%s r%u,r%u,r%u\n",
4980 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerionf0de28c2005-12-13 20:21:11 +00004981 /* rA = rS << rB */
cerion07b07a92005-12-22 14:32:35 +00004982 /* ppc64 semantics are:
cerionf0de28c2005-12-13 20:21:11 +00004983 slw(x,y) = (x << (y & 63)) -- primary result
4984 & ~((y << 57) >>s 63) -- make result 0
4985 for y in 64 ..
4986 */
cerionf0de28c2005-12-13 20:21:11 +00004987 assign( rA,
4988 binop(
4989 Iop_And64,
4990 binop( Iop_Shl64,
4991 mkexpr(rS),
4992 unop( Iop_64to8,
4993 binop(Iop_And64, mkexpr(rB), mkU64(63)))),
4994 unop( Iop_Not64,
4995 binop( Iop_Sar64,
4996 binop(Iop_Shl64, mkexpr(rB), mkU8(57)),
4997 mkU8(63)))) );
cerion07b07a92005-12-22 14:32:35 +00004998 break;
cerionf0de28c2005-12-13 20:21:11 +00004999
cerion5b2325f2005-12-23 00:55:09 +00005000 case 0x31A: { // srad (Shift Right Alg DWord, PPC64 p570)
cerion07b07a92005-12-22 14:32:35 +00005001 IRTemp sh_amt = newTemp(Ity_I64);
cerion5b2325f2005-12-23 00:55:09 +00005002 DIP("srad%s r%u,r%u,r%u\n",
5003 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerionf0de28c2005-12-13 20:21:11 +00005004 /* amt = rB & 127
5005 rA = Sar64( rS, amt > 63 ? 63 : amt )
5006 XER.CA = amt > 63 ? sign-of-rS : (computation as per srawi)
5007 */
cerion07b07a92005-12-22 14:32:35 +00005008 assign( sh_amt, binop(Iop_And64, mkU64(0x7F), mkexpr(rB)) );
cerionf0de28c2005-12-13 20:21:11 +00005009 assign( outofrange,
5010 unop( Iop_1Uto8,
cerion5b2325f2005-12-23 00:55:09 +00005011 binop(Iop_CmpLT64U, mkU64(63),
5012 mkexpr(sh_amt)) ));
cerionf0de28c2005-12-13 20:21:11 +00005013 assign( rA,
5014 binop( Iop_Sar64,
5015 mkexpr(rS),
5016 unop( Iop_64to8,
5017 IRExpr_Mux0X( mkexpr(outofrange),
cerion07b07a92005-12-22 14:32:35 +00005018 mkexpr(sh_amt),
5019 mkU64(63)) ))
cerionf0de28c2005-12-13 20:21:11 +00005020 );
cerion5b2325f2005-12-23 00:55:09 +00005021 set_XER_CA( ty, PPCG_FLAG_OP_SRAD,
cerion07b07a92005-12-22 14:32:35 +00005022 mkexpr(rA), mkexpr(rS), mkexpr(sh_amt),
cerion5b2325f2005-12-23 00:55:09 +00005023 mkSzWiden32(ty, getXER_CA32(), /* Syned */False) );
cerion07b07a92005-12-22 14:32:35 +00005024 break;
5025 }
5026
cerion5b2325f2005-12-23 00:55:09 +00005027 case 0x33A: case 0x33B: // sradi (Shr Alg DWord Imm, PPC64 p571)
cerionf0de28c2005-12-13 20:21:11 +00005028 sh_imm |= b1<<5;
5029 vassert(sh_imm < 64);
cerion5b2325f2005-12-23 00:55:09 +00005030 DIP("sradi%s r%u,r%u,%u\n",
5031 flag_rC ? ".":"", rA_addr, rS_addr, sh_imm);
cerionf0de28c2005-12-13 20:21:11 +00005032 assign( rA, binop(Iop_Sar64, getIReg(rS_addr), mkU8(sh_imm)) );
5033
cerion5b2325f2005-12-23 00:55:09 +00005034 set_XER_CA( ty, PPCG_FLAG_OP_SRADI,
cerionf0de28c2005-12-13 20:21:11 +00005035 mkexpr(rA),
5036 getIReg(rS_addr),
5037 mkU64(sh_imm),
cerion5b2325f2005-12-23 00:55:09 +00005038 mkSzWiden32(ty, getXER_CA32(), /* Syned */False) );
cerionf0de28c2005-12-13 20:21:11 +00005039 break;
5040
cerion5b2325f2005-12-23 00:55:09 +00005041 case 0x21B: // srd (Shift Right DWord, PPC64 p574)
5042 DIP("srd%s r%u,r%u,r%u\n",
5043 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerionf0de28c2005-12-13 20:21:11 +00005044 /* rA = rS >>u rB */
cerion07b07a92005-12-22 14:32:35 +00005045 /* ppc semantics are:
cerionf0de28c2005-12-13 20:21:11 +00005046 srw(x,y) = (x >>u (y & 63)) -- primary result
5047 & ~((y << 57) >>s 63) -- make result 0
5048 for y in 64 .. 127
5049 */
cerionf0de28c2005-12-13 20:21:11 +00005050 assign( rA,
5051 binop(
5052 Iop_And64,
5053 binop( Iop_Shr64,
5054 mkexpr(rS),
5055 unop( Iop_64to8,
5056 binop(Iop_And64, mkexpr(rB), mkU64(63)))),
5057 unop( Iop_Not64,
5058 binop( Iop_Sar64,
5059 binop(Iop_Shl64, mkexpr(rB), mkU8(57)),
5060 mkU8(63)))) );
cerion07b07a92005-12-22 14:32:35 +00005061 break;
cerionf0de28c2005-12-13 20:21:11 +00005062
cerionb85e8bb2005-02-16 08:54:33 +00005063 default:
cerion5b2325f2005-12-23 00:55:09 +00005064 vex_printf("dis_int_shift(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00005065 return False;
5066 }
5067 } else {
cerion5b2325f2005-12-23 00:55:09 +00005068 vex_printf("dis_int_shift(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00005069 return False;
5070 }
cerion0d330c52005-02-28 16:43:16 +00005071
cerion76de5cf2005-11-18 18:25:12 +00005072 putIReg( rA_addr, mkexpr(rA) );
cerionb85e8bb2005-02-16 08:54:33 +00005073
cerion76de5cf2005-11-18 18:25:12 +00005074 if (flag_rC) {
5075 set_CR0( mkexpr(rA) );
cerionb85e8bb2005-02-16 08:54:33 +00005076 }
5077 return True;
cerion645c9302005-01-31 10:09:59 +00005078}
5079
5080
5081
sewardj602857d2005-09-06 09:10:09 +00005082/*
5083 Integer Load/Store Reverse Instructions
5084*/
sewardjfb957972005-09-08 17:53:03 +00005085static IRExpr* /* :: Ity_I32 */ gen_byterev32 ( IRTemp t )
5086{
5087 vassert(typeOfIRTemp(irbb->tyenv, t) == Ity_I32);
5088 return
5089 binop(Iop_Or32,
5090 binop(Iop_Shl32, mkexpr(t), mkU8(24)),
5091 binop(Iop_Or32,
5092 binop(Iop_And32, binop(Iop_Shl32, mkexpr(t), mkU8(8)),
5093 mkU32(0x00FF0000)),
5094 binop(Iop_Or32,
5095 binop(Iop_And32, binop(Iop_Shr32, mkexpr(t), mkU8(8)),
5096 mkU32(0x0000FF00)),
5097 binop(Iop_And32, binop(Iop_Shr32, mkexpr(t), mkU8(24)),
5098 mkU32(0x000000FF) )
5099 )));
5100}
5101
sewardj602857d2005-09-06 09:10:09 +00005102static Bool dis_int_ldst_rev ( UInt theInstr )
5103{
5104 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00005105 UChar opc1 = ifieldOPC(theInstr);
5106 UChar rD_addr = ifieldRegDS(theInstr);
5107 UChar rS_addr = rD_addr;
5108 UChar rA_addr = ifieldRegA(theInstr);
5109 UChar rB_addr = ifieldRegB(theInstr);
5110 UInt opc2 = ifieldOPClo10(theInstr);
5111 UChar b0 = ifieldBIT0(theInstr);
cerionedf7fc52005-11-18 20:57:41 +00005112
ceriond953ebb2005-11-29 13:27:20 +00005113 IRType ty = mode64 ? Ity_I64 : Ity_I32;
5114 IRTemp EA = newTemp(ty);
cerionedf7fc52005-11-18 20:57:41 +00005115 IRTemp w1 = newTemp(Ity_I32);
5116 IRTemp w2 = newTemp(Ity_I32);
sewardj602857d2005-09-06 09:10:09 +00005117
5118 if (opc1 != 0x1F || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00005119 vex_printf("dis_int_ldst_rev(ppc)(opc1|b0)\n");
sewardj602857d2005-09-06 09:10:09 +00005120 return False;
5121 }
sewardjafe85832005-09-09 10:25:39 +00005122
ceriond953ebb2005-11-29 13:27:20 +00005123 assign( EA, ea_rAor0_idxd( rA_addr, rB_addr ) );
sewardj602857d2005-09-06 09:10:09 +00005124
5125 switch (opc2) {
sewardjb51f0f42005-07-18 11:38:02 +00005126//zz case 0x316: // lhbrx (Load Half Word Byte-Reverse Indexed, PPC32 p449)
5127//zz vassert(0);
5128//zz
ceriond953ebb2005-11-29 13:27:20 +00005129//zz DIP("lhbrx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
sewardjb51f0f42005-07-18 11:38:02 +00005130//zz assign( byte0, loadBE(Ity_I8, mkexpr(EA)) );
5131//zz assign( byte1, loadBE(Ity_I8, binop(Iop_Add32, mkexpr(EA),mkU32(1))) );
cerion76de5cf2005-11-18 18:25:12 +00005132//zz assign( rD, binop(Iop_Or32,
sewardjb51f0f42005-07-18 11:38:02 +00005133//zz binop(Iop_Shl32, mkexpr(byte1), mkU8(8)),
5134//zz mkexpr(byte0)) );
cerion76de5cf2005-11-18 18:25:12 +00005135//zz putIReg( rD_addr, mkexpr(rD));
sewardjb51f0f42005-07-18 11:38:02 +00005136//zz break;
sewardj602857d2005-09-06 09:10:09 +00005137
sewardjfb957972005-09-08 17:53:03 +00005138 case 0x216: // lwbrx (Load Word Byte-Reverse Indexed, PPC32 p459)
ceriond953ebb2005-11-29 13:27:20 +00005139 DIP("lwbrx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
sewardjfb957972005-09-08 17:53:03 +00005140 assign( w1, loadBE(Ity_I32, mkexpr(EA)) );
5141 assign( w2, gen_byterev32(w1) );
cerion5b2325f2005-12-23 00:55:09 +00005142 putIReg( rD_addr, mkSzWiden32(ty, mkexpr(w2),
5143 /* Signed */False) );
sewardjfb957972005-09-08 17:53:03 +00005144 break;
sewardj602857d2005-09-06 09:10:09 +00005145
sewardjb51f0f42005-07-18 11:38:02 +00005146//zz case 0x396: // sthbrx (Store Half Word Byte-Reverse Indexed, PPC32 p523)
5147//zz vassert(0);
5148//zz
ceriond953ebb2005-11-29 13:27:20 +00005149//zz DIP("sthbrx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
cerion76de5cf2005-11-18 18:25:12 +00005150//zz assign( rS, getIReg(rS_addr) );
5151//zz assign( byte0, binop(Iop_And32, mkexpr(rS), mkU32(0x00FF)) );
5152//zz assign( byte1, binop(Iop_And32, mkexpr(rS), mkU32(0xFF00)) );
sewardjb51f0f42005-07-18 11:38:02 +00005153//zz
5154//zz assign( tmp16,
5155//zz unop(Iop_32to16,
5156//zz binop(Iop_Or32,
5157//zz binop(Iop_Shl32, mkexpr(byte0), mkU8(8)),
5158//zz binop(Iop_Shr32, mkexpr(byte1), mkU8(8)))) );
5159//zz storeBE( mkexpr(EA), getIReg(tmp16) );
5160//zz break;
sewardj602857d2005-09-06 09:10:09 +00005161
cerion5b2325f2005-12-23 00:55:09 +00005162 case 0x296: // stwbrx (Store Word Byte-Reverse Indxd, PPC32 p531)
ceriond953ebb2005-11-29 13:27:20 +00005163 DIP("stwbrx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00005164 assign( w1, mkSzNarrow32(ty, getIReg(rS_addr)) );
sewardjfb957972005-09-08 17:53:03 +00005165 storeBE( mkexpr(EA), gen_byterev32(w1) );
5166 break;
5167
5168 default:
cerion5b2325f2005-12-23 00:55:09 +00005169 vex_printf("dis_int_ldst_rev(ppc)(opc2)\n");
sewardjfb957972005-09-08 17:53:03 +00005170 return False;
sewardj602857d2005-09-06 09:10:09 +00005171 }
5172 return True;
5173}
cerion645c9302005-01-31 10:09:59 +00005174
5175
5176
cerion3d870a32005-03-18 12:23:33 +00005177/*
5178 Processor Control Instructions
5179*/
cerion8c3adda2005-01-31 11:54:05 +00005180static Bool dis_proc_ctl ( UInt theInstr )
5181{
cerion76de5cf2005-11-18 18:25:12 +00005182 UChar opc1 = ifieldOPC(theInstr);
cerionb85e8bb2005-02-16 08:54:33 +00005183
5184 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00005185 UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) );
5186 UChar b21to22 = toUChar( IFIELD( theInstr, 21, 2 ) );
5187 UChar rD_addr = ifieldRegDS(theInstr);
5188 UInt b11to20 = IFIELD( theInstr, 11, 10 );
cerione9d361a2005-03-04 17:35:29 +00005189
cerion76de5cf2005-11-18 18:25:12 +00005190 /* XFX-Form */
5191 UChar rS_addr = rD_addr;
5192 UInt SPR = b11to20;
cerionedf7fc52005-11-18 20:57:41 +00005193 UInt TBR = b11to20;
cerion76de5cf2005-11-18 18:25:12 +00005194 UChar b20 = toUChar( IFIELD( theInstr, 20, 1 ) );
5195 UInt CRM = IFIELD( theInstr, 12, 8 );
5196 UChar b11 = toUChar( IFIELD( theInstr, 11, 1 ) );
5197
5198 UInt opc2 = ifieldOPClo10(theInstr);
5199 UChar b0 = ifieldBIT0(theInstr);
5200
cerion2831b002005-11-30 19:55:22 +00005201 IRType ty = mode64 ? Ity_I64 : Ity_I32;
cerionf0de28c2005-12-13 20:21:11 +00005202 IRTemp rS = newTemp(ty);
ceriond953ebb2005-11-29 13:27:20 +00005203 assign( rS, getIReg(rS_addr) );
sewardj41a7b702005-11-18 22:18:23 +00005204
cerion76de5cf2005-11-18 18:25:12 +00005205 /* Reorder SPR field as per PPC32 p470 */
5206 SPR = ((SPR & 0x1F) << 5) | ((SPR >> 5) & 0x1F);
sewardj73a91972005-09-06 10:25:46 +00005207 /* Reorder TBR field as per PPC32 p475 */
5208 TBR = ((TBR & 31) << 5) | ((TBR >> 5) & 31);
cerionb85e8bb2005-02-16 08:54:33 +00005209
5210 if (opc1 != 0x1F || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00005211 vex_printf("dis_proc_ctl(ppc)(opc1|b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00005212 return False;
5213 }
5214
5215 switch (opc2) {
cerioncb14e732005-09-09 16:38:19 +00005216 /* X-Form */
cerion5b2325f2005-12-23 00:55:09 +00005217 case 0x200: { // mcrxr (Move to Cond Register from XER, PPC32 p466)
cerioncb14e732005-09-09 16:38:19 +00005218 if (b21to22 != 0 || b11to20 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00005219 vex_printf("dis_proc_ctl(ppc)(mcrxr,b21to22|b11to20)\n");
cerioncb14e732005-09-09 16:38:19 +00005220 return False;
5221 }
5222 DIP("mcrxr crf%d\n", crfD);
cerionedf7fc52005-11-18 20:57:41 +00005223 /* Move XER[0-3] (the top 4 bits of XER) to CR[crfD] */
ceriond953ebb2005-11-29 13:27:20 +00005224 putGST_field( PPC_GST_CR,
5225 getGST_field( PPC_GST_XER, 7 ),
cerionedf7fc52005-11-18 20:57:41 +00005226 crfD );
sewardj55ccc3e2005-09-09 19:45:02 +00005227
5228 // Clear XER[0-3]
cerionedf7fc52005-11-18 20:57:41 +00005229 putXER_SO( mkU8(0) );
5230 putXER_OV( mkU8(0) );
5231 putXER_CA( mkU8(0) );
cerioncb14e732005-09-09 16:38:19 +00005232 break;
sewardj55ccc3e2005-09-09 19:45:02 +00005233 }
cerionb85e8bb2005-02-16 08:54:33 +00005234
sewardj72aefb22006-03-01 18:58:39 +00005235 case 0x013:
5236 // b11to20==0: mfcr (Move from Cond Register, PPC32 p467)
5237 // b20==1 & b11==0: mfocrf (Move from One CR Field)
5238 // However it seems that the 'mfcr' behaviour is an acceptable
5239 // implementation of mfocr (from the 2.02 arch spec)
5240 if (b11to20 == 0) {
5241 DIP("mfcr r%u\n", rD_addr);
5242 putIReg( rD_addr, mkSzWiden32(ty, getGST( PPC_GST_CR ),
5243 /* Signed */False) );
5244 break;
cerionb85e8bb2005-02-16 08:54:33 +00005245 }
sewardj72aefb22006-03-01 18:58:39 +00005246 if (b20 == 1 && b11 == 0) {
5247 DIP("mfocrf r%u,%u\n", rD_addr, CRM);
5248 putIReg( rD_addr, mkSzWiden32(ty, getGST( PPC_GST_CR ),
5249 /* Signed */False) );
5250 break;
5251 }
5252 /* not decodable */
5253 return False;
5254
cerionb85e8bb2005-02-16 08:54:33 +00005255 /* XFX-Form */
cerione9d361a2005-03-04 17:35:29 +00005256 case 0x153: // mfspr (Move from Special-Purpose Register, PPC32 p470)
cerionb85e8bb2005-02-16 08:54:33 +00005257
cerion76de5cf2005-11-18 18:25:12 +00005258 switch (SPR) { // Choose a register...
ceriond953ebb2005-11-29 13:27:20 +00005259 case 0x1:
5260 DIP("mfxer r%u\n", rD_addr);
cerion2831b002005-11-30 19:55:22 +00005261 putIReg( rD_addr, mkSzWiden32(ty, getGST( PPC_GST_XER ),
5262 /* Signed */False) );
ceriond953ebb2005-11-29 13:27:20 +00005263 break;
5264 case 0x8:
5265 DIP("mflr r%u\n", rD_addr);
5266 putIReg( rD_addr, getGST( PPC_GST_LR ) );
5267 break;
5268 case 0x9:
5269 DIP("mfctr r%u\n", rD_addr);
5270 putIReg( rD_addr, getGST( PPC_GST_CTR ) );
5271 break;
5272 case 0x100:
5273 DIP("mfvrsave r%u\n", rD_addr);
cerion2831b002005-11-30 19:55:22 +00005274 putIReg( rD_addr, mkSzWiden32(ty, getGST( PPC_GST_VRSAVE ),
5275 /* Signed */False) );
ceriond953ebb2005-11-29 13:27:20 +00005276 break;
5277
5278 default:
cerion5b2325f2005-12-23 00:55:09 +00005279 vex_printf("dis_proc_ctl(ppc)(mfspr,SPR)(0x%x)\n", SPR);
ceriond953ebb2005-11-29 13:27:20 +00005280 return False;
cerionb85e8bb2005-02-16 08:54:33 +00005281 }
5282 break;
5283
sewardj73a91972005-09-06 10:25:46 +00005284 case 0x173: { // mftb (Move from Time Base, PPC32 p475)
5285 IRTemp val = newTemp(Ity_I64);
5286 IRExpr** args = mkIRExprVec_0();
cerion4c4f5ef2006-01-02 14:41:50 +00005287 IRDirty* d = unsafeIRDirty_1_N(
5288 val,
5289 0/*regparms*/,
5290 "ppcg_dirtyhelper_MFTB",
5291 fnptr_to_fnentry(&ppcg_dirtyhelper_MFTB),
5292 args );
sewardj73a91972005-09-06 10:25:46 +00005293 /* execute the dirty call, dumping the result in val. */
5294 stmt( IRStmt_Dirty(d) );
5295
5296 switch (TBR) {
ceriond953ebb2005-11-29 13:27:20 +00005297 case 269:
ceriond953ebb2005-11-29 13:27:20 +00005298 DIP("mftbu r%u", rD_addr);
cerion2831b002005-11-30 19:55:22 +00005299 putIReg( rD_addr,
5300 mkSzWiden32(ty, unop(Iop_64HIto32, mkexpr(val)),
5301 /* Signed */False) );
ceriond953ebb2005-11-29 13:27:20 +00005302 break;
5303 case 268:
ceriond953ebb2005-11-29 13:27:20 +00005304 DIP("mftb r%u", rD_addr);
cerion2831b002005-11-30 19:55:22 +00005305 putIReg( rD_addr, (mode64) ? mkexpr(val) :
5306 unop(Iop_64to32, mkexpr(val)) );
ceriond953ebb2005-11-29 13:27:20 +00005307 break;
5308 default:
5309 return False; /* illegal instruction */
sewardj73a91972005-09-06 10:25:46 +00005310 }
5311 break;
5312 }
5313
sewardj72aefb22006-03-01 18:58:39 +00005314 case 0x090: {
5315 // b20==0: mtcrf (Move to Cond Register Fields, PPC32 p477)
5316 // b20==1: mtocrf (Move to One Cond Reg Field)
cerionedf7fc52005-11-18 20:57:41 +00005317 Int cr;
5318 UChar shft;
sewardj72aefb22006-03-01 18:58:39 +00005319 if (b11 != 0)
cerionb85e8bb2005-02-16 08:54:33 +00005320 return False;
sewardj72aefb22006-03-01 18:58:39 +00005321 if (b20 == 1) {
5322 /* ppc64 v2.02 spec says mtocrf gives undefined outcome if >
5323 1 field is written. It seems more robust to decline to
5324 decode the insn if so. */
5325 switch (CRM) {
5326 case 0x01: case 0x02: case 0x04: case 0x08:
5327 case 0x10: case 0x20: case 0x40: case 0x80:
5328 break;
5329 default:
5330 return False;
5331 }
cerionb85e8bb2005-02-16 08:54:33 +00005332 }
sewardj72aefb22006-03-01 18:58:39 +00005333 DIP("%s 0x%x,r%u\n", b20==1 ? "mtocrf" : "mtcrf",
5334 CRM, rS_addr);
cerionedf7fc52005-11-18 20:57:41 +00005335 /* Write to each field specified by CRM */
5336 for (cr = 0; cr < 8; cr++) {
5337 if ((CRM & (1 << (7-cr))) == 0)
5338 continue;
5339 shft = 4*(7-cr);
ceriond953ebb2005-11-29 13:27:20 +00005340 putGST_field( PPC_GST_CR,
cerion2831b002005-11-30 19:55:22 +00005341 binop(Iop_Shr32,
5342 mkSzNarrow32(ty, mkexpr(rS)),
5343 mkU8(shft)), cr );
cerionedf7fc52005-11-18 20:57:41 +00005344 }
cerionb85e8bb2005-02-16 08:54:33 +00005345 break;
cerionedf7fc52005-11-18 20:57:41 +00005346 }
cerione9d361a2005-03-04 17:35:29 +00005347
5348 case 0x1D3: // mtspr (Move to Special-Purpose Register, PPC32 p483)
cerionb85e8bb2005-02-16 08:54:33 +00005349
cerion76de5cf2005-11-18 18:25:12 +00005350 switch (SPR) { // Choose a register...
ceriond953ebb2005-11-29 13:27:20 +00005351 case 0x1:
5352 DIP("mtxer r%u\n", rS_addr);
cerion2831b002005-11-30 19:55:22 +00005353 putGST( PPC_GST_XER, mkSzNarrow32(ty, mkexpr(rS)) );
ceriond953ebb2005-11-29 13:27:20 +00005354 break;
5355 case 0x8:
5356 DIP("mtlr r%u\n", rS_addr);
5357 putGST( PPC_GST_LR, mkexpr(rS) );
5358 break;
5359 case 0x9:
5360 DIP("mtctr r%u\n", rS_addr);
5361 putGST( PPC_GST_CTR, mkexpr(rS) );
5362 break;
5363 case 0x100:
5364 DIP("mtvrsave r%u\n", rS_addr);
cerion2831b002005-11-30 19:55:22 +00005365 putGST( PPC_GST_VRSAVE, mkSzNarrow32(ty, mkexpr(rS)) );
ceriond953ebb2005-11-29 13:27:20 +00005366 break;
5367
5368 default:
cerion5b2325f2005-12-23 00:55:09 +00005369 vex_printf("dis_proc_ctl(ppc)(mtspr,SPR)(%u)\n", SPR);
ceriond953ebb2005-11-29 13:27:20 +00005370 return False;
cerionb85e8bb2005-02-16 08:54:33 +00005371 }
5372 break;
5373
5374 default:
cerion5b2325f2005-12-23 00:55:09 +00005375 vex_printf("dis_proc_ctl(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00005376 return False;
5377 }
5378 return True;
cerion8c3adda2005-01-31 11:54:05 +00005379}
5380
5381
cerion3d870a32005-03-18 12:23:33 +00005382/*
5383 Cache Management Instructions
5384*/
sewardjd94b73a2005-06-30 12:08:48 +00005385static Bool dis_cache_manage ( UInt theInstr,
sewardj9e6491a2005-07-02 19:24:10 +00005386 DisResult* dres,
sewardjd94b73a2005-06-30 12:08:48 +00005387 VexArchInfo* guest_archinfo )
cerion8c3adda2005-01-31 11:54:05 +00005388{
cerionb85e8bb2005-02-16 08:54:33 +00005389 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00005390 UChar opc1 = ifieldOPC(theInstr);
5391 UChar b21to25 = ifieldRegDS(theInstr);
5392 UChar rA_addr = ifieldRegA(theInstr);
5393 UChar rB_addr = ifieldRegB(theInstr);
5394 UInt opc2 = ifieldOPClo10(theInstr);
5395 UChar b0 = ifieldBIT0(theInstr);
cerion5b2325f2005-12-23 00:55:09 +00005396 UInt lineszB = guest_archinfo->ppc_cache_line_szB;
ceriond953ebb2005-11-29 13:27:20 +00005397
5398 IRType ty = mode64 ? Ity_I64 : Ity_I32;
cerion094d1392005-06-20 13:45:57 +00005399
sewardj6be67232006-01-24 19:00:05 +00005400 /* For dcbt, the lowest two bits of b21to25 encode an
5401 access-direction hint (TH field) which we ignore. Well, that's
5402 what the PowerPC documentation says. In fact xlc -O4 on POWER5
5403 seems to generate values of 8 and 10 for b21to25. */
5404 if (opc1 == 0x1F && opc2 == 0x116) {
5405 /* b21to25 &= ~3; */ /* if the docs were true */
5406 b21to25 = 0; /* blunt instrument */
5407 }
5408
cerionb85e8bb2005-02-16 08:54:33 +00005409 if (opc1 != 0x1F || b21to25 != 0 || b0 != 0) {
sewardj6be67232006-01-24 19:00:05 +00005410 if (0) vex_printf("dis_cache_manage %d %d %d\n",
5411 (Int)opc1, (Int)b21to25, (Int)b0);
cerion5b2325f2005-12-23 00:55:09 +00005412 vex_printf("dis_cache_manage(ppc)(opc1|b21to25|b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00005413 return False;
5414 }
sewardjd94b73a2005-06-30 12:08:48 +00005415
5416 /* stay sane .. */
5417 vassert(lineszB == 32 || lineszB == 128);
cerionb85e8bb2005-02-16 08:54:33 +00005418
5419 switch (opc2) {
sewardjb51f0f42005-07-18 11:38:02 +00005420//zz case 0x2F6: // dcba (Data Cache Block Allocate, PPC32 p380)
5421//zz vassert(0); /* AWAITING TEST CASE */
ceriond953ebb2005-11-29 13:27:20 +00005422//zz DIP("dcba r%u,r%u\n", rA_addr, rB_addr);
cerion5b2325f2005-12-23 00:55:09 +00005423//zz if (0) vex_printf("vex ppc->IR: kludged dcba\n");
sewardjb51f0f42005-07-18 11:38:02 +00005424//zz break;
sewardj20ef5472005-07-21 14:48:31 +00005425
5426 case 0x056: // dcbf (Data Cache Block Flush, PPC32 p382)
ceriond953ebb2005-11-29 13:27:20 +00005427 DIP("dcbf r%u,r%u\n", rA_addr, rB_addr);
sewardj20ef5472005-07-21 14:48:31 +00005428 /* nop as far as vex is concerned */
sewardj20ef5472005-07-21 14:48:31 +00005429 break;
cerionb85e8bb2005-02-16 08:54:33 +00005430
cerione9d361a2005-03-04 17:35:29 +00005431 case 0x036: // dcbst (Data Cache Block Store, PPC32 p384)
ceriond953ebb2005-11-29 13:27:20 +00005432 DIP("dcbst r%u,r%u\n", rA_addr, rB_addr);
sewardjb51f0f42005-07-18 11:38:02 +00005433 /* nop as far as vex is concerned */
cerionb85e8bb2005-02-16 08:54:33 +00005434 break;
cerion8c3adda2005-01-31 11:54:05 +00005435
cerione9d361a2005-03-04 17:35:29 +00005436 case 0x116: // dcbt (Data Cache Block Touch, PPC32 p385)
ceriond953ebb2005-11-29 13:27:20 +00005437 DIP("dcbt r%u,r%u\n", rA_addr, rB_addr);
sewardjb51f0f42005-07-18 11:38:02 +00005438 /* nop as far as vex is concerned */
cerionb85e8bb2005-02-16 08:54:33 +00005439 break;
5440
cerione9d361a2005-03-04 17:35:29 +00005441 case 0x0F6: // dcbtst (Data Cache Block Touch for Store, PPC32 p386)
ceriond953ebb2005-11-29 13:27:20 +00005442 DIP("dcbtst r%u,r%u\n", rA_addr, rB_addr);
sewardjb51f0f42005-07-18 11:38:02 +00005443 /* nop as far as vex is concerned */
cerionb85e8bb2005-02-16 08:54:33 +00005444 break;
5445
cerion094d1392005-06-20 13:45:57 +00005446 case 0x3F6: { // dcbz (Data Cache Block Clear to Zero, PPC32 p387)
sewardjd94b73a2005-06-30 12:08:48 +00005447 /* Clear all bytes in cache block at (rA|0) + rB. */
cerion2831b002005-11-30 19:55:22 +00005448 IRTemp EA = newTemp(ty);
5449 IRTemp addr = newTemp(ty);
cerion094d1392005-06-20 13:45:57 +00005450 IRExpr* irx_addr;
cerion094d1392005-06-20 13:45:57 +00005451 UInt i;
ceriond953ebb2005-11-29 13:27:20 +00005452 DIP("dcbz r%u,r%u\n", rA_addr, rB_addr);
sewardjcb1f68e2005-12-30 03:39:14 +00005453
ceriond953ebb2005-11-29 13:27:20 +00005454 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
cerion094d1392005-06-20 13:45:57 +00005455
cerion2831b002005-11-30 19:55:22 +00005456 if (mode64) {
5457 /* Round EA down to the start of the containing block. */
5458 assign( addr, binop( Iop_And64,
5459 mkexpr(EA),
5460 mkU64( ~((ULong)lineszB-1) )) );
5461
5462 for (i = 0; i < lineszB / 8; i++) {
5463 irx_addr = binop( Iop_Add64, mkexpr(addr), mkU64(i*8) );
5464 storeBE( irx_addr, mkU64(0) );
5465 }
5466 } else {
5467 /* Round EA down to the start of the containing block. */
5468 assign( addr, binop( Iop_And32,
5469 mkexpr(EA),
5470 mkU32( ~(lineszB-1) )) );
5471
5472 for (i = 0; i < lineszB / 4; i++) {
5473 irx_addr = binop( Iop_Add32, mkexpr(addr), mkU32(i*4) );
5474 storeBE( irx_addr, mkU32(0) );
5475 }
cerion094d1392005-06-20 13:45:57 +00005476 }
cerionb85e8bb2005-02-16 08:54:33 +00005477 break;
cerion094d1392005-06-20 13:45:57 +00005478 }
cerion8c3adda2005-01-31 11:54:05 +00005479
sewardj7ce9d152005-03-15 16:54:13 +00005480 case 0x3D6: {
5481 // icbi (Instruction Cache Block Invalidate, PPC32 p431)
5482 /* Invalidate all translations containing code from the cache
sewardjd94b73a2005-06-30 12:08:48 +00005483 block at (rA|0) + rB. */
ceriond953ebb2005-11-29 13:27:20 +00005484 IRTemp EA = newTemp(ty);
5485 IRTemp addr = newTemp(ty);
5486 DIP("icbi r%u,r%u\n", rA_addr, rB_addr);
5487 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
sewardj7ce9d152005-03-15 16:54:13 +00005488
cerion2831b002005-11-30 19:55:22 +00005489 /* Round EA down to the start of the containing block. */
5490 assign( addr, binop( mkSzOp(ty, Iop_And8),
ceriond953ebb2005-11-29 13:27:20 +00005491 mkexpr(EA),
cerion2831b002005-11-30 19:55:22 +00005492 mkSzImm(ty, ~(((ULong)lineszB)-1) )) );
ceriond953ebb2005-11-29 13:27:20 +00005493 putGST( PPC_GST_TISTART, mkexpr(addr) );
cerion2831b002005-11-30 19:55:22 +00005494 putGST( PPC_GST_TILEN, mkSzImm(ty, lineszB) );
sewardj7ce9d152005-03-15 16:54:13 +00005495
sewardja8078f62005-03-15 18:27:40 +00005496 /* be paranoid ... */
5497 stmt( IRStmt_MFence() );
5498
sewardj7ce9d152005-03-15 16:54:13 +00005499 irbb->jumpkind = Ijk_TInval;
cerion2831b002005-11-30 19:55:22 +00005500 irbb->next = mkSzImm(ty, nextInsnAddr());
sewardj9e6491a2005-07-02 19:24:10 +00005501 dres->whatNext = Dis_StopHere;
cerionb85e8bb2005-02-16 08:54:33 +00005502 break;
sewardj7ce9d152005-03-15 16:54:13 +00005503 }
cerion8c3adda2005-01-31 11:54:05 +00005504
cerionb85e8bb2005-02-16 08:54:33 +00005505 default:
cerion5b2325f2005-12-23 00:55:09 +00005506 vex_printf("dis_cache_manage(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00005507 return False;
5508 }
5509 return True;
cerion8c3adda2005-01-31 11:54:05 +00005510}
5511
5512
sewardje14bb9f2005-07-22 09:39:02 +00005513/*------------------------------------------------------------*/
5514/*--- Floating Point Helpers ---*/
5515/*------------------------------------------------------------*/
5516
sewardje14bb9f2005-07-22 09:39:02 +00005517/* --------- Synthesise a 2-bit FPU rounding mode. --------- */
5518/* Produces a value in 0 .. 3, which is encoded as per the type
cerion5b2325f2005-12-23 00:55:09 +00005519 IRRoundingMode. PPCRoundingMode encoding is different to
sewardje14bb9f2005-07-22 09:39:02 +00005520 IRRoundingMode, so need to map it.
5521*/
sewardjb183b852006-02-03 16:08:03 +00005522static IRExpr* /* :: Ity_I32 */ get_IR_roundingmode ( void )
sewardje14bb9f2005-07-22 09:39:02 +00005523{
5524/*
5525 rounding mode | PPC | IR
5526 ------------------------
5527 to nearest | 00 | 00
5528 to zero | 01 | 11
5529 to +infinity | 10 | 10
5530 to -infinity | 11 | 01
5531*/
5532 IRTemp rm_PPC32 = newTemp(Ity_I32);
ceriond953ebb2005-11-29 13:27:20 +00005533 assign( rm_PPC32, getGST_masked( PPC_GST_FPSCR, MASK_FPSCR_RN ) );
sewardje14bb9f2005-07-22 09:39:02 +00005534
5535 // rm_IR = XOR( rm_PPC32, (rm_PPC32 << 1) & 2)
sewardjb183b852006-02-03 16:08:03 +00005536 return binop( Iop_Xor32,
5537 mkexpr(rm_PPC32),
5538 binop( Iop_And32,
5539 binop(Iop_Shl32, mkexpr(rm_PPC32), mkU8(1)),
5540 mkU32(2) ));
sewardje14bb9f2005-07-22 09:39:02 +00005541}
cerion094d1392005-06-20 13:45:57 +00005542
5543
cerion3d870a32005-03-18 12:23:33 +00005544/*------------------------------------------------------------*/
5545/*--- Floating Point Instruction Translation ---*/
5546/*------------------------------------------------------------*/
5547
5548/*
5549 Floating Point Load Instructions
5550*/
5551static Bool dis_fp_load ( UInt theInstr )
5552{
cerion76de5cf2005-11-18 18:25:12 +00005553 /* X-Form, D-Form */
5554 UChar opc1 = ifieldOPC(theInstr);
5555 UChar frD_addr = ifieldRegDS(theInstr);
5556 UChar rA_addr = ifieldRegA(theInstr);
5557 UChar rB_addr = ifieldRegB(theInstr);
5558 UInt opc2 = ifieldOPClo10(theInstr);
5559 UChar b0 = ifieldBIT0(theInstr);
cerion2831b002005-11-30 19:55:22 +00005560 UInt uimm16 = ifieldUIMM16(theInstr);
cerion094d1392005-06-20 13:45:57 +00005561
cerion2831b002005-11-30 19:55:22 +00005562 Int simm16 = extend_s_16to32(uimm16);
5563 IRType ty = mode64 ? Ity_I64 : Ity_I32;
5564 IRTemp EA = newTemp(ty);
5565 IRTemp rA = newTemp(ty);
5566 IRTemp rB = newTemp(ty);
cerion094d1392005-06-20 13:45:57 +00005567
5568 assign( rA, getIReg(rA_addr) );
5569 assign( rB, getIReg(rB_addr) );
ceriond953ebb2005-11-29 13:27:20 +00005570
sewardjb183b852006-02-03 16:08:03 +00005571 /* These are completely straightforward from a rounding and status
5572 bits perspective: no rounding involved and no funny status or CR
5573 bits affected. */
cerion3d870a32005-03-18 12:23:33 +00005574
sewardjb183b852006-02-03 16:08:03 +00005575 switch (opc1) {
sewardje14bb9f2005-07-22 09:39:02 +00005576 case 0x30: // lfs (Load Float Single, PPC32 p441)
ceriond953ebb2005-11-29 13:27:20 +00005577 DIP("lfs fr%u,%d(r%u)\n", frD_addr, simm16, rA_addr);
5578 assign( EA, ea_rAor0_simm(rA_addr, simm16) );
cerion5b2325f2005-12-23 00:55:09 +00005579 putFReg( frD_addr,
5580 unop(Iop_F32toF64, loadBE(Ity_F32, mkexpr(EA))) );
sewardje14bb9f2005-07-22 09:39:02 +00005581 break;
5582
cerion5b2325f2005-12-23 00:55:09 +00005583 case 0x31: // lfsu (Load Float Single, Update, PPC32 p442)
sewardjb183b852006-02-03 16:08:03 +00005584 if (rA_addr == 0)
cerion729edb72005-12-02 16:03:46 +00005585 return False;
cerion729edb72005-12-02 16:03:46 +00005586 DIP("lfsu fr%u,%d(r%u)\n", frD_addr, simm16, rA_addr);
5587 assign( EA, ea_rA_simm(rA_addr, simm16) );
cerion5b2325f2005-12-23 00:55:09 +00005588 putFReg( frD_addr,
5589 unop(Iop_F32toF64, loadBE(Ity_F32, mkexpr(EA))) );
cerion729edb72005-12-02 16:03:46 +00005590 putIReg( rA_addr, mkexpr(EA) );
5591 break;
cerion094d1392005-06-20 13:45:57 +00005592
5593 case 0x32: // lfd (Load Float Double, PPC32 p437)
ceriond953ebb2005-11-29 13:27:20 +00005594 DIP("lfd fr%u,%d(r%u)\n", frD_addr, simm16, rA_addr);
5595 assign( EA, ea_rAor0_simm(rA_addr, simm16) );
cerion094d1392005-06-20 13:45:57 +00005596 putFReg( frD_addr, loadBE(Ity_F64, mkexpr(EA)) );
5597 break;
cerion3d870a32005-03-18 12:23:33 +00005598
cerion5b2325f2005-12-23 00:55:09 +00005599 case 0x33: // lfdu (Load Float Double, Update, PPC32 p438)
sewardjb183b852006-02-03 16:08:03 +00005600 if (rA_addr == 0)
sewardj0e2cc672005-07-29 21:58:51 +00005601 return False;
ceriond953ebb2005-11-29 13:27:20 +00005602 DIP("lfdu fr%u,%d(r%u)\n", frD_addr, simm16, rA_addr);
5603 assign( EA, ea_rA_simm(rA_addr, simm16) );
sewardj0e2cc672005-07-29 21:58:51 +00005604 putFReg( frD_addr, loadBE(Ity_F64, mkexpr(EA)) );
5605 putIReg( rA_addr, mkexpr(EA) );
5606 break;
sewardje14bb9f2005-07-22 09:39:02 +00005607
5608 case 0x1F:
5609 if (b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00005610 vex_printf("dis_fp_load(ppc)(instr,b0)\n");
sewardje14bb9f2005-07-22 09:39:02 +00005611 return False;
5612 }
5613
5614 switch(opc2) {
ceriond953ebb2005-11-29 13:27:20 +00005615 case 0x217: // lfsx (Load Float Single Indexed, PPC32 p444)
5616 DIP("lfsx fr%u,r%u,r%u\n", frD_addr, rA_addr, rB_addr);
5617 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
5618 putFReg( frD_addr, unop( Iop_F32toF64,
5619 loadBE(Ity_F32, mkexpr(EA))) );
5620 break;
5621
cerion5b2325f2005-12-23 00:55:09 +00005622 case 0x237: // lfsux (Load Float Single, Update Indxd, PPC32 p443)
sewardjb183b852006-02-03 16:08:03 +00005623 if (rA_addr == 0)
sewardje14bb9f2005-07-22 09:39:02 +00005624 return False;
ceriond953ebb2005-11-29 13:27:20 +00005625 DIP("lfsux fr%u,r%u,r%u\n", frD_addr, rA_addr, rB_addr);
5626 assign( EA, ea_rA_idxd(rA_addr, rB_addr) );
cerion5b2325f2005-12-23 00:55:09 +00005627 putFReg( frD_addr,
5628 unop(Iop_F32toF64, loadBE(Ity_F32, mkexpr(EA))) );
ceriond953ebb2005-11-29 13:27:20 +00005629 putIReg( rA_addr, mkexpr(EA) );
5630 break;
5631
5632 case 0x257: // lfdx (Load Float Double Indexed, PPC32 p440)
5633 DIP("lfdx fr%u,r%u,r%u\n", frD_addr, rA_addr, rB_addr);
5634 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
5635 putFReg( frD_addr, loadBE(Ity_F64, mkexpr(EA)) );
5636 break;
5637
cerion5b2325f2005-12-23 00:55:09 +00005638 case 0x277: // lfdux (Load Float Double, Update Indxd, PPC32 p439)
sewardjb183b852006-02-03 16:08:03 +00005639 if (rA_addr == 0)
ceriond953ebb2005-11-29 13:27:20 +00005640 return False;
ceriond953ebb2005-11-29 13:27:20 +00005641 DIP("lfdux fr%u,r%u,r%u\n", frD_addr, rA_addr, rB_addr);
5642 assign( EA, ea_rA_idxd(rA_addr, rB_addr) );
5643 putFReg( frD_addr, loadBE(Ity_F64, mkexpr(EA)) );
5644 putIReg( rA_addr, mkexpr(EA) );
5645 break;
5646
5647 default:
cerion5b2325f2005-12-23 00:55:09 +00005648 vex_printf("dis_fp_load(ppc)(opc2)\n");
ceriond953ebb2005-11-29 13:27:20 +00005649 return False;
sewardje14bb9f2005-07-22 09:39:02 +00005650 }
5651 break;
cerion3d870a32005-03-18 12:23:33 +00005652
5653 default:
cerion5b2325f2005-12-23 00:55:09 +00005654 vex_printf("dis_fp_load(ppc)(opc1)\n");
cerion3d870a32005-03-18 12:23:33 +00005655 return False;
5656 }
5657 return True;
5658}
5659
5660
5661
5662/*
5663 Floating Point Store Instructions
5664*/
5665static Bool dis_fp_store ( UInt theInstr )
5666{
cerion76de5cf2005-11-18 18:25:12 +00005667 /* X-Form, D-Form */
5668 UChar opc1 = ifieldOPC(theInstr);
5669 UChar frS_addr = ifieldRegDS(theInstr);
5670 UChar rA_addr = ifieldRegA(theInstr);
5671 UChar rB_addr = ifieldRegB(theInstr);
5672 UInt opc2 = ifieldOPClo10(theInstr);
5673 UChar b0 = ifieldBIT0(theInstr);
ceriond953ebb2005-11-29 13:27:20 +00005674 Int uimm16 = ifieldUIMM16(theInstr);
cerion094d1392005-06-20 13:45:57 +00005675
cerion2831b002005-11-30 19:55:22 +00005676 Int simm16 = extend_s_16to32(uimm16);
5677 IRTemp frS = newTemp(Ity_F64);
5678 IRType ty = mode64 ? Ity_I64 : Ity_I32;
5679 IRTemp EA = newTemp(ty);
5680 IRTemp rA = newTemp(ty);
5681 IRTemp rB = newTemp(ty);
cerion094d1392005-06-20 13:45:57 +00005682
5683 assign( frS, getFReg(frS_addr) );
cerionedf7fc52005-11-18 20:57:41 +00005684 assign( rA, getIReg(rA_addr) );
5685 assign( rB, getIReg(rB_addr) );
cerion3d870a32005-03-18 12:23:33 +00005686
sewardjb183b852006-02-03 16:08:03 +00005687 /* These are straightforward from a status bits perspective: no
5688 funny status or CR bits affected. For single precision stores,
5689 the values are truncated and denormalised (not rounded) to turn
5690 them into single precision values. */
5691
5692 switch (opc1) {
sewardje14bb9f2005-07-22 09:39:02 +00005693
5694 case 0x34: // stfs (Store Float Single, PPC32 p518)
ceriond953ebb2005-11-29 13:27:20 +00005695 DIP("stfs fr%u,%d(r%u)\n", frS_addr, simm16, rA_addr);
5696 assign( EA, ea_rAor0_simm(rA_addr, simm16) );
sewardjb183b852006-02-03 16:08:03 +00005697 /* Use Iop_TruncF64asF32 to truncate and possible denormalise
5698 the value to be stored in the correct way, without any
5699 rounding. */
sewardje14bb9f2005-07-22 09:39:02 +00005700 storeBE( mkexpr(EA),
sewardjb183b852006-02-03 16:08:03 +00005701 unop(Iop_TruncF64asF32, mkexpr(frS)) );
sewardje14bb9f2005-07-22 09:39:02 +00005702 break;
5703
cerion5b2325f2005-12-23 00:55:09 +00005704 case 0x35: // stfsu (Store Float Single, Update, PPC32 p519)
sewardjb183b852006-02-03 16:08:03 +00005705 if (rA_addr == 0)
cerion729edb72005-12-02 16:03:46 +00005706 return False;
cerion729edb72005-12-02 16:03:46 +00005707 DIP("stfsu fr%u,%d(r%u)\n", frS_addr, simm16, rA_addr);
5708 assign( EA, ea_rA_simm(rA_addr, simm16) );
sewardjb183b852006-02-03 16:08:03 +00005709 /* See comment for stfs */
cerion729edb72005-12-02 16:03:46 +00005710 storeBE( mkexpr(EA),
sewardjb183b852006-02-03 16:08:03 +00005711 unop(Iop_TruncF64asF32, mkexpr(frS)) );
cerion729edb72005-12-02 16:03:46 +00005712 putIReg( rA_addr, mkexpr(EA) );
5713 break;
cerion3d870a32005-03-18 12:23:33 +00005714
cerion094d1392005-06-20 13:45:57 +00005715 case 0x36: // stfd (Store Float Double, PPC32 p513)
ceriond953ebb2005-11-29 13:27:20 +00005716 DIP("stfd fr%u,%d(r%u)\n", frS_addr, simm16, rA_addr);
5717 assign( EA, ea_rAor0_simm(rA_addr, simm16) );
cerion094d1392005-06-20 13:45:57 +00005718 storeBE( mkexpr(EA), mkexpr(frS) );
5719 break;
cerion3d870a32005-03-18 12:23:33 +00005720
cerion5b2325f2005-12-23 00:55:09 +00005721 case 0x37: // stfdu (Store Float Double, Update, PPC32 p514)
sewardjb183b852006-02-03 16:08:03 +00005722 if (rA_addr == 0)
sewardje14bb9f2005-07-22 09:39:02 +00005723 return False;
ceriond953ebb2005-11-29 13:27:20 +00005724 DIP("stfdu fr%u,%d(r%u)\n", frS_addr, simm16, rA_addr);
5725 assign( EA, ea_rA_simm(rA_addr, simm16) );
sewardje14bb9f2005-07-22 09:39:02 +00005726 storeBE( mkexpr(EA), mkexpr(frS) );
5727 putIReg( rA_addr, mkexpr(EA) );
5728 break;
5729
5730 case 0x1F:
5731 if (b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00005732 vex_printf("dis_fp_store(ppc)(instr,b0)\n");
sewardje14bb9f2005-07-22 09:39:02 +00005733 return False;
5734 }
sewardje14bb9f2005-07-22 09:39:02 +00005735 switch(opc2) {
ceriond953ebb2005-11-29 13:27:20 +00005736 case 0x297: // stfsx (Store Float Single Indexed, PPC32 p521)
5737 DIP("stfsx fr%u,r%u,r%u\n", frS_addr, rA_addr, rB_addr);
5738 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
sewardjb183b852006-02-03 16:08:03 +00005739 /* See note for stfs */
5740 storeBE( mkexpr(EA),
5741 unop(Iop_TruncF64asF32, mkexpr(frS)) );
ceriond953ebb2005-11-29 13:27:20 +00005742 break;
5743
cerion5b2325f2005-12-23 00:55:09 +00005744 case 0x2B7: // stfsux (Store Float Sgl, Update Indxd, PPC32 p520)
sewardjb183b852006-02-03 16:08:03 +00005745 if (rA_addr == 0)
cerion729edb72005-12-02 16:03:46 +00005746 return False;
cerion729edb72005-12-02 16:03:46 +00005747 DIP("stfsux fr%u,r%u,r%u\n", frS_addr, rA_addr, rB_addr);
5748 assign( EA, ea_rA_idxd(rA_addr, rB_addr) );
sewardjb183b852006-02-03 16:08:03 +00005749 /* See note for stfs */
5750 storeBE( mkexpr(EA),
5751 unop(Iop_TruncF64asF32, mkexpr(frS)) );
cerion729edb72005-12-02 16:03:46 +00005752 putIReg( rA_addr, mkexpr(EA) );
5753 break;
sewardje14bb9f2005-07-22 09:39:02 +00005754
ceriond953ebb2005-11-29 13:27:20 +00005755 case 0x2D7: // stfdx (Store Float Double Indexed, PPC32 p516)
5756 DIP("stfdx fr%u,r%u,r%u\n", frS_addr, rA_addr, rB_addr);
5757 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
5758 storeBE( mkexpr(EA), mkexpr(frS) );
5759 break;
sewardje14bb9f2005-07-22 09:39:02 +00005760
cerion5b2325f2005-12-23 00:55:09 +00005761 case 0x2F7: // stfdux (Store Float Dbl, Update Indxd, PPC32 p515)
sewardjb183b852006-02-03 16:08:03 +00005762 if (rA_addr == 0)
ceriond953ebb2005-11-29 13:27:20 +00005763 return False;
ceriond953ebb2005-11-29 13:27:20 +00005764 DIP("stfdux fr%u,r%u,r%u\n", frS_addr, rA_addr, rB_addr);
5765 assign( EA, ea_rA_idxd(rA_addr, rB_addr) );
5766 storeBE( mkexpr(EA), mkexpr(frS) );
5767 putIReg( rA_addr, mkexpr(EA) );
5768 break;
sewardj5f63c0c2005-09-09 10:36:55 +00005769
sewardj09e88d12006-01-27 16:05:49 +00005770 case 0x3D7: // stfiwx (Store Float as Int, Indexed, PPC32 p517)
sewardj5117ce12006-01-27 21:20:15 +00005771 // NOTE: POWERPC OPTIONAL, "Graphics Group" (PPC32_GX)
sewardj09e88d12006-01-27 16:05:49 +00005772 DIP("stfiwx fr%u,r%u,r%u\n", frS_addr, rA_addr, rB_addr);
5773 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
5774 storeBE( mkexpr(EA),
5775 unop(Iop_64to32, unop(Iop_ReinterpF64asI64, mkexpr(frS))) );
5776 break;
sewardje14bb9f2005-07-22 09:39:02 +00005777
ceriond953ebb2005-11-29 13:27:20 +00005778 default:
cerion5b2325f2005-12-23 00:55:09 +00005779 vex_printf("dis_fp_store(ppc)(opc2)\n");
ceriond953ebb2005-11-29 13:27:20 +00005780 return False;
sewardje14bb9f2005-07-22 09:39:02 +00005781 }
5782 break;
cerion3d870a32005-03-18 12:23:33 +00005783
5784 default:
cerion5b2325f2005-12-23 00:55:09 +00005785 vex_printf("dis_fp_store(ppc)(opc1)\n");
cerion3d870a32005-03-18 12:23:33 +00005786 return False;
5787 }
5788 return True;
5789}
5790
5791
5792
5793/*
5794 Floating Point Arith Instructions
5795*/
5796static Bool dis_fp_arith ( UInt theInstr )
5797{
5798 /* A-Form */
cerion76de5cf2005-11-18 18:25:12 +00005799 UChar opc1 = ifieldOPC(theInstr);
5800 UChar frD_addr = ifieldRegDS(theInstr);
5801 UChar frA_addr = ifieldRegA(theInstr);
5802 UChar frB_addr = ifieldRegB(theInstr);
5803 UChar frC_addr = ifieldRegC(theInstr);
5804 UChar opc2 = ifieldOPClo5(theInstr);
5805 UChar flag_rC = ifieldBIT0(theInstr);
cerion094d1392005-06-20 13:45:57 +00005806
sewardjb183b852006-02-03 16:08:03 +00005807 IRTemp frD = newTemp(Ity_F64);
5808 IRTemp frA = newTemp(Ity_F64);
5809 IRTemp frB = newTemp(Ity_F64);
5810 IRTemp frC = newTemp(Ity_F64);
5811 IRExpr* rm = get_IR_roundingmode();
5812
5813 /* By default, we will examine the results of the operation and set
5814 fpscr[FPRF] accordingly. */
5815 Bool set_FPRF = True;
5816
5817 /* By default, if flag_RC is set, we will clear cr1 after the
5818 operation. In reality we should set cr1 to indicate the
5819 exception status of the operation, but since we're not
5820 simulating exceptions, the exception status will appear to be
5821 zero. Hence cr1 should be cleared if this is a . form insn. */
5822 Bool clear_CR1 = True;
cerion094d1392005-06-20 13:45:57 +00005823
5824 assign( frA, getFReg(frA_addr));
5825 assign( frB, getFReg(frB_addr));
5826 assign( frC, getFReg(frC_addr));
cerion3d870a32005-03-18 12:23:33 +00005827
5828 switch (opc1) {
sewardje14bb9f2005-07-22 09:39:02 +00005829 case 0x3B:
5830 switch (opc2) {
5831 case 0x12: // fdivs (Floating Divide Single, PPC32 p407)
sewardjb183b852006-02-03 16:08:03 +00005832 if (frC_addr != 0)
sewardje14bb9f2005-07-22 09:39:02 +00005833 return False;
cerion5b2325f2005-12-23 00:55:09 +00005834 DIP("fdivs%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00005835 frD_addr, frA_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00005836 assign( frD, triop( Iop_DivF64r32,
5837 rm, mkexpr(frA), mkexpr(frB) ));
sewardje14bb9f2005-07-22 09:39:02 +00005838 break;
5839
5840 case 0x14: // fsubs (Floating Subtract Single, PPC32 p430)
sewardjb183b852006-02-03 16:08:03 +00005841 if (frC_addr != 0)
sewardje14bb9f2005-07-22 09:39:02 +00005842 return False;
cerion5b2325f2005-12-23 00:55:09 +00005843 DIP("fsubs%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00005844 frD_addr, frA_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00005845 assign( frD, triop( Iop_SubF64r32,
5846 rm, mkexpr(frA), mkexpr(frB) ));
sewardje14bb9f2005-07-22 09:39:02 +00005847 break;
5848
5849 case 0x15: // fadds (Floating Add Single, PPC32 p401)
sewardjb183b852006-02-03 16:08:03 +00005850 if (frC_addr != 0)
sewardje14bb9f2005-07-22 09:39:02 +00005851 return False;
cerion5b2325f2005-12-23 00:55:09 +00005852 DIP("fadds%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00005853 frD_addr, frA_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00005854 assign( frD, triop( Iop_AddF64r32,
5855 rm, mkexpr(frA), mkexpr(frB) ));
sewardje14bb9f2005-07-22 09:39:02 +00005856 break;
5857
sewardj870c84b2006-01-24 03:33:43 +00005858 case 0x16: // fsqrts (Floating SqRt (Single-Precision), PPC32 p428)
sewardj5117ce12006-01-27 21:20:15 +00005859 // NOTE: POWERPC OPTIONAL, "General-Purpose Group" (PPC32_FX)
sewardjb183b852006-02-03 16:08:03 +00005860 if (frA_addr != 0 || frC_addr != 0)
sewardj870c84b2006-01-24 03:33:43 +00005861 return False;
sewardj870c84b2006-01-24 03:33:43 +00005862 DIP("fsqrts%s fr%u,fr%u\n", flag_rC ? ".":"",
5863 frD_addr, frB_addr);
sewardj79fd33f2006-01-29 17:07:57 +00005864 // however illogically, on ppc970 this insn behaves identically
sewardjb183b852006-02-03 16:08:03 +00005865 // to fsqrt (double-precision). So use SqrtF64, not SqrtF64r32.
5866 assign( frD, binop( Iop_SqrtF64, rm, mkexpr(frB) ));
sewardj870c84b2006-01-24 03:33:43 +00005867 break;
sewardje14bb9f2005-07-22 09:39:02 +00005868
sewardjbaf971a2006-01-27 15:09:35 +00005869 case 0x18: // fres (Floating Reciprocal Estimate Single, PPC32 p421)
sewardj5117ce12006-01-27 21:20:15 +00005870 // NOTE: POWERPC OPTIONAL, "Graphics Group" (PPC32_GX)
sewardjb183b852006-02-03 16:08:03 +00005871 if (frA_addr != 0 || frC_addr != 0)
sewardjbaf971a2006-01-27 15:09:35 +00005872 return False;
sewardjbaf971a2006-01-27 15:09:35 +00005873 DIP("fres%s fr%u,fr%u\n", flag_rC ? ".":"",
5874 frD_addr, frB_addr);
sewardj157b19b2006-01-31 16:32:25 +00005875 { IRExpr* ieee_one
5876 = IRExpr_Const(IRConst_F64i(0x3ff0000000000000ULL));
sewardjb183b852006-02-03 16:08:03 +00005877 assign( frD, triop( Iop_DivF64r32,
5878 rm,
5879 ieee_one, mkexpr(frB) ));
sewardj157b19b2006-01-31 16:32:25 +00005880 }
sewardjbaf971a2006-01-27 15:09:35 +00005881 break;
sewardje14bb9f2005-07-22 09:39:02 +00005882
5883 case 0x19: // fmuls (Floating Multiply Single, PPC32 p414)
sewardjb183b852006-02-03 16:08:03 +00005884 if (frB_addr != 0)
sewardje14bb9f2005-07-22 09:39:02 +00005885 return False;
cerion5b2325f2005-12-23 00:55:09 +00005886 DIP("fmuls%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00005887 frD_addr, frA_addr, frC_addr);
sewardjb183b852006-02-03 16:08:03 +00005888 assign( frD, triop( Iop_MulF64r32,
5889 rm, mkexpr(frA), mkexpr(frC) ));
sewardje14bb9f2005-07-22 09:39:02 +00005890 break;
5891
sewardj79fd33f2006-01-29 17:07:57 +00005892 case 0x1A: // frsqrtes (Floating Recip SqRt Est Single)
5893 // NOTE: POWERPC OPTIONAL, "Graphics Group" (PPC32_GX)
5894 // Undocumented instruction?
sewardjb183b852006-02-03 16:08:03 +00005895 if (frA_addr != 0 || frC_addr != 0)
sewardj79fd33f2006-01-29 17:07:57 +00005896 return False;
sewardj79fd33f2006-01-29 17:07:57 +00005897 DIP("frsqrtes%s fr%u,fr%u\n", flag_rC ? ".":"",
5898 frD_addr, frB_addr);
5899 assign( frD, unop(Iop_Est5FRSqrt, mkexpr(frB)) );
5900 break;
5901
sewardje14bb9f2005-07-22 09:39:02 +00005902 default:
cerion5b2325f2005-12-23 00:55:09 +00005903 vex_printf("dis_fp_arith(ppc)(3B: opc2)\n");
sewardje14bb9f2005-07-22 09:39:02 +00005904 return False;
5905 }
5906 break;
cerion094d1392005-06-20 13:45:57 +00005907
cerion3d870a32005-03-18 12:23:33 +00005908 case 0x3F:
5909 switch (opc2) {
cerion5b2325f2005-12-23 00:55:09 +00005910 case 0x12: // fdiv (Floating Div (Double-Precision), PPC32 p406)
sewardjb183b852006-02-03 16:08:03 +00005911 if (frC_addr != 0)
sewardje14bb9f2005-07-22 09:39:02 +00005912 return False;
cerion5b2325f2005-12-23 00:55:09 +00005913 DIP("fdiv%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00005914 frD_addr, frA_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00005915 assign( frD, triop(Iop_DivF64, rm, mkexpr(frA), mkexpr(frB)) );
sewardje14bb9f2005-07-22 09:39:02 +00005916 break;
5917
cerion5b2325f2005-12-23 00:55:09 +00005918 case 0x14: // fsub (Floating Sub (Double-Precision), PPC32 p429)
sewardjb183b852006-02-03 16:08:03 +00005919 if (frC_addr != 0)
sewardje14bb9f2005-07-22 09:39:02 +00005920 return False;
cerion5b2325f2005-12-23 00:55:09 +00005921 DIP("fsub%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00005922 frD_addr, frA_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00005923 assign( frD, triop(Iop_SubF64, rm, mkexpr(frA), mkexpr(frB)) );
sewardje14bb9f2005-07-22 09:39:02 +00005924 break;
cerion3d870a32005-03-18 12:23:33 +00005925
5926 case 0x15: // fadd (Floating Add (Double-Precision), PPC32 p400)
sewardjb183b852006-02-03 16:08:03 +00005927 if (frC_addr != 0)
cerion3d870a32005-03-18 12:23:33 +00005928 return False;
cerion5b2325f2005-12-23 00:55:09 +00005929 DIP("fadd%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
cerion3d870a32005-03-18 12:23:33 +00005930 frD_addr, frA_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00005931 assign( frD, triop(Iop_AddF64, rm, mkexpr(frA), mkexpr(frB)) );
cerion094d1392005-06-20 13:45:57 +00005932 break;
cerion3d870a32005-03-18 12:23:33 +00005933
cerion876ef412005-12-14 22:00:53 +00005934 case 0x16: // fsqrt (Floating SqRt (Double-Precision), PPC32 p427)
sewardj5117ce12006-01-27 21:20:15 +00005935 // NOTE: POWERPC OPTIONAL, "General-Purpose Group" (PPC32_FX)
sewardjb183b852006-02-03 16:08:03 +00005936 if (frA_addr != 0 || frC_addr != 0)
cerion876ef412005-12-14 22:00:53 +00005937 return False;
cerion5b2325f2005-12-23 00:55:09 +00005938 DIP("fsqrt%s fr%u,fr%u\n", flag_rC ? ".":"",
cerion876ef412005-12-14 22:00:53 +00005939 frD_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00005940 assign( frD, binop(Iop_SqrtF64, rm, mkexpr(frB)) );
cerion876ef412005-12-14 22:00:53 +00005941 break;
sewardje14bb9f2005-07-22 09:39:02 +00005942
5943 case 0x17: { // fsel (Floating Select, PPC32 p426)
sewardj5117ce12006-01-27 21:20:15 +00005944 // NOTE: POWERPC OPTIONAL, "Graphics Group" (PPC32_GX)
sewardje14bb9f2005-07-22 09:39:02 +00005945 IRTemp cc = newTemp(Ity_I32);
5946 IRTemp cc_b0 = newTemp(Ity_I32);
5947
cerion5b2325f2005-12-23 00:55:09 +00005948 DIP("fsel%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00005949 frD_addr, frA_addr, frC_addr, frB_addr);
5950
5951 // cc: UN == 0x41, LT == 0x01, GT == 0x00, EQ == 0x40
5952 // => GT|EQ == (cc & 0x1 == 0)
cerion5b2325f2005-12-23 00:55:09 +00005953 assign( cc, binop(Iop_CmpF64, mkexpr(frA),
5954 IRExpr_Const(IRConst_F64(0))) );
sewardje14bb9f2005-07-22 09:39:02 +00005955 assign( cc_b0, binop(Iop_And32, mkexpr(cc), mkU32(1)) );
5956
5957 // frD = (frA >= 0.0) ? frC : frB
5958 // = (cc_b0 == 0) ? frC : frB
5959 assign( frD,
5960 IRExpr_Mux0X(
5961 unop(Iop_1Uto8,
5962 binop(Iop_CmpEQ32, mkexpr(cc_b0), mkU32(0))),
5963 mkexpr(frB),
5964 mkexpr(frC) ));
sewardjb183b852006-02-03 16:08:03 +00005965
5966 /* One of the rare ones which don't mess with FPRF */
5967 set_FPRF = False;
sewardje14bb9f2005-07-22 09:39:02 +00005968 break;
5969 }
5970
sewardj79fd33f2006-01-29 17:07:57 +00005971 case 0x18: // fre (Floating Reciprocal Estimate)
5972 // NOTE: POWERPC OPTIONAL, "Graphics Group" (PPC32_GX)
5973 // Note: unclear whether this insn really exists or not
5974 // ppc970 doesn't have it, but POWER5 does
sewardjb183b852006-02-03 16:08:03 +00005975 if (frA_addr != 0 || frC_addr != 0)
sewardj79fd33f2006-01-29 17:07:57 +00005976 return False;
sewardj79fd33f2006-01-29 17:07:57 +00005977 DIP("fre%s fr%u,fr%u\n", flag_rC ? ".":"",
5978 frD_addr, frB_addr);
sewardj157b19b2006-01-31 16:32:25 +00005979 { IRExpr* ieee_one
5980 = IRExpr_Const(IRConst_F64i(0x3ff0000000000000ULL));
sewardjb183b852006-02-03 16:08:03 +00005981 assign( frD, triop( Iop_DivF64,
sewardj56de4212006-02-06 22:19:17 +00005982 rm,
sewardjb183b852006-02-03 16:08:03 +00005983 ieee_one, mkexpr(frB) ));
sewardj157b19b2006-01-31 16:32:25 +00005984 }
sewardj79fd33f2006-01-29 17:07:57 +00005985 break;
5986
cerion5b2325f2005-12-23 00:55:09 +00005987 case 0x19: // fmul (Floating Mult (Double Precision), PPC32 p413)
sewardjb183b852006-02-03 16:08:03 +00005988 if (frB_addr != 0)
cerion5b2325f2005-12-23 00:55:09 +00005989 vex_printf("dis_fp_arith(ppc)(instr,fmul)\n");
cerion5b2325f2005-12-23 00:55:09 +00005990 DIP("fmul%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00005991 frD_addr, frA_addr, frC_addr);
sewardjb183b852006-02-03 16:08:03 +00005992 assign( frD, triop(Iop_MulF64, rm, mkexpr(frA), mkexpr(frC)) );
sewardje14bb9f2005-07-22 09:39:02 +00005993 break;
5994
sewardjbaf971a2006-01-27 15:09:35 +00005995 case 0x1A: // frsqrte (Floating Recip SqRt Est., PPC32 p424)
sewardj5117ce12006-01-27 21:20:15 +00005996 // NOTE: POWERPC OPTIONAL, "Graphics Group" (PPC32_GX)
sewardjb183b852006-02-03 16:08:03 +00005997 if (frA_addr != 0 || frC_addr != 0)
sewardjbaf971a2006-01-27 15:09:35 +00005998 return False;
sewardjbaf971a2006-01-27 15:09:35 +00005999 DIP("frsqrte%s fr%u,fr%u\n", flag_rC ? ".":"",
6000 frD_addr, frB_addr);
6001 assign( frD, unop(Iop_Est5FRSqrt, mkexpr(frB)) );
6002 break;
cerion3d870a32005-03-18 12:23:33 +00006003
6004 default:
cerion5b2325f2005-12-23 00:55:09 +00006005 vex_printf("dis_fp_arith(ppc)(3F: opc2)\n");
cerion3d870a32005-03-18 12:23:33 +00006006 return False;
6007 }
cerion094d1392005-06-20 13:45:57 +00006008 break;
6009
cerion3d870a32005-03-18 12:23:33 +00006010 default:
cerion5b2325f2005-12-23 00:55:09 +00006011 vex_printf("dis_fp_arith(ppc)(opc1)\n");
cerion3d870a32005-03-18 12:23:33 +00006012 return False;
6013 }
cerion094d1392005-06-20 13:45:57 +00006014
6015 putFReg( frD_addr, mkexpr(frD) );
sewardjb183b852006-02-03 16:08:03 +00006016
6017 if (set_FPRF) {
6018 // XXX XXX XXX FIXME
6019 // set FPRF from frD
6020 }
6021
6022 if (flag_rC && clear_CR1) {
6023 putCR321( 1, mkU8(0) );
6024 putCR0( 1, mkU8(0) );
6025 }
6026
cerion3d870a32005-03-18 12:23:33 +00006027 return True;
6028}
6029
6030
6031
sewardje14bb9f2005-07-22 09:39:02 +00006032/*
6033 Floating Point Mult-Add Instructions
6034*/
6035static Bool dis_fp_multadd ( UInt theInstr )
6036{
6037 /* A-Form */
cerion76de5cf2005-11-18 18:25:12 +00006038 UChar opc1 = ifieldOPC(theInstr);
6039 UChar frD_addr = ifieldRegDS(theInstr);
6040 UChar frA_addr = ifieldRegA(theInstr);
6041 UChar frB_addr = ifieldRegB(theInstr);
6042 UChar frC_addr = ifieldRegC(theInstr);
6043 UChar opc2 = ifieldOPClo5(theInstr);
6044 UChar flag_rC = ifieldBIT0(theInstr);
sewardje14bb9f2005-07-22 09:39:02 +00006045
sewardjb183b852006-02-03 16:08:03 +00006046 IRTemp frD = newTemp(Ity_F64);
6047 IRTemp frA = newTemp(Ity_F64);
6048 IRTemp frB = newTemp(Ity_F64);
6049 IRTemp frC = newTemp(Ity_F64);
6050 IRTemp rmt = newTemp(Ity_I32);
6051 IRExpr* rm;
6052
6053 /* By default, we will examine the results of the operation and set
6054 fpscr[FPRF] accordingly. */
6055 Bool set_FPRF = True;
6056
6057 /* By default, if flag_RC is set, we will clear cr1 after the
6058 operation. In reality we should set cr1 to indicate the
6059 exception status of the operation, but since we're not
6060 simulating exceptions, the exception status will appear to be
6061 zero. Hence cr1 should be cleared if this is a . form insn. */
6062 Bool clear_CR1 = True;
6063
6064 /* Bind the rounding mode expression to a temp; there's no
6065 point in creating gratuitous CSEs, as we know we'll need
6066 to use it twice. */
6067 assign( rmt, get_IR_roundingmode() );
6068 rm = mkexpr(rmt);
sewardje14bb9f2005-07-22 09:39:02 +00006069
6070 assign( frA, getFReg(frA_addr));
6071 assign( frB, getFReg(frB_addr));
6072 assign( frC, getFReg(frC_addr));
6073
sewardjb183b852006-02-03 16:08:03 +00006074 /* The rounding in this is all a bit dodgy. The idea is to only do
6075 one rounding. That clearly isn't achieveable without dedicated
6076 four-input IR primops, although in the single precision case we
6077 can sort-of simulate it by doing the inner multiply in double
6078 precision.
6079
6080 In the negated cases, the negation happens after rounding. */
6081
sewardje14bb9f2005-07-22 09:39:02 +00006082 switch (opc1) {
6083 case 0x3B:
6084 switch (opc2) {
6085 case 0x1C: // fmsubs (Floating Mult-Subtr Single, PPC32 p412)
cerion5b2325f2005-12-23 00:55:09 +00006086 DIP("fmsubs%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00006087 frD_addr, frA_addr, frC_addr, frB_addr);
sewardj40c80262006-02-08 19:30:46 +00006088 assign( frD, qop( Iop_MSubF64r32, rm,
6089 mkexpr(frA), mkexpr(frC), mkexpr(frB) ));
sewardjb183b852006-02-03 16:08:03 +00006090 break;
sewardje14bb9f2005-07-22 09:39:02 +00006091
6092 case 0x1D: // fmadds (Floating Mult-Add Single, PPC32 p409)
cerion5b2325f2005-12-23 00:55:09 +00006093 DIP("fmadds%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00006094 frD_addr, frA_addr, frC_addr, frB_addr);
sewardj40c80262006-02-08 19:30:46 +00006095 assign( frD, qop( Iop_MAddF64r32, rm,
6096 mkexpr(frA), mkexpr(frC), mkexpr(frB) ));
sewardje14bb9f2005-07-22 09:39:02 +00006097 break;
6098
6099 case 0x1E: // fnmsubs (Float Neg Mult-Subtr Single, PPC32 p420)
cerion5b2325f2005-12-23 00:55:09 +00006100 DIP("fnmsubs%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00006101 frD_addr, frA_addr, frC_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00006102 assign( frD, unop( Iop_NegF64,
sewardj40c80262006-02-08 19:30:46 +00006103 qop( Iop_MSubF64r32, rm,
6104 mkexpr(frA), mkexpr(frC), mkexpr(frB) )));
sewardje14bb9f2005-07-22 09:39:02 +00006105 break;
6106
6107 case 0x1F: // fnmadds (Floating Negative Multiply-Add Single, PPC32 p418)
cerion5b2325f2005-12-23 00:55:09 +00006108 DIP("fnmadds%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00006109 frD_addr, frA_addr, frC_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00006110 assign( frD, unop( Iop_NegF64,
sewardj40c80262006-02-08 19:30:46 +00006111 qop( Iop_MAddF64r32, rm,
6112 mkexpr(frA), mkexpr(frC), mkexpr(frB) )));
sewardje14bb9f2005-07-22 09:39:02 +00006113 break;
6114
6115 default:
cerion5b2325f2005-12-23 00:55:09 +00006116 vex_printf("dis_fp_multadd(ppc)(3B: opc2)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006117 return False;
6118 }
6119 break;
6120
6121 case 0x3F:
6122 switch (opc2) {
cerion5b2325f2005-12-23 00:55:09 +00006123 case 0x1C: // fmsub (Float Mult-Sub (Dbl Precision), PPC32 p411)
6124 DIP("fmsub%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00006125 frD_addr, frA_addr, frC_addr, frB_addr);
sewardj40c80262006-02-08 19:30:46 +00006126 assign( frD, qop( Iop_MSubF64, rm,
6127 mkexpr(frA), mkexpr(frC), mkexpr(frB) ));
sewardje14bb9f2005-07-22 09:39:02 +00006128 break;
6129
cerion5b2325f2005-12-23 00:55:09 +00006130 case 0x1D: // fmadd (Float Mult-Add (Dbl Precision), PPC32 p408)
6131 DIP("fmadd%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00006132 frD_addr, frA_addr, frC_addr, frB_addr);
sewardj40c80262006-02-08 19:30:46 +00006133 assign( frD, qop( Iop_MAddF64, rm,
6134 mkexpr(frA), mkexpr(frC), mkexpr(frB) ));
sewardje14bb9f2005-07-22 09:39:02 +00006135 break;
6136
cerion5b2325f2005-12-23 00:55:09 +00006137 case 0x1E: // fnmsub (Float Neg Mult-Subtr (Dbl Precision), PPC32 p419)
6138 DIP("fnmsub%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardj0e2cc672005-07-29 21:58:51 +00006139 frD_addr, frA_addr, frC_addr, frB_addr);
6140 assign( frD, unop( Iop_NegF64,
sewardj40c80262006-02-08 19:30:46 +00006141 qop( Iop_MSubF64, rm,
6142 mkexpr(frA), mkexpr(frC), mkexpr(frB) )));
sewardj0e2cc672005-07-29 21:58:51 +00006143 break;
6144
cerion5b2325f2005-12-23 00:55:09 +00006145 case 0x1F: // fnmadd (Float Neg Mult-Add (Dbl Precision), PPC32 p417)
6146 DIP("fnmadd%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardj0e2cc672005-07-29 21:58:51 +00006147 frD_addr, frA_addr, frC_addr, frB_addr);
6148 assign( frD, unop( Iop_NegF64,
sewardj40c80262006-02-08 19:30:46 +00006149 qop( Iop_MAddF64, rm,
6150 mkexpr(frA), mkexpr(frC), mkexpr(frB) )));
sewardj0e2cc672005-07-29 21:58:51 +00006151 break;
sewardje14bb9f2005-07-22 09:39:02 +00006152
6153 default:
cerion5b2325f2005-12-23 00:55:09 +00006154 vex_printf("dis_fp_multadd(ppc)(3F: opc2)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006155 return False;
6156 }
6157 break;
6158
6159 default:
cerion5b2325f2005-12-23 00:55:09 +00006160 vex_printf("dis_fp_multadd(ppc)(opc1)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006161 return False;
6162 }
6163
6164 putFReg( frD_addr, mkexpr(frD) );
sewardjb183b852006-02-03 16:08:03 +00006165
6166 if (set_FPRF) {
6167 // XXX XXX XXX FIXME
6168 // set FPRF from frD
6169 }
6170
6171 if (flag_rC && clear_CR1) {
6172 putCR321( 1, mkU8(0) );
6173 putCR0( 1, mkU8(0) );
6174 }
6175
sewardje14bb9f2005-07-22 09:39:02 +00006176 return True;
6177}
6178
6179
6180
6181/*
6182 Floating Point Compare Instructions
6183*/
6184static Bool dis_fp_cmp ( UInt theInstr )
6185{
6186 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00006187 UChar opc1 = ifieldOPC(theInstr);
6188 UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) );
6189 UChar b21to22 = toUChar( IFIELD( theInstr, 21, 2 ) );
6190 UChar frA_addr = ifieldRegA(theInstr);
6191 UChar frB_addr = ifieldRegB(theInstr);
6192 UInt opc2 = ifieldOPClo10(theInstr);
6193 UChar b0 = ifieldBIT0(theInstr);
sewardje14bb9f2005-07-22 09:39:02 +00006194
6195 IRTemp ccIR = newTemp(Ity_I32);
6196 IRTemp ccPPC32 = newTemp(Ity_I32);
6197
sewardje14bb9f2005-07-22 09:39:02 +00006198 IRTemp frA = newTemp(Ity_F64);
6199 IRTemp frB = newTemp(Ity_F64);
sewardje14bb9f2005-07-22 09:39:02 +00006200
6201 if (opc1 != 0x3F || b21to22 != 0 || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006202 vex_printf("dis_fp_cmp(ppc)(instr)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006203 return False;
6204 }
6205
6206 assign( frA, getFReg(frA_addr));
6207 assign( frB, getFReg(frB_addr));
6208
6209 assign( ccIR, binop(Iop_CmpF64, mkexpr(frA), mkexpr(frB)) );
6210
6211 /* Map compare result from IR to PPC32 */
6212 /*
6213 FP cmp result | PPC | IR
6214 --------------------------
6215 UN | 0x1 | 0x45
6216 EQ | 0x2 | 0x40
6217 GT | 0x4 | 0x00
6218 LT | 0x8 | 0x01
6219 */
6220
sewardjb183b852006-02-03 16:08:03 +00006221 // ccPPC32 = Shl(1, (~(ccIR>>5) & 2)
6222 // | ((ccIR ^ (ccIR>>6)) & 1)
sewardje14bb9f2005-07-22 09:39:02 +00006223 assign(
6224 ccPPC32,
sewardjb183b852006-02-03 16:08:03 +00006225 binop(
6226 Iop_Shl32,
6227 mkU32(1),
6228 unop(
6229 Iop_32to8,
6230 binop(
6231 Iop_Or32,
6232 binop(
6233 Iop_And32,
6234 unop(
6235 Iop_Not32,
6236 binop(Iop_Shr32, mkexpr(ccIR), mkU8(5))
6237 ),
6238 mkU32(2)
6239 ),
6240 binop(
6241 Iop_And32,
6242 binop(
6243 Iop_Xor32,
6244 mkexpr(ccIR),
6245 binop(Iop_Shr32, mkexpr(ccIR), mkU8(6))
6246 ),
6247 mkU32(1)
6248 )
6249 )
6250 )
6251 )
sewardje14bb9f2005-07-22 09:39:02 +00006252 );
6253
ceriond953ebb2005-11-29 13:27:20 +00006254 putGST_field( PPC_GST_CR, mkexpr(ccPPC32), crfD );
sewardje14bb9f2005-07-22 09:39:02 +00006255
cerionedf7fc52005-11-18 20:57:41 +00006256 /* CAB: TODO?: Support writing cc to FPSCR->FPCC ?
ceriond953ebb2005-11-29 13:27:20 +00006257 putGST_field( PPC_GST_FPSCR, mkexpr(ccPPC32), 4 );
cerionedf7fc52005-11-18 20:57:41 +00006258 */
sewardjb183b852006-02-03 16:08:03 +00006259 // XXX XXX XXX FIXME
6260 // Also write the result into FPRF (it's not entirely clear how)
sewardje14bb9f2005-07-22 09:39:02 +00006261
cerionedf7fc52005-11-18 20:57:41 +00006262 /* Note: Differences between fcmpu and fcmpo are only in exception
6263 flag settings, which aren't supported anyway. */
sewardje14bb9f2005-07-22 09:39:02 +00006264 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00006265 case 0x000: // fcmpu (Floating Compare Unordered, PPC32 p403)
6266 DIP("fcmpu crf%d,fr%u,fr%u\n", crfD, frA_addr, frB_addr);
6267 break;
6268 case 0x020: // fcmpo (Floating Compare Ordered, PPC32 p402)
6269 DIP("fcmpo crf%d,fr%u,fr%u\n", crfD, frA_addr, frB_addr);
6270 break;
6271 default:
cerion5b2325f2005-12-23 00:55:09 +00006272 vex_printf("dis_fp_cmp(ppc)(opc2)\n");
ceriond953ebb2005-11-29 13:27:20 +00006273 return False;
sewardje14bb9f2005-07-22 09:39:02 +00006274 }
6275 return True;
6276}
6277
6278
6279
6280/*
6281 Floating Point Rounding/Conversion Instructions
6282*/
6283static Bool dis_fp_round ( UInt theInstr )
6284{
6285 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00006286 UChar opc1 = ifieldOPC(theInstr);
6287 UChar frD_addr = ifieldRegDS(theInstr);
6288 UChar b16to20 = ifieldRegA(theInstr);
6289 UChar frB_addr = ifieldRegB(theInstr);
6290 UInt opc2 = ifieldOPClo10(theInstr);
6291 UChar flag_rC = ifieldBIT0(theInstr);
sewardje14bb9f2005-07-22 09:39:02 +00006292
sewardjb183b852006-02-03 16:08:03 +00006293 IRTemp frD = newTemp(Ity_F64);
6294 IRTemp frB = newTemp(Ity_F64);
6295 IRTemp r_tmp32 = newTemp(Ity_I32);
6296 IRTemp r_tmp64 = newTemp(Ity_I64);
6297 IRExpr* rm = get_IR_roundingmode();
sewardje14bb9f2005-07-22 09:39:02 +00006298
sewardjb183b852006-02-03 16:08:03 +00006299 /* By default, we will examine the results of the operation and set
6300 fpscr[FPRF] accordingly. */
6301 Bool set_FPRF = True;
6302
6303 /* By default, if flag_RC is set, we will clear cr1 after the
6304 operation. In reality we should set cr1 to indicate the
6305 exception status of the operation, but since we're not
6306 simulating exceptions, the exception status will appear to be
6307 zero. Hence cr1 should be cleared if this is a . form insn. */
6308 Bool clear_CR1 = True;
6309
sewardje14bb9f2005-07-22 09:39:02 +00006310 if (opc1 != 0x3F || b16to20 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006311 vex_printf("dis_fp_round(ppc)(instr)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006312 return False;
6313 }
6314
6315 assign( frB, getFReg(frB_addr));
6316
sewardje14bb9f2005-07-22 09:39:02 +00006317 switch (opc2) {
cerionf0de28c2005-12-13 20:21:11 +00006318 case 0x00C: // frsp (Float Round to Single, PPC32 p423)
cerion5b2325f2005-12-23 00:55:09 +00006319 DIP("frsp%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00006320 assign( frD, binop( Iop_RoundF64toF32, rm, mkexpr(frB) ));
ceriond953ebb2005-11-29 13:27:20 +00006321 break;
6322
cerionf0de28c2005-12-13 20:21:11 +00006323 case 0x00E: // fctiw (Float Conv to Int, PPC32 p404)
cerion5b2325f2005-12-23 00:55:09 +00006324 DIP("fctiw%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
6325 assign( r_tmp32,
sewardjb183b852006-02-03 16:08:03 +00006326 binop(Iop_F64toI32, rm, mkexpr(frB)) );
ceriond953ebb2005-11-29 13:27:20 +00006327 assign( frD, unop( Iop_ReinterpI64asF64,
cerion07b07a92005-12-22 14:32:35 +00006328 unop( Iop_32Uto64, mkexpr(r_tmp32))));
sewardjb183b852006-02-03 16:08:03 +00006329 /* FPRF is undefined after fctiw. Leave unchanged. */
6330 set_FPRF = False;
ceriond953ebb2005-11-29 13:27:20 +00006331 break;
6332
cerionf0de28c2005-12-13 20:21:11 +00006333 case 0x00F: // fctiwz (Float Conv to Int, Round to Zero, PPC32 p405)
cerion5b2325f2005-12-23 00:55:09 +00006334 DIP("fctiwz%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00006335 assign( r_tmp32,
6336 binop(Iop_F64toI32, mkU32(Irrm_ZERO), mkexpr(frB) ));
ceriond953ebb2005-11-29 13:27:20 +00006337 assign( frD, unop( Iop_ReinterpI64asF64,
cerion07b07a92005-12-22 14:32:35 +00006338 unop( Iop_32Uto64, mkexpr(r_tmp32))));
sewardjb183b852006-02-03 16:08:03 +00006339 /* FPRF is undefined after fctiwz. Leave unchanged. */
6340 set_FPRF = False;
ceriond953ebb2005-11-29 13:27:20 +00006341 break;
cerionf0de28c2005-12-13 20:21:11 +00006342
cerion5b2325f2005-12-23 00:55:09 +00006343 case 0x32E: // fctid (Float Conv to Int DWord, PPC64 p437)
6344 DIP("fctid%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
6345 assign( r_tmp64,
sewardjb183b852006-02-03 16:08:03 +00006346 binop(Iop_F64toI64, rm, mkexpr(frB)) );
cerion07b07a92005-12-22 14:32:35 +00006347 assign( frD, unop( Iop_ReinterpI64asF64, mkexpr(r_tmp64)) );
sewardjb183b852006-02-03 16:08:03 +00006348 /* FPRF is undefined after fctid. Leave unchanged. */
6349 set_FPRF = False;
cerion07b07a92005-12-22 14:32:35 +00006350 break;
cerionf0de28c2005-12-13 20:21:11 +00006351
cerion5b2325f2005-12-23 00:55:09 +00006352 case 0x32F: // fctidz (Float Conv to Int DWord, Round to Zero, PPC64 p437)
6353 DIP("fctidz%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00006354 assign( r_tmp64,
6355 binop(Iop_F64toI64, mkU32(Irrm_ZERO), mkexpr(frB)) );
cerion07b07a92005-12-22 14:32:35 +00006356 assign( frD, unop( Iop_ReinterpI64asF64, mkexpr(r_tmp64)) );
sewardjb183b852006-02-03 16:08:03 +00006357 /* FPRF is undefined after fctidz. Leave unchanged. */
6358 set_FPRF = False;
cerion07b07a92005-12-22 14:32:35 +00006359 break;
cerionf0de28c2005-12-13 20:21:11 +00006360
cerion5b2325f2005-12-23 00:55:09 +00006361 case 0x34E: // fcfid (Float Conv from Int DWord, PPC64 p434)
6362 DIP("fcfid%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
cerion07b07a92005-12-22 14:32:35 +00006363 assign( r_tmp64, unop( Iop_ReinterpF64asI64, mkexpr(frB)) );
sewardjb183b852006-02-03 16:08:03 +00006364 assign( frD,
6365 binop(Iop_I64toF64, rm, mkexpr(r_tmp64)) );
cerion07b07a92005-12-22 14:32:35 +00006366 break;
cerionf0de28c2005-12-13 20:21:11 +00006367
ceriond953ebb2005-11-29 13:27:20 +00006368 default:
cerion5b2325f2005-12-23 00:55:09 +00006369 vex_printf("dis_fp_round(ppc)(opc2)\n");
ceriond953ebb2005-11-29 13:27:20 +00006370 return False;
sewardje14bb9f2005-07-22 09:39:02 +00006371 }
6372
6373 putFReg( frD_addr, mkexpr(frD) );
sewardjb183b852006-02-03 16:08:03 +00006374
6375 if (set_FPRF) {
6376 // XXX XXX XXX FIXME
6377 // set FPRF from frD
6378 }
6379
6380 if (flag_rC && clear_CR1) {
6381 putCR321( 1, mkU8(0) );
6382 putCR0( 1, mkU8(0) );
6383 }
6384
sewardje14bb9f2005-07-22 09:39:02 +00006385 return True;
6386}
6387
6388
6389
6390/*
6391 Floating Point Move Instructions
6392*/
6393static Bool dis_fp_move ( UInt theInstr )
6394{
6395 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00006396 UChar opc1 = ifieldOPC(theInstr);
6397 UChar frD_addr = ifieldRegDS(theInstr);
6398 UChar b16to20 = ifieldRegA(theInstr);
6399 UChar frB_addr = ifieldRegB(theInstr);
6400 UInt opc2 = ifieldOPClo10(theInstr);
6401 UChar flag_rC = ifieldBIT0(theInstr);
sewardje14bb9f2005-07-22 09:39:02 +00006402
6403 IRTemp frD = newTemp(Ity_F64);
6404 IRTemp frB = newTemp(Ity_F64);
6405
6406 if (opc1 != 0x3F || b16to20 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006407 vex_printf("dis_fp_move(ppc)(instr)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006408 return False;
6409 }
6410
6411 assign( frB, getFReg(frB_addr));
6412
sewardje14bb9f2005-07-22 09:39:02 +00006413 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00006414 case 0x028: // fneg (Floating Negate, PPC32 p416)
cerion5b2325f2005-12-23 00:55:09 +00006415 DIP("fneg%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
ceriond953ebb2005-11-29 13:27:20 +00006416 assign( frD, unop( Iop_NegF64, mkexpr(frB) ));
6417 break;
6418
6419 case 0x048: // fmr (Floating Move Register, PPC32 p410)
cerion5b2325f2005-12-23 00:55:09 +00006420 DIP("fmr%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
ceriond953ebb2005-11-29 13:27:20 +00006421 assign( frD, mkexpr(frB) );
6422 break;
6423
6424 case 0x088: // fnabs (Floating Negative Absolute Value, PPC32 p415)
cerion5b2325f2005-12-23 00:55:09 +00006425 DIP("fnabs%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
ceriond953ebb2005-11-29 13:27:20 +00006426 assign( frD, unop( Iop_NegF64, unop( Iop_AbsF64, mkexpr(frB) )));
6427 break;
6428
6429 case 0x108: // fabs (Floating Absolute Value, PPC32 p399)
cerion5b2325f2005-12-23 00:55:09 +00006430 DIP("fabs%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
ceriond953ebb2005-11-29 13:27:20 +00006431 assign( frD, unop( Iop_AbsF64, mkexpr(frB) ));
6432 break;
6433
6434 default:
cerion5b2325f2005-12-23 00:55:09 +00006435 vex_printf("dis_fp_move(ppc)(opc2)\n");
ceriond953ebb2005-11-29 13:27:20 +00006436 return False;
sewardje14bb9f2005-07-22 09:39:02 +00006437 }
6438
6439 putFReg( frD_addr, mkexpr(frD) );
sewardjb183b852006-02-03 16:08:03 +00006440
6441 /* None of these change FPRF. cr1 is set in the usual way though,
6442 if flag_rC is set. */
6443
6444 if (flag_rC) {
6445 putCR321( 1, mkU8(0) );
6446 putCR0( 1, mkU8(0) );
6447 }
6448
sewardje14bb9f2005-07-22 09:39:02 +00006449 return True;
6450}
6451
6452
6453
6454/*
6455 Floating Point Status/Control Register Instructions
6456*/
6457static Bool dis_fp_scr ( UInt theInstr )
6458{
cerion76de5cf2005-11-18 18:25:12 +00006459 /* Many forms - see each switch case */
6460 UChar opc1 = ifieldOPC(theInstr);
6461 UInt opc2 = ifieldOPClo10(theInstr);
6462 UChar flag_rC = ifieldBIT0(theInstr);
sewardje14bb9f2005-07-22 09:39:02 +00006463
6464 if (opc1 != 0x3F) {
cerion5b2325f2005-12-23 00:55:09 +00006465 vex_printf("dis_fp_scr(ppc)(instr)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006466 return False;
6467 }
6468
6469 switch (opc2) {
cerion3ea49ee2006-01-04 10:53:00 +00006470 case 0x026: { // mtfsb1 (Move to FPSCR Bit 1, PPC32 p479)
6471 // Bit crbD of the FPSCR is set.
6472 UChar crbD = ifieldRegDS(theInstr);
6473 UInt b11to20 = IFIELD(theInstr, 11, 10);
6474
6475 if (b11to20 != 0) {
6476 vex_printf("dis_fp_scr(ppc)(instr,mtfsb1)\n");
6477 return False;
6478 }
6479 DIP("mtfsb1%s crb%d \n", flag_rC ? ".":"", crbD);
6480 putGST_masked( PPC_GST_FPSCR, mkU32(1<<(31-crbD)), 1<<(31-crbD) );
6481 break;
6482 }
6483
sewardjb51f0f42005-07-18 11:38:02 +00006484//zz case 0x040: { // mcrfs (Move to Condition Register from FPSCR, PPC32 p465)
cerion76de5cf2005-11-18 18:25:12 +00006485//zz UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) );
6486//zz UChar b21to22 = toUChar( IFIELD( theInstr, 21, 2 ) );
6487//zz UChar crfS = toUChar( IFIELD( theInstr, 18, 3 ) );
6488//zz UChar b11to17 = toUChar( IFIELD( theInstr, 11, 7 ) );
sewardjb51f0f42005-07-18 11:38:02 +00006489//zz
6490//zz IRTemp tmp = newTemp(Ity_I32);
6491//zz
cerion76de5cf2005-11-18 18:25:12 +00006492//zz if (b21to22 != 0 || b11to17 != 0 || flag_rC != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006493//zz vex_printf("dis_fp_scr(ppc)(instr,mcrfs)\n");
sewardjb51f0f42005-07-18 11:38:02 +00006494//zz return False;
6495//zz }
6496//zz DIP("mcrfs crf%d,crf%d\n", crfD, crfS);
ceriond953ebb2005-11-29 13:27:20 +00006497//zz assign( tmp, getGST_field( PPC_GST_FPSCR, crfS ) );
6498//zz putGST_field( PPC_GST_CR, mkexpr(tmp), crfD );
sewardjb51f0f42005-07-18 11:38:02 +00006499//zz break;
6500//zz }
sewardj0e2cc672005-07-29 21:58:51 +00006501
6502 case 0x046: { // mtfsb0 (Move to FPSCR Bit 0, PPC32 p478)
6503 // Bit crbD of the FPSCR is cleared.
cerion76de5cf2005-11-18 18:25:12 +00006504 UChar crbD = ifieldRegDS(theInstr);
6505 UInt b11to20 = IFIELD(theInstr, 11, 10);
sewardj0e2cc672005-07-29 21:58:51 +00006506
6507 if (b11to20 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006508 vex_printf("dis_fp_scr(ppc)(instr,mtfsb0)\n");
sewardj0e2cc672005-07-29 21:58:51 +00006509 return False;
6510 }
cerion5b2325f2005-12-23 00:55:09 +00006511 DIP("mtfsb0%s crb%d\n", flag_rC ? ".":"", crbD);
ceriond953ebb2005-11-29 13:27:20 +00006512 putGST_masked( PPC_GST_FPSCR, mkU32(0), 1<<(31-crbD) );
sewardj0e2cc672005-07-29 21:58:51 +00006513 break;
6514 }
6515
6516 case 0x086: { // mtfsfi (Move to FPSCR Field Immediate, PPC32 p481)
cerion76de5cf2005-11-18 18:25:12 +00006517 UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) );
6518 UChar b16to22 = toUChar( IFIELD( theInstr, 16, 7 ) );
6519 UChar IMM = toUChar( IFIELD( theInstr, 12, 4 ) );
6520 UChar b11 = toUChar( IFIELD( theInstr, 11, 1 ) );
sewardj0e2cc672005-07-29 21:58:51 +00006521
6522 if (b16to22 != 0 || b11 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006523 vex_printf("dis_fp_scr(ppc)(instr,mtfsfi)\n");
sewardj0e2cc672005-07-29 21:58:51 +00006524 return False;
6525 }
cerion5b2325f2005-12-23 00:55:09 +00006526 DIP("mtfsfi%s crf%d,%d\n", flag_rC ? ".":"", crfD, IMM);
ceriond953ebb2005-11-29 13:27:20 +00006527 putGST_field( PPC_GST_FPSCR, mkU32(IMM), crfD );
sewardj0e2cc672005-07-29 21:58:51 +00006528 break;
6529 }
sewardje14bb9f2005-07-22 09:39:02 +00006530
6531 case 0x247: { // mffs (Move from FPSCR, PPC32 p468)
cerion76de5cf2005-11-18 18:25:12 +00006532 UChar frD_addr = ifieldRegDS(theInstr);
6533 UInt b11to20 = IFIELD(theInstr, 11, 10);
sewardje14bb9f2005-07-22 09:39:02 +00006534
6535 if (b11to20 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006536 vex_printf("dis_fp_scr(ppc)(instr,mffs)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006537 return False;
6538 }
cerion5b2325f2005-12-23 00:55:09 +00006539 DIP("mffs%s fr%u\n", flag_rC ? ".":"", frD_addr);
6540 putFReg( frD_addr,
6541 unop( Iop_ReinterpI64asF64,
6542 unop( Iop_32Uto64,
6543 getGST_masked( PPC_GST_FPSCR, MASK_FPSCR_RN ) )));
sewardje14bb9f2005-07-22 09:39:02 +00006544 break;
6545 }
6546
6547 case 0x2C7: { // mtfsf (Move to FPSCR Fields, PPC32 p480)
cerion76de5cf2005-11-18 18:25:12 +00006548 UChar b25 = toUChar( IFIELD(theInstr, 25, 1) );
6549 UChar FM = toUChar( IFIELD(theInstr, 17, 8) );
6550 UChar b16 = toUChar( IFIELD(theInstr, 16, 1) );
6551 UChar frB_addr = ifieldRegB(theInstr);
6552 IRTemp frB = newTemp(Ity_F64);
sewardje14bb9f2005-07-22 09:39:02 +00006553 IRTemp rB_32 = newTemp(Ity_I32);
cerion76de5cf2005-11-18 18:25:12 +00006554 Int i, mask;
sewardje14bb9f2005-07-22 09:39:02 +00006555
6556 if (b25 != 0 || b16 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006557 vex_printf("dis_fp_scr(ppc)(instr,mtfsf)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006558 return False;
6559 }
cerion5b2325f2005-12-23 00:55:09 +00006560 DIP("mtfsf%s %d,fr%u\n", flag_rC ? ".":"", FM, frB_addr);
sewardje14bb9f2005-07-22 09:39:02 +00006561 assign( frB, getFReg(frB_addr));
6562 assign( rB_32, unop( Iop_64to32,
6563 unop( Iop_ReinterpF64asI64, mkexpr(frB) )));
6564 // Build 32bit mask from FM:
cerion76de5cf2005-11-18 18:25:12 +00006565 mask = 0;
sewardje14bb9f2005-07-22 09:39:02 +00006566 for (i=0; i<8; i++) {
6567 if ((FM & (1<<(7-i))) == 1) {
6568 mask |= 0xF << (7-i);
6569 }
6570 }
ceriond953ebb2005-11-29 13:27:20 +00006571 putGST_masked( PPC_GST_FPSCR, mkexpr(rB_32), mask );
sewardje14bb9f2005-07-22 09:39:02 +00006572 break;
6573 }
6574
6575 default:
cerion5b2325f2005-12-23 00:55:09 +00006576 vex_printf("dis_fp_scr(ppc)(opc2)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006577 return False;
6578 }
6579 return True;
6580}
6581
6582
6583
cerion32aad402005-09-10 12:02:24 +00006584/*------------------------------------------------------------*/
6585/*--- AltiVec Instruction Translation ---*/
6586/*------------------------------------------------------------*/
6587
6588/*
6589 Altivec Cache Control Instructions (Data Streams)
6590*/
6591static Bool dis_av_datastream ( UInt theInstr )
6592{
cerion76de5cf2005-11-18 18:25:12 +00006593 /* X-Form */
6594 UChar opc1 = ifieldOPC(theInstr);
6595 UChar flag_T = toUChar( IFIELD( theInstr, 25, 1 ) );
6596 UChar flag_A = flag_T;
6597 UChar b23to24 = toUChar( IFIELD( theInstr, 23, 2 ) );
6598 UChar STRM = toUChar( IFIELD( theInstr, 21, 2 ) );
6599 UChar rA_addr = ifieldRegA(theInstr);
6600 UChar rB_addr = ifieldRegB(theInstr);
6601 UInt opc2 = ifieldOPClo10(theInstr);
6602 UChar b0 = ifieldBIT0(theInstr);
cerion32aad402005-09-10 12:02:24 +00006603
6604 if (opc1 != 0x1F || b23to24 != 0 || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006605 vex_printf("dis_av_datastream(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00006606 return False;
6607 }
6608
6609 switch (opc2) {
6610 case 0x156: // dst (Data Stream Touch, AV p115)
cerion5b2325f2005-12-23 00:55:09 +00006611 DIP("dst%s r%u,r%u,%d\n", flag_T ? "t" : "",
6612 rA_addr, rB_addr, STRM);
cerion32aad402005-09-10 12:02:24 +00006613 DIP(" => not implemented\n");
6614 return False;
6615
6616 case 0x176: // dstst (Data Stream Touch for Store, AV p117)
cerion5b2325f2005-12-23 00:55:09 +00006617 DIP("dstst%s r%u,r%u,%d\n", flag_T ? "t" : "",
6618 rA_addr, rB_addr, STRM);
cerion32aad402005-09-10 12:02:24 +00006619 DIP(" => not implemented\n");
6620 return False;
6621
6622 case 0x336: // dss (Data Stream Stop, AV p114)
6623 if (rA_addr != 0 || rB_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006624 vex_printf("dis_av_datastream(ppc)(opc2,dst)\n");
cerion32aad402005-09-10 12:02:24 +00006625 return False;
6626 }
6627 if (flag_A == 0) {
6628 DIP("dss %d\n", STRM);
6629 DIP(" => not implemented\n");
6630 } else {
6631 DIP("dssall\n");
6632 DIP(" => not implemented\n");
6633 }
6634 return False;
6635
6636 default:
cerion5b2325f2005-12-23 00:55:09 +00006637 vex_printf("dis_av_datastream(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00006638 return False;
6639 }
6640 return True;
6641}
6642
6643/*
6644 AltiVec Processor Control Instructions
6645*/
6646static Bool dis_av_procctl ( UInt theInstr )
6647{
cerion76de5cf2005-11-18 18:25:12 +00006648 /* VX-Form */
6649 UChar opc1 = ifieldOPC(theInstr);
6650 UChar vD_addr = ifieldRegDS(theInstr);
6651 UChar vA_addr = ifieldRegA(theInstr);
6652 UChar vB_addr = ifieldRegB(theInstr);
6653 UInt opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00006654
6655 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00006656 vex_printf("dis_av_procctl(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00006657 return False;
6658 }
6659
6660 switch (opc2) {
6661 case 0x604: // mfvscr (Move from VSCR, AV p129)
6662 if (vA_addr != 0 || vB_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006663 vex_printf("dis_av_procctl(ppc)(opc2,dst)\n");
cerion32aad402005-09-10 12:02:24 +00006664 return False;
6665 }
6666 DIP("mfvscr v%d\n", vD_addr);
ceriond953ebb2005-11-29 13:27:20 +00006667 putVReg( vD_addr, unop(Iop_32UtoV128, getGST( PPC_GST_VSCR )) );
cerion225a0342005-09-12 20:49:09 +00006668 break;
cerion32aad402005-09-10 12:02:24 +00006669
cerion225a0342005-09-12 20:49:09 +00006670 case 0x644: { // mtvscr (Move to VSCR, AV p130)
sewardj197bd172005-10-12 11:34:33 +00006671 IRTemp vB = newTemp(Ity_V128);
cerion32aad402005-09-10 12:02:24 +00006672 if (vD_addr != 0 || vA_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006673 vex_printf("dis_av_procctl(ppc)(opc2,dst)\n");
cerion32aad402005-09-10 12:02:24 +00006674 return False;
6675 }
6676 DIP("mtvscr v%d\n", vB_addr);
cerion225a0342005-09-12 20:49:09 +00006677 assign( vB, getVReg(vB_addr));
ceriond953ebb2005-11-29 13:27:20 +00006678 putGST( PPC_GST_VSCR, unop(Iop_V128to32, mkexpr(vB)) );
cerion225a0342005-09-12 20:49:09 +00006679 break;
6680 }
cerion32aad402005-09-10 12:02:24 +00006681 default:
cerion5b2325f2005-12-23 00:55:09 +00006682 vex_printf("dis_av_procctl(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00006683 return False;
6684 }
6685 return True;
6686}
ceriona982c052005-06-28 17:23:09 +00006687
6688/*
6689 AltiVec Load Instructions
6690*/
6691static Bool dis_av_load ( UInt theInstr )
6692{
cerion76de5cf2005-11-18 18:25:12 +00006693 /* X-Form */
6694 UChar opc1 = ifieldOPC(theInstr);
6695 UChar vD_addr = ifieldRegDS(theInstr);
6696 UChar rA_addr = ifieldRegA(theInstr);
6697 UChar rB_addr = ifieldRegB(theInstr);
6698 UInt opc2 = ifieldOPClo10(theInstr);
6699 UChar b0 = ifieldBIT0(theInstr);
ceriona982c052005-06-28 17:23:09 +00006700
cerionfb197c42005-12-24 12:32:10 +00006701 IRType ty = mode64 ? Ity_I64 : Ity_I32;
6702 IRTemp EA = newTemp(ty);
6703 IRTemp EA_align16 = newTemp(ty);
cerion2831b002005-11-30 19:55:22 +00006704
ceriona982c052005-06-28 17:23:09 +00006705 if (opc1 != 0x1F || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006706 vex_printf("dis_av_load(ppc)(instr)\n");
ceriona982c052005-06-28 17:23:09 +00006707 return False;
6708 }
6709
cerionfb197c42005-12-24 12:32:10 +00006710 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
6711 assign( EA_align16, addr_align( mkexpr(EA), 16 ) );
ceriona50fde52005-07-01 21:16:10 +00006712
ceriona982c052005-06-28 17:23:09 +00006713 switch (opc2) {
6714
cerion6f6c6a02005-09-13 18:41:09 +00006715 case 0x006: { // lvsl (Load Vector for Shift Left, AV p123)
cerionfb197c42005-12-24 12:32:10 +00006716 IRDirty* d;
sewardjd1470942005-10-22 02:01:16 +00006717 UInt vD_off = vectorGuestRegOffset(vD_addr);
6718 IRExpr** args = mkIRExprVec_3(
6719 mkU32(vD_off),
cerionfb197c42005-12-24 12:32:10 +00006720 binop(Iop_And32, mkSzNarrow32(ty, mkexpr(EA)),
6721 mkU32(0xF)),
sewardjd1470942005-10-22 02:01:16 +00006722 mkU32(0)/*left*/ );
cerion5b2325f2005-12-23 00:55:09 +00006723 if (!mode64) {
cerion4c4f5ef2006-01-02 14:41:50 +00006724 d = unsafeIRDirty_0_N (
6725 0/*regparms*/,
6726 "ppc32g_dirtyhelper_LVS",
6727 fnptr_to_fnentry(&ppc32g_dirtyhelper_LVS),
6728 args );
cerion5b2325f2005-12-23 00:55:09 +00006729 } else {
cerion4c4f5ef2006-01-02 14:41:50 +00006730 d = unsafeIRDirty_0_N (
6731 0/*regparms*/,
6732 "ppc64g_dirtyhelper_LVS",
6733 fnptr_to_fnentry(&ppc64g_dirtyhelper_LVS),
6734 args );
cerion5b2325f2005-12-23 00:55:09 +00006735 }
ceriond953ebb2005-11-29 13:27:20 +00006736 DIP("lvsl v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr);
cerion6f6c6a02005-09-13 18:41:09 +00006737 /* declare guest state effects */
6738 d->needsBBP = True;
6739 d->nFxState = 1;
6740 d->fxState[0].fx = Ifx_Write;
sewardjd1470942005-10-22 02:01:16 +00006741 d->fxState[0].offset = vD_off;
cerion6f6c6a02005-09-13 18:41:09 +00006742 d->fxState[0].size = sizeof(U128);
cerion32aad402005-09-10 12:02:24 +00006743
cerion6f6c6a02005-09-13 18:41:09 +00006744 /* execute the dirty call, side-effecting guest state */
6745 stmt( IRStmt_Dirty(d) );
6746 break;
6747 }
6748 case 0x026: { // lvsr (Load Vector for Shift Right, AV p125)
cerionfb197c42005-12-24 12:32:10 +00006749 IRDirty* d;
sewardjd1470942005-10-22 02:01:16 +00006750 UInt vD_off = vectorGuestRegOffset(vD_addr);
6751 IRExpr** args = mkIRExprVec_3(
6752 mkU32(vD_off),
cerionfb197c42005-12-24 12:32:10 +00006753 binop(Iop_And32, mkSzNarrow32(ty, mkexpr(EA)),
6754 mkU32(0xF)),
sewardjd1470942005-10-22 02:01:16 +00006755 mkU32(1)/*right*/ );
cerion5b2325f2005-12-23 00:55:09 +00006756 if (!mode64) {
cerion4c4f5ef2006-01-02 14:41:50 +00006757 d = unsafeIRDirty_0_N (
6758 0/*regparms*/,
6759 "ppc32g_dirtyhelper_LVS",
6760 fnptr_to_fnentry(&ppc32g_dirtyhelper_LVS),
6761 args );
cerion5b2325f2005-12-23 00:55:09 +00006762 } else {
cerion4c4f5ef2006-01-02 14:41:50 +00006763 d = unsafeIRDirty_0_N (
6764 0/*regparms*/,
6765 "ppc64g_dirtyhelper_LVS",
6766 fnptr_to_fnentry(&ppc64g_dirtyhelper_LVS),
6767 args );
cerion5b2325f2005-12-23 00:55:09 +00006768 }
ceriond953ebb2005-11-29 13:27:20 +00006769 DIP("lvsr v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr);
cerion6f6c6a02005-09-13 18:41:09 +00006770 /* declare guest state effects */
6771 d->needsBBP = True;
6772 d->nFxState = 1;
6773 d->fxState[0].fx = Ifx_Write;
sewardjd1470942005-10-22 02:01:16 +00006774 d->fxState[0].offset = vD_off;
cerion6f6c6a02005-09-13 18:41:09 +00006775 d->fxState[0].size = sizeof(U128);
cerion32aad402005-09-10 12:02:24 +00006776
cerion6f6c6a02005-09-13 18:41:09 +00006777 /* execute the dirty call, side-effecting guest state */
6778 stmt( IRStmt_Dirty(d) );
6779 break;
6780 }
cerion32aad402005-09-10 12:02:24 +00006781 case 0x007: // lvebx (Load Vector Element Byte Indexed, AV p119)
ceriond953ebb2005-11-29 13:27:20 +00006782 DIP("lvebx v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr);
cerion61c92742005-09-14 22:59:26 +00006783 /* loads addressed byte into vector[EA[0:3]
6784 since all other destination bytes are undefined,
6785 can simply load entire vector from 16-aligned EA */
cerionfb197c42005-12-24 12:32:10 +00006786 putVReg( vD_addr, loadBE(Ity_V128, mkexpr(EA_align16)) );
cerion61c92742005-09-14 22:59:26 +00006787 break;
cerion32aad402005-09-10 12:02:24 +00006788
6789 case 0x027: // lvehx (Load Vector Element Half Word Indexed, AV p121)
ceriond953ebb2005-11-29 13:27:20 +00006790 DIP("lvehx v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr);
cerion61c92742005-09-14 22:59:26 +00006791 /* see note for lvebx */
cerionfb197c42005-12-24 12:32:10 +00006792 putVReg( vD_addr, loadBE(Ity_V128, mkexpr(EA_align16)) );
cerion61c92742005-09-14 22:59:26 +00006793 break;
cerion32aad402005-09-10 12:02:24 +00006794
6795 case 0x047: // lvewx (Load Vector Element Word Indexed, AV p122)
ceriond953ebb2005-11-29 13:27:20 +00006796 DIP("lvewx v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr);
cerion61c92742005-09-14 22:59:26 +00006797 /* see note for lvebx */
cerionfb197c42005-12-24 12:32:10 +00006798 putVReg( vD_addr, loadBE(Ity_V128, mkexpr(EA_align16)) );
cerion61c92742005-09-14 22:59:26 +00006799 break;
ceriona982c052005-06-28 17:23:09 +00006800
6801 case 0x067: // lvx (Load Vector Indexed, AV p127)
ceriond953ebb2005-11-29 13:27:20 +00006802 DIP("lvx v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr);
cerionfb197c42005-12-24 12:32:10 +00006803 putVReg( vD_addr, loadBE(Ity_V128, mkexpr(EA_align16)) );
ceriona50fde52005-07-01 21:16:10 +00006804 break;
ceriona982c052005-06-28 17:23:09 +00006805
cerion32aad402005-09-10 12:02:24 +00006806 case 0x167: // lvxl (Load Vector Indexed LRU, AV p128)
6807 // XXX: lvxl gives explicit control over cache block replacement
ceriond953ebb2005-11-29 13:27:20 +00006808 DIP("lvxl v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr);
cerion32aad402005-09-10 12:02:24 +00006809 DIP(" => not implemented\n");
6810 return False;
ceriona982c052005-06-28 17:23:09 +00006811
6812 default:
cerion5b2325f2005-12-23 00:55:09 +00006813 vex_printf("dis_av_load(ppc)(opc2)\n");
ceriona982c052005-06-28 17:23:09 +00006814 return False;
6815 }
6816 return True;
6817}
6818
cerion2831b002005-11-30 19:55:22 +00006819
ceriona982c052005-06-28 17:23:09 +00006820/*
6821 AltiVec Store Instructions
6822*/
6823static Bool dis_av_store ( UInt theInstr )
6824{
cerion76de5cf2005-11-18 18:25:12 +00006825 /* X-Form */
6826 UChar opc1 = ifieldOPC(theInstr);
6827 UChar vS_addr = ifieldRegDS(theInstr);
6828 UChar rA_addr = ifieldRegA(theInstr);
6829 UChar rB_addr = ifieldRegB(theInstr);
6830 UInt opc2 = ifieldOPClo10(theInstr);
6831 UChar b0 = ifieldBIT0(theInstr);
ceriona982c052005-06-28 17:23:09 +00006832
cerion2831b002005-11-30 19:55:22 +00006833 IRType ty = mode64 ? Ity_I64 : Ity_I32;
6834 IRTemp EA = newTemp(ty);
ceriondba87e22006-01-02 15:15:45 +00006835 IRTemp addr_aligned = newTemp(ty);
cerion2831b002005-11-30 19:55:22 +00006836 IRTemp vS = newTemp(Ity_V128);
6837 IRTemp eb = newTemp(Ity_I8);
6838 IRTemp idx = newTemp(Ity_I8);
ceriona982c052005-06-28 17:23:09 +00006839
6840 if (opc1 != 0x1F || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006841 vex_printf("dis_av_store(ppc)(instr)\n");
ceriona982c052005-06-28 17:23:09 +00006842 return False;
6843 }
6844
ceriond953ebb2005-11-29 13:27:20 +00006845 assign( vS, getVReg(vS_addr));
cerion2831b002005-11-30 19:55:22 +00006846 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
ceriond953ebb2005-11-29 13:27:20 +00006847
ceriona982c052005-06-28 17:23:09 +00006848 switch (opc2) {
cerion61c92742005-09-14 22:59:26 +00006849 case 0x087: { // stvebx (Store Vector Byte Indexed, AV p131)
ceriond953ebb2005-11-29 13:27:20 +00006850 DIP("stvebx v%d,r%u,r%u\n", vS_addr, rA_addr, rB_addr);
cerion61c92742005-09-14 22:59:26 +00006851 assign( eb, binop(Iop_And8, mkU8(0xF),
cerion2831b002005-11-30 19:55:22 +00006852 unop(Iop_32to8,
6853 mkSzNarrow32(ty, mkexpr(EA)) )) );
cerion5b2325f2005-12-23 00:55:09 +00006854 assign( idx, binop(Iop_Shl8,
6855 binop(Iop_Sub8, mkU8(15), mkexpr(eb)),
cerion61c92742005-09-14 22:59:26 +00006856 mkU8(3)) );
6857 storeBE( mkexpr(EA),
6858 unop(Iop_32to8, unop(Iop_V128to32,
6859 binop(Iop_ShrV128, mkexpr(vS), mkexpr(idx)))) );
6860 break;
6861 }
6862 case 0x0A7: { // stvehx (Store Vector Half Word Indexed, AV p132)
ceriond953ebb2005-11-29 13:27:20 +00006863 DIP("stvehx v%d,r%u,r%u\n", vS_addr, rA_addr, rB_addr);
ceriondba87e22006-01-02 15:15:45 +00006864 assign( addr_aligned, addr_align(mkexpr(EA), 2) );
cerion61c92742005-09-14 22:59:26 +00006865 assign( eb, binop(Iop_And8, mkU8(0xF),
ceriondba87e22006-01-02 15:15:45 +00006866 mkSzNarrow8(ty, mkexpr(addr_aligned) )) );
cerion5b2325f2005-12-23 00:55:09 +00006867 assign( idx, binop(Iop_Shl8,
6868 binop(Iop_Sub8, mkU8(14), mkexpr(eb)),
cerion61c92742005-09-14 22:59:26 +00006869 mkU8(3)) );
ceriond953ebb2005-11-29 13:27:20 +00006870 storeBE( mkexpr(addr_aligned),
cerion61c92742005-09-14 22:59:26 +00006871 unop(Iop_32to16, unop(Iop_V128to32,
6872 binop(Iop_ShrV128, mkexpr(vS), mkexpr(idx)))) );
6873 break;
6874 }
6875 case 0x0C7: { // stvewx (Store Vector Word Indexed, AV p133)
ceriond953ebb2005-11-29 13:27:20 +00006876 DIP("stvewx v%d,r%u,r%u\n", vS_addr, rA_addr, rB_addr);
ceriondba87e22006-01-02 15:15:45 +00006877 assign( addr_aligned, addr_align(mkexpr(EA), 4) );
cerion61c92742005-09-14 22:59:26 +00006878 assign( eb, binop(Iop_And8, mkU8(0xF),
ceriondba87e22006-01-02 15:15:45 +00006879 mkSzNarrow8(ty, mkexpr(addr_aligned) )) );
cerion5b2325f2005-12-23 00:55:09 +00006880 assign( idx, binop(Iop_Shl8,
6881 binop(Iop_Sub8, mkU8(12), mkexpr(eb)),
cerion61c92742005-09-14 22:59:26 +00006882 mkU8(3)) );
ceriond953ebb2005-11-29 13:27:20 +00006883 storeBE( mkexpr(addr_aligned),
cerion61c92742005-09-14 22:59:26 +00006884 unop(Iop_V128to32,
6885 binop(Iop_ShrV128, mkexpr(vS), mkexpr(idx))) );
6886 break;
6887 }
cerion32aad402005-09-10 12:02:24 +00006888
ceriona982c052005-06-28 17:23:09 +00006889 case 0x0E7: // stvx (Store Vector Indexed, AV p134)
ceriond953ebb2005-11-29 13:27:20 +00006890 DIP("stvx v%d,r%u,r%u\n", vS_addr, rA_addr, rB_addr);
6891 storeBE( addr_align( mkexpr(EA), 16 ), mkexpr(vS) );
ceriona982c052005-06-28 17:23:09 +00006892 break;
6893
cerion32aad402005-09-10 12:02:24 +00006894 case 0x1E7: // stvxl (Store Vector Indexed LRU, AV p135)
6895 // XXX: stvxl can give explicit control over cache block replacement
ceriond953ebb2005-11-29 13:27:20 +00006896 DIP("stvxl v%d,r%u,r%u\n", vS_addr, rA_addr, rB_addr);
cerion32aad402005-09-10 12:02:24 +00006897 DIP(" => not implemented\n");
6898 return False;
ceriond953ebb2005-11-29 13:27:20 +00006899// STORE(vS, 16, addr_align( mkexpr(EA), 16 ));
cerion5b2325f2005-12-23 00:55:09 +00006900// break;
ceriona982c052005-06-28 17:23:09 +00006901
6902 default:
cerion5b2325f2005-12-23 00:55:09 +00006903 vex_printf("dis_av_store(ppc)(opc2)\n");
ceriona982c052005-06-28 17:23:09 +00006904 return False;
6905 }
6906 return True;
6907}
6908
cerion32aad402005-09-10 12:02:24 +00006909/*
6910 AltiVec Arithmetic Instructions
6911*/
6912static Bool dis_av_arith ( UInt theInstr )
6913{
cerion76de5cf2005-11-18 18:25:12 +00006914 /* VX-Form */
6915 UChar opc1 = ifieldOPC(theInstr);
6916 UChar vD_addr = ifieldRegDS(theInstr);
6917 UChar vA_addr = ifieldRegA(theInstr);
6918 UChar vB_addr = ifieldRegB(theInstr);
6919 UInt opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00006920
ceriond3e52412005-09-14 21:15:40 +00006921 IRTemp vA = newTemp(Ity_V128);
6922 IRTemp vB = newTemp(Ity_V128);
cerion4a49b032005-11-08 16:23:07 +00006923 IRTemp z3 = newTemp(Ity_I64);
6924 IRTemp z2 = newTemp(Ity_I64);
6925 IRTemp z1 = newTemp(Ity_I64);
6926 IRTemp z0 = newTemp(Ity_I64);
6927 IRTemp aEvn, aOdd;
6928 IRTemp a15, a14, a13, a12, a11, a10, a9, a8;
6929 IRTemp a7, a6, a5, a4, a3, a2, a1, a0;
6930 IRTemp b3, b2, b1, b0;
6931
6932 aEvn = aOdd = IRTemp_INVALID;
6933 a15 = a14 = a13 = a12 = a11 = a10 = a9 = a8 = IRTemp_INVALID;
6934 a7 = a6 = a5 = a4 = a3 = a2 = a1 = a0 = IRTemp_INVALID;
6935 b3 = b2 = b1 = b0 = IRTemp_INVALID;
6936
ceriond3e52412005-09-14 21:15:40 +00006937 assign( vA, getVReg(vA_addr));
6938 assign( vB, getVReg(vB_addr));
6939
cerion32aad402005-09-10 12:02:24 +00006940 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00006941 vex_printf("dis_av_arith(ppc)(opc1 != 0x4)\n");
cerion32aad402005-09-10 12:02:24 +00006942 return False;
6943 }
6944
6945 switch (opc2) {
6946 /* Add */
ceriond3e52412005-09-14 21:15:40 +00006947 case 0x180: { // vaddcuw (Add Carryout Unsigned Word, AV p136)
cerion32aad402005-09-10 12:02:24 +00006948 DIP("vaddcuw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006949 /* unsigned_ov(x+y) = (y >u not(x)) */
ceriond3e52412005-09-14 21:15:40 +00006950 putVReg( vD_addr, binop(Iop_ShrN32x4,
cerion36991ef2005-09-15 12:42:16 +00006951 binop(Iop_CmpGT32Ux4, mkexpr(vB),
6952 unop(Iop_NotV128, mkexpr(vA))),
ceriond3e52412005-09-14 21:15:40 +00006953 mkU8(31)) );
6954 break;
6955 }
cerion32aad402005-09-10 12:02:24 +00006956 case 0x000: // vaddubm (Add Unsigned Byte Modulo, AV p141)
6957 DIP("vaddubm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006958 putVReg( vD_addr, binop(Iop_Add8x16, mkexpr(vA), mkexpr(vB)) );
6959 break;
6960
cerion32aad402005-09-10 12:02:24 +00006961 case 0x040: // vadduhm (Add Unsigned Half Word Modulo, AV p143)
6962 DIP("vadduhm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006963 putVReg( vD_addr, binop(Iop_Add16x8, mkexpr(vA), mkexpr(vB)) );
6964 break;
6965
cerion32aad402005-09-10 12:02:24 +00006966 case 0x080: // vadduwm (Add Unsigned Word Modulo, AV p145)
6967 DIP("vadduwm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006968 putVReg( vD_addr, binop(Iop_Add32x4, mkexpr(vA), mkexpr(vB)) );
6969 break;
6970
cerion32aad402005-09-10 12:02:24 +00006971 case 0x200: // vaddubs (Add Unsigned Byte Saturate, AV p142)
6972 DIP("vaddubs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006973 putVReg( vD_addr, binop(Iop_QAdd8Ux16, mkexpr(vA), mkexpr(vB)) );
6974 // TODO: set VSCR[SAT], perhaps via new primop: Iop_SatOfQAdd8Ux16
6975 break;
6976
cerion32aad402005-09-10 12:02:24 +00006977 case 0x240: // vadduhs (Add Unsigned Half Word Saturate, AV p144)
6978 DIP("vadduhs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006979 putVReg( vD_addr, binop(Iop_QAdd16Ux8, mkexpr(vA), mkexpr(vB)) );
6980 // TODO: set VSCR[SAT]
6981 break;
6982
cerion32aad402005-09-10 12:02:24 +00006983 case 0x280: // vadduws (Add Unsigned Word Saturate, AV p146)
6984 DIP("vadduws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006985 putVReg( vD_addr, binop(Iop_QAdd32Ux4, mkexpr(vA), mkexpr(vB)) );
6986 // TODO: set VSCR[SAT]
6987 break;
6988
cerion32aad402005-09-10 12:02:24 +00006989 case 0x300: // vaddsbs (Add Signed Byte Saturate, AV p138)
6990 DIP("vaddsbs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006991 putVReg( vD_addr, binop(Iop_QAdd8Sx16, mkexpr(vA), mkexpr(vB)) );
6992 // TODO: set VSCR[SAT]
6993 break;
6994
cerion32aad402005-09-10 12:02:24 +00006995 case 0x340: // vaddshs (Add Signed Half Word Saturate, AV p139)
6996 DIP("vaddshs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006997 putVReg( vD_addr, binop(Iop_QAdd16Sx8, mkexpr(vA), mkexpr(vB)) );
6998 // TODO: set VSCR[SAT]
6999 break;
7000
cerion32aad402005-09-10 12:02:24 +00007001 case 0x380: // vaddsws (Add Signed Word Saturate, AV p140)
7002 DIP("vaddsws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007003 putVReg( vD_addr, binop(Iop_QAdd32Sx4, mkexpr(vA), mkexpr(vB)) );
7004 // TODO: set VSCR[SAT]
7005 break;
7006
7007
cerion32aad402005-09-10 12:02:24 +00007008 /* Subtract */
cerion36991ef2005-09-15 12:42:16 +00007009 case 0x580: { // vsubcuw (Subtract Carryout Unsigned Word, AV p260)
cerion32aad402005-09-10 12:02:24 +00007010 DIP("vsubcuw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007011 /* unsigned_ov(x-y) = (y >u x) */
7012 putVReg( vD_addr, binop(Iop_ShrN32x4,
7013 unop(Iop_NotV128,
7014 binop(Iop_CmpGT32Ux4, mkexpr(vB),
7015 mkexpr(vA))),
7016 mkU8(31)) );
7017 break;
7018 }
cerion32aad402005-09-10 12:02:24 +00007019 case 0x400: // vsububm (Subtract Unsigned Byte Modulo, AV p265)
7020 DIP("vsububm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007021 putVReg( vD_addr, binop(Iop_Sub8x16, mkexpr(vA), mkexpr(vB)) );
7022 break;
7023
cerion32aad402005-09-10 12:02:24 +00007024 case 0x440: // vsubuhm (Subtract Unsigned Half Word Modulo, AV p267)
7025 DIP("vsubuhm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007026 putVReg( vD_addr, binop(Iop_Sub16x8, mkexpr(vA), mkexpr(vB)) );
7027 break;
7028
cerion32aad402005-09-10 12:02:24 +00007029 case 0x480: // vsubuwm (Subtract Unsigned Word Modulo, AV p269)
7030 DIP("vsubuwm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007031 putVReg( vD_addr, binop(Iop_Sub32x4, mkexpr(vA), mkexpr(vB)) );
7032 break;
7033
cerion32aad402005-09-10 12:02:24 +00007034 case 0x600: // vsububs (Subtract Unsigned Byte Saturate, AV p266)
7035 DIP("vsububs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007036 putVReg( vD_addr, binop(Iop_QSub8Ux16, mkexpr(vA), mkexpr(vB)) );
7037 // TODO: set VSCR[SAT]
7038 break;
7039
cerion5b2325f2005-12-23 00:55:09 +00007040 case 0x640: // vsubuhs (Subtract Unsigned HWord Saturate, AV p268)
cerion32aad402005-09-10 12:02:24 +00007041 DIP("vsubuhs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007042 putVReg( vD_addr, binop(Iop_QSub16Ux8, mkexpr(vA), mkexpr(vB)) );
7043 // TODO: set VSCR[SAT]
7044 break;
7045
cerion32aad402005-09-10 12:02:24 +00007046 case 0x680: // vsubuws (Subtract Unsigned Word Saturate, AV p270)
7047 DIP("vsubuws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007048 putVReg( vD_addr, binop(Iop_QSub32Ux4, mkexpr(vA), mkexpr(vB)) );
7049 // TODO: set VSCR[SAT]
7050 break;
7051
cerion32aad402005-09-10 12:02:24 +00007052 case 0x700: // vsubsbs (Subtract Signed Byte Saturate, AV p262)
7053 DIP("vsubsbs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007054 putVReg( vD_addr, binop(Iop_QSub8Sx16, mkexpr(vA), mkexpr(vB)) );
7055 // TODO: set VSCR[SAT]
7056 break;
7057
cerion32aad402005-09-10 12:02:24 +00007058 case 0x740: // vsubshs (Subtract Signed Half Word Saturate, AV p263)
7059 DIP("vsubshs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007060 putVReg( vD_addr, binop(Iop_QSub16Sx8, mkexpr(vA), mkexpr(vB)) );
7061 // TODO: set VSCR[SAT]
7062 break;
7063
cerion32aad402005-09-10 12:02:24 +00007064 case 0x780: // vsubsws (Subtract Signed Word Saturate, AV p264)
7065 DIP("vsubsws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007066 putVReg( vD_addr, binop(Iop_QSub32Sx4, mkexpr(vA), mkexpr(vB)) );
7067 // TODO: set VSCR[SAT]
7068 break;
cerion32aad402005-09-10 12:02:24 +00007069
7070
7071 /* Maximum */
7072 case 0x002: // vmaxub (Maximum Unsigned Byte, AV p182)
7073 DIP("vmaxub v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007074 putVReg( vD_addr, binop(Iop_Max8Ux16, mkexpr(vA), mkexpr(vB)) );
7075 break;
cerion32aad402005-09-10 12:02:24 +00007076
7077 case 0x042: // vmaxuh (Maximum Unsigned Half Word, AV p183)
7078 DIP("vmaxuh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007079 putVReg( vD_addr, binop(Iop_Max16Ux8, mkexpr(vA), mkexpr(vB)) );
7080 break;
cerion32aad402005-09-10 12:02:24 +00007081
7082 case 0x082: // vmaxuw (Maximum Unsigned Word, AV p184)
7083 DIP("vmaxuw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007084 putVReg( vD_addr, binop(Iop_Max32Ux4, mkexpr(vA), mkexpr(vB)) );
7085 break;
cerion32aad402005-09-10 12:02:24 +00007086
7087 case 0x102: // vmaxsb (Maximum Signed Byte, AV p179)
7088 DIP("vmaxsb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007089 putVReg( vD_addr, binop(Iop_Max8Sx16, mkexpr(vA), mkexpr(vB)) );
7090 break;
cerion32aad402005-09-10 12:02:24 +00007091
7092 case 0x142: // vmaxsh (Maximum Signed Half Word, AV p180)
7093 DIP("vmaxsh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007094 putVReg( vD_addr, binop(Iop_Max16Sx8, mkexpr(vA), mkexpr(vB)) );
7095 break;
cerion32aad402005-09-10 12:02:24 +00007096
7097 case 0x182: // vmaxsw (Maximum Signed Word, AV p181)
7098 DIP("vmaxsw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007099 putVReg( vD_addr, binop(Iop_Max32Sx4, mkexpr(vA), mkexpr(vB)) );
7100 break;
cerion32aad402005-09-10 12:02:24 +00007101
7102
7103 /* Minimum */
7104 case 0x202: // vminub (Minimum Unsigned Byte, AV p191)
7105 DIP("vminub v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007106 putVReg( vD_addr, binop(Iop_Min8Ux16, mkexpr(vA), mkexpr(vB)) );
7107 break;
cerion32aad402005-09-10 12:02:24 +00007108
7109 case 0x242: // vminuh (Minimum Unsigned Half Word, AV p192)
7110 DIP("vminuh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007111 putVReg( vD_addr, binop(Iop_Min16Ux8, mkexpr(vA), mkexpr(vB)) );
7112 break;
cerion32aad402005-09-10 12:02:24 +00007113
7114 case 0x282: // vminuw (Minimum Unsigned Word, AV p193)
7115 DIP("vminuw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007116 putVReg( vD_addr, binop(Iop_Min32Ux4, mkexpr(vA), mkexpr(vB)) );
7117 break;
cerion32aad402005-09-10 12:02:24 +00007118
7119 case 0x302: // vminsb (Minimum Signed Byte, AV p188)
7120 DIP("vminsb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007121 putVReg( vD_addr, binop(Iop_Min8Sx16, mkexpr(vA), mkexpr(vB)) );
7122 break;
cerion32aad402005-09-10 12:02:24 +00007123
7124 case 0x342: // vminsh (Minimum Signed Half Word, AV p189)
7125 DIP("vminsh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007126 putVReg( vD_addr, binop(Iop_Min16Sx8, mkexpr(vA), mkexpr(vB)) );
7127 break;
cerion32aad402005-09-10 12:02:24 +00007128
7129 case 0x382: // vminsw (Minimum Signed Word, AV p190)
7130 DIP("vminsw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007131 putVReg( vD_addr, binop(Iop_Min32Sx4, mkexpr(vA), mkexpr(vB)) );
7132 break;
7133
cerion32aad402005-09-10 12:02:24 +00007134
7135 /* Average */
7136 case 0x402: // vavgub (Average Unsigned Byte, AV p152)
7137 DIP("vavgub v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007138 putVReg( vD_addr, binop(Iop_Avg8Ux16, mkexpr(vA), mkexpr(vB)) );
7139 break;
cerion32aad402005-09-10 12:02:24 +00007140
7141 case 0x442: // vavguh (Average Unsigned Half Word, AV p153)
7142 DIP("vavguh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007143 putVReg( vD_addr, binop(Iop_Avg16Ux8, mkexpr(vA), mkexpr(vB)) );
7144 break;
cerion32aad402005-09-10 12:02:24 +00007145
7146 case 0x482: // vavguw (Average Unsigned Word, AV p154)
7147 DIP("vavguw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007148 putVReg( vD_addr, binop(Iop_Avg32Ux4, mkexpr(vA), mkexpr(vB)) );
7149 break;
cerion32aad402005-09-10 12:02:24 +00007150
7151 case 0x502: // vavgsb (Average Signed Byte, AV p149)
7152 DIP("vavgsb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007153 putVReg( vD_addr, binop(Iop_Avg8Sx16, mkexpr(vA), mkexpr(vB)) );
7154 break;
cerion32aad402005-09-10 12:02:24 +00007155
7156 case 0x542: // vavgsh (Average Signed Half Word, AV p150)
7157 DIP("vavgsh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007158 putVReg( vD_addr, binop(Iop_Avg16Sx8, mkexpr(vA), mkexpr(vB)) );
7159 break;
cerion32aad402005-09-10 12:02:24 +00007160
7161 case 0x582: // vavgsw (Average Signed Word, AV p151)
7162 DIP("vavgsw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007163 putVReg( vD_addr, binop(Iop_Avg32Sx4, mkexpr(vA), mkexpr(vB)) );
7164 break;
cerion32aad402005-09-10 12:02:24 +00007165
7166
7167 /* Multiply */
7168 case 0x008: // vmuloub (Multiply Odd Unsigned Byte, AV p213)
7169 DIP("vmuloub v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00007170 putVReg( vD_addr,
7171 binop(Iop_MullEven8Ux16, mkexpr(vA), mkexpr(vB)));
cerion36991ef2005-09-15 12:42:16 +00007172 break;
cerion32aad402005-09-10 12:02:24 +00007173
7174 case 0x048: // vmulouh (Multiply Odd Unsigned Half Word, AV p214)
7175 DIP("vmulouh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00007176 putVReg( vD_addr,
7177 binop(Iop_MullEven16Ux8, mkexpr(vA), mkexpr(vB)));
cerion36991ef2005-09-15 12:42:16 +00007178 break;
cerion32aad402005-09-10 12:02:24 +00007179
7180 case 0x108: // vmulosb (Multiply Odd Signed Byte, AV p211)
7181 DIP("vmulosb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00007182 putVReg( vD_addr,
7183 binop(Iop_MullEven8Sx16, mkexpr(vA), mkexpr(vB)));
cerion36991ef2005-09-15 12:42:16 +00007184 break;
cerion32aad402005-09-10 12:02:24 +00007185
7186 case 0x148: // vmulosh (Multiply Odd Signed Half Word, AV p212)
7187 DIP("vmulosh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00007188 putVReg( vD_addr,
7189 binop(Iop_MullEven16Sx8, mkexpr(vA), mkexpr(vB)));
cerion36991ef2005-09-15 12:42:16 +00007190 break;
cerion32aad402005-09-10 12:02:24 +00007191
7192 case 0x208: // vmuleub (Multiply Even Unsigned Byte, AV p209)
7193 DIP("vmuleub v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion24d06f12005-11-09 21:34:20 +00007194 putVReg( vD_addr, MK_Iop_MullOdd8Ux16( mkexpr(vA), mkexpr(vB) ));
cerion36991ef2005-09-15 12:42:16 +00007195 break;
cerion32aad402005-09-10 12:02:24 +00007196
7197 case 0x248: // vmuleuh (Multiply Even Unsigned Half Word, AV p210)
7198 DIP("vmuleuh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion24d06f12005-11-09 21:34:20 +00007199 putVReg( vD_addr, MK_Iop_MullOdd16Ux8( mkexpr(vA), mkexpr(vB) ));
cerion36991ef2005-09-15 12:42:16 +00007200 break;
cerion32aad402005-09-10 12:02:24 +00007201
7202 case 0x308: // vmulesb (Multiply Even Signed Byte, AV p207)
7203 DIP("vmulesb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion24d06f12005-11-09 21:34:20 +00007204 putVReg( vD_addr, MK_Iop_MullOdd8Sx16( mkexpr(vA), mkexpr(vB) ));
cerion36991ef2005-09-15 12:42:16 +00007205 break;
cerion32aad402005-09-10 12:02:24 +00007206
7207 case 0x348: // vmulesh (Multiply Even Signed Half Word, AV p208)
7208 DIP("vmulesh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion24d06f12005-11-09 21:34:20 +00007209 putVReg( vD_addr, MK_Iop_MullOdd16Sx8( mkexpr(vA), mkexpr(vB) ));
cerion36991ef2005-09-15 12:42:16 +00007210 break;
cerion32aad402005-09-10 12:02:24 +00007211
7212
7213 /* Sum Across Partial */
cerion4a49b032005-11-08 16:23:07 +00007214 case 0x608: { // vsum4ubs (Sum Partial (1/4) UB Saturate, AV p275)
7215 IRTemp aEE, aEO, aOE, aOO;
7216 aEE = aEO = aOE = aOO = IRTemp_INVALID;
cerion32aad402005-09-10 12:02:24 +00007217 DIP("vsum4ubs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion32aad402005-09-10 12:02:24 +00007218
cerion4a49b032005-11-08 16:23:07 +00007219 /* vA: V128_8Ux16 -> 4 x V128_32Ux4, sign-extended */
7220 expand8Ux16( mkexpr(vA), &aEvn, &aOdd ); // (15,13...),(14,12...)
7221 expand16Ux8( mkexpr(aEvn), &aEE, &aEO ); // (15,11...),(13, 9...)
7222 expand16Ux8( mkexpr(aOdd), &aOE, &aOO ); // (14,10...),(12, 8...)
7223
7224 /* break V128 to 4xI32's, zero-extending to I64's */
7225 breakV128to4x64U( mkexpr(aEE), &a15, &a11, &a7, &a3 );
7226 breakV128to4x64U( mkexpr(aOE), &a14, &a10, &a6, &a2 );
7227 breakV128to4x64U( mkexpr(aEO), &a13, &a9, &a5, &a1 );
7228 breakV128to4x64U( mkexpr(aOO), &a12, &a8, &a4, &a0 );
7229 breakV128to4x64U( mkexpr(vB), &b3, &b2, &b1, &b0 );
7230
7231 /* add lanes */
7232 assign( z3, binop(Iop_Add64, mkexpr(b3),
cerion5b2325f2005-12-23 00:55:09 +00007233 binop(Iop_Add64,
7234 binop(Iop_Add64, mkexpr(a15), mkexpr(a14)),
7235 binop(Iop_Add64, mkexpr(a13), mkexpr(a12)))) );
cerion4a49b032005-11-08 16:23:07 +00007236 assign( z2, binop(Iop_Add64, mkexpr(b2),
cerion5b2325f2005-12-23 00:55:09 +00007237 binop(Iop_Add64,
7238 binop(Iop_Add64, mkexpr(a11), mkexpr(a10)),
7239 binop(Iop_Add64, mkexpr(a9), mkexpr(a8)))) );
cerion4a49b032005-11-08 16:23:07 +00007240 assign( z1, binop(Iop_Add64, mkexpr(b1),
cerion5b2325f2005-12-23 00:55:09 +00007241 binop(Iop_Add64,
7242 binop(Iop_Add64, mkexpr(a7), mkexpr(a6)),
7243 binop(Iop_Add64, mkexpr(a5), mkexpr(a4)))) );
cerion4a49b032005-11-08 16:23:07 +00007244 assign( z0, binop(Iop_Add64, mkexpr(b0),
cerion5b2325f2005-12-23 00:55:09 +00007245 binop(Iop_Add64,
7246 binop(Iop_Add64, mkexpr(a3), mkexpr(a2)),
7247 binop(Iop_Add64, mkexpr(a1), mkexpr(a0)))) );
cerion4a49b032005-11-08 16:23:07 +00007248
7249 /* saturate-narrow to 32bit, and combine to V128 */
7250 putVReg( vD_addr, mkV128from4x64U( mkexpr(z3), mkexpr(z2),
7251 mkexpr(z1), mkexpr(z0)) );
7252 break;
7253 }
7254 case 0x708: { // vsum4sbs (Sum Partial (1/4) SB Saturate, AV p273)
7255 IRTemp aEE, aEO, aOE, aOO;
7256 aEE = aEO = aOE = aOO = IRTemp_INVALID;
cerion32aad402005-09-10 12:02:24 +00007257 DIP("vsum4sbs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion32aad402005-09-10 12:02:24 +00007258
cerion4a49b032005-11-08 16:23:07 +00007259 /* vA: V128_8Sx16 -> 4 x V128_32Sx4, sign-extended */
7260 expand8Sx16( mkexpr(vA), &aEvn, &aOdd ); // (15,13...),(14,12...)
7261 expand16Sx8( mkexpr(aEvn), &aEE, &aEO ); // (15,11...),(13, 9...)
7262 expand16Sx8( mkexpr(aOdd), &aOE, &aOO ); // (14,10...),(12, 8...)
7263
7264 /* break V128 to 4xI32's, sign-extending to I64's */
7265 breakV128to4x64S( mkexpr(aEE), &a15, &a11, &a7, &a3 );
7266 breakV128to4x64S( mkexpr(aOE), &a14, &a10, &a6, &a2 );
7267 breakV128to4x64S( mkexpr(aEO), &a13, &a9, &a5, &a1 );
7268 breakV128to4x64S( mkexpr(aOO), &a12, &a8, &a4, &a0 );
7269 breakV128to4x64S( mkexpr(vB), &b3, &b2, &b1, &b0 );
7270
7271 /* add lanes */
7272 assign( z3, binop(Iop_Add64, mkexpr(b3),
cerion5b2325f2005-12-23 00:55:09 +00007273 binop(Iop_Add64,
7274 binop(Iop_Add64, mkexpr(a15), mkexpr(a14)),
7275 binop(Iop_Add64, mkexpr(a13), mkexpr(a12)))) );
cerion4a49b032005-11-08 16:23:07 +00007276 assign( z2, binop(Iop_Add64, mkexpr(b2),
cerion5b2325f2005-12-23 00:55:09 +00007277 binop(Iop_Add64,
7278 binop(Iop_Add64, mkexpr(a11), mkexpr(a10)),
7279 binop(Iop_Add64, mkexpr(a9), mkexpr(a8)))) );
cerion4a49b032005-11-08 16:23:07 +00007280 assign( z1, binop(Iop_Add64, mkexpr(b1),
cerion5b2325f2005-12-23 00:55:09 +00007281 binop(Iop_Add64,
7282 binop(Iop_Add64, mkexpr(a7), mkexpr(a6)),
7283 binop(Iop_Add64, mkexpr(a5), mkexpr(a4)))) );
cerion4a49b032005-11-08 16:23:07 +00007284 assign( z0, binop(Iop_Add64, mkexpr(b0),
cerion5b2325f2005-12-23 00:55:09 +00007285 binop(Iop_Add64,
7286 binop(Iop_Add64, mkexpr(a3), mkexpr(a2)),
7287 binop(Iop_Add64, mkexpr(a1), mkexpr(a0)))) );
cerion4a49b032005-11-08 16:23:07 +00007288
7289 /* saturate-narrow to 32bit, and combine to V128 */
7290 putVReg( vD_addr, mkV128from4x64S( mkexpr(z3), mkexpr(z2),
7291 mkexpr(z1), mkexpr(z0)) );
7292 break;
7293 }
7294 case 0x648: { // vsum4shs (Sum Partial (1/4) SHW Saturate, AV p274)
cerion32aad402005-09-10 12:02:24 +00007295 DIP("vsum4shs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion32aad402005-09-10 12:02:24 +00007296
cerion4a49b032005-11-08 16:23:07 +00007297 /* vA: V128_16Sx8 -> 2 x V128_32Sx4, sign-extended */
7298 expand16Sx8( mkexpr(vA), &aEvn, &aOdd ); // (7,5...),(6,4...)
7299
7300 /* break V128 to 4xI32's, sign-extending to I64's */
7301 breakV128to4x64S( mkexpr(aEvn), &a7, &a5, &a3, &a1 );
7302 breakV128to4x64S( mkexpr(aOdd), &a6, &a4, &a2, &a0 );
7303 breakV128to4x64S( mkexpr(vB), &b3, &b2, &b1, &b0 );
7304
7305 /* add lanes */
7306 assign( z3, binop(Iop_Add64, mkexpr(b3),
7307 binop(Iop_Add64, mkexpr(a7), mkexpr(a6))));
7308 assign( z2, binop(Iop_Add64, mkexpr(b2),
7309 binop(Iop_Add64, mkexpr(a5), mkexpr(a4))));
7310 assign( z1, binop(Iop_Add64, mkexpr(b1),
7311 binop(Iop_Add64, mkexpr(a3), mkexpr(a2))));
7312 assign( z0, binop(Iop_Add64, mkexpr(b0),
7313 binop(Iop_Add64, mkexpr(a1), mkexpr(a0))));
7314
7315 /* saturate-narrow to 32bit, and combine to V128 */
7316 putVReg( vD_addr, mkV128from4x64S( mkexpr(z3), mkexpr(z2),
7317 mkexpr(z1), mkexpr(z0)) );
7318 break;
7319 }
7320 case 0x688: { // vsum2sws (Sum Partial (1/2) SW Saturate, AV p272)
cerion32aad402005-09-10 12:02:24 +00007321 DIP("vsum2sws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion32aad402005-09-10 12:02:24 +00007322
cerion4a49b032005-11-08 16:23:07 +00007323 /* break V128 to 4xI32's, sign-extending to I64's */
7324 breakV128to4x64S( mkexpr(vA), &a3, &a2, &a1, &a0 );
7325 breakV128to4x64S( mkexpr(vB), &b3, &b2, &b1, &b0 );
7326
7327 /* add lanes */
7328 assign( z2, binop(Iop_Add64, mkexpr(b2),
7329 binop(Iop_Add64, mkexpr(a3), mkexpr(a2))) );
7330 assign( z0, binop(Iop_Add64, mkexpr(b0),
7331 binop(Iop_Add64, mkexpr(a1), mkexpr(a0))) );
7332
7333 /* saturate-narrow to 32bit, and combine to V128 */
7334 putVReg( vD_addr, mkV128from4x64S( mkU64(0), mkexpr(z2),
7335 mkU64(0), mkexpr(z0)) );
7336 break;
7337 }
7338 case 0x788: { // vsumsws (Sum SW Saturate, AV p271)
cerion32aad402005-09-10 12:02:24 +00007339 DIP("vsumsws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion32aad402005-09-10 12:02:24 +00007340
cerion4a49b032005-11-08 16:23:07 +00007341 /* break V128 to 4xI32's, sign-extending to I64's */
7342 breakV128to4x64S( mkexpr(vA), &a3, &a2, &a1, &a0 );
7343 breakV128to4x64S( mkexpr(vB), &b3, &b2, &b1, &b0 );
7344
7345 /* add lanes */
7346 assign( z0, binop(Iop_Add64, mkexpr(b0),
cerion5b2325f2005-12-23 00:55:09 +00007347 binop(Iop_Add64,
7348 binop(Iop_Add64, mkexpr(a3), mkexpr(a2)),
7349 binop(Iop_Add64, mkexpr(a1), mkexpr(a0)))) );
cerion4a49b032005-11-08 16:23:07 +00007350
7351 /* saturate-narrow to 32bit, and combine to V128 */
7352 putVReg( vD_addr, mkV128from4x64S( mkU64(0), mkU64(0),
7353 mkU64(0), mkexpr(z0)) );
7354 break;
7355 }
cerion32aad402005-09-10 12:02:24 +00007356 default:
cerion5b2325f2005-12-23 00:55:09 +00007357 vex_printf("dis_av_arith(ppc)(opc2=0x%x)\n", opc2);
cerion32aad402005-09-10 12:02:24 +00007358 return False;
7359 }
7360 return True;
7361}
7362
7363/*
7364 AltiVec Logic Instructions
7365*/
7366static Bool dis_av_logic ( UInt theInstr )
7367{
cerion76de5cf2005-11-18 18:25:12 +00007368 /* VX-Form */
7369 UChar opc1 = ifieldOPC(theInstr);
7370 UChar vD_addr = ifieldRegDS(theInstr);
7371 UChar vA_addr = ifieldRegA(theInstr);
7372 UChar vB_addr = ifieldRegB(theInstr);
7373 UInt opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00007374
cerion225a0342005-09-12 20:49:09 +00007375 IRTemp vA = newTemp(Ity_V128);
7376 IRTemp vB = newTemp(Ity_V128);
7377 assign( vA, getVReg(vA_addr));
7378 assign( vB, getVReg(vB_addr));
7379
cerion32aad402005-09-10 12:02:24 +00007380 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00007381 vex_printf("dis_av_logic(ppc)(opc1 != 0x4)\n");
cerion32aad402005-09-10 12:02:24 +00007382 return False;
7383 }
7384
7385 switch (opc2) {
7386 case 0x404: // vand (And, AV p147)
7387 DIP("vand v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion225a0342005-09-12 20:49:09 +00007388 putVReg( vD_addr, binop(Iop_AndV128, mkexpr(vA), mkexpr(vB)) );
7389 break;
cerion32aad402005-09-10 12:02:24 +00007390
7391 case 0x444: // vandc (And, AV p148)
7392 DIP("vandc v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion6e7a0ea2005-09-13 13:34:09 +00007393 putVReg( vD_addr, binop(Iop_AndV128, mkexpr(vA),
cerion76de5cf2005-11-18 18:25:12 +00007394 unop(Iop_NotV128, mkexpr(vB))) );
cerion6e7a0ea2005-09-13 13:34:09 +00007395 break;
cerion32aad402005-09-10 12:02:24 +00007396
7397 case 0x484: // vor (Or, AV p217)
7398 DIP("vor v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion225a0342005-09-12 20:49:09 +00007399 putVReg( vD_addr, binop(Iop_OrV128, mkexpr(vA), mkexpr(vB)) );
7400 break;
cerion32aad402005-09-10 12:02:24 +00007401
7402 case 0x4C4: // vxor (Xor, AV p282)
7403 DIP("vxor v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion225a0342005-09-12 20:49:09 +00007404 putVReg( vD_addr, binop(Iop_XorV128, mkexpr(vA), mkexpr(vB)) );
7405 break;
cerion32aad402005-09-10 12:02:24 +00007406
7407 case 0x504: // vnor (Nor, AV p216)
7408 DIP("vnor v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion6e7a0ea2005-09-13 13:34:09 +00007409 putVReg( vD_addr,
7410 unop(Iop_NotV128, binop(Iop_OrV128, mkexpr(vA), mkexpr(vB))) );
7411 break;
cerion32aad402005-09-10 12:02:24 +00007412
7413 default:
cerion5b2325f2005-12-23 00:55:09 +00007414 vex_printf("dis_av_logic(ppc)(opc2=0x%x)\n", opc2);
cerion32aad402005-09-10 12:02:24 +00007415 return False;
7416 }
7417 return True;
7418}
7419
7420/*
7421 AltiVec Compare Instructions
7422*/
7423static Bool dis_av_cmp ( UInt theInstr )
7424{
cerion76de5cf2005-11-18 18:25:12 +00007425 /* VXR-Form */
7426 UChar opc1 = ifieldOPC(theInstr);
7427 UChar vD_addr = ifieldRegDS(theInstr);
7428 UChar vA_addr = ifieldRegA(theInstr);
7429 UChar vB_addr = ifieldRegB(theInstr);
7430 UChar flag_rC = ifieldBIT10(theInstr);
7431 UInt opc2 = IFIELD( theInstr, 0, 10 );
cerion32aad402005-09-10 12:02:24 +00007432
cerion0c439222005-09-15 14:22:58 +00007433 IRTemp vA = newTemp(Ity_V128);
7434 IRTemp vB = newTemp(Ity_V128);
7435 IRTemp vD = newTemp(Ity_V128);
7436 assign( vA, getVReg(vA_addr));
7437 assign( vB, getVReg(vB_addr));
7438
cerion32aad402005-09-10 12:02:24 +00007439 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00007440 vex_printf("dis_av_cmp(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00007441 return False;
7442 }
7443
7444 switch (opc2) {
7445 case 0x006: // vcmpequb (Compare Equal-to Unsigned B, AV p160)
cerion5b2325f2005-12-23 00:55:09 +00007446 DIP("vcmpequb%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7447 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007448 assign( vD, binop(Iop_CmpEQ8x16, mkexpr(vA), mkexpr(vB)) );
7449 break;
cerion32aad402005-09-10 12:02:24 +00007450
7451 case 0x046: // vcmpequh (Compare Equal-to Unsigned HW, AV p161)
cerion5b2325f2005-12-23 00:55:09 +00007452 DIP("vcmpequh%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7453 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007454 assign( vD, binop(Iop_CmpEQ16x8, mkexpr(vA), mkexpr(vB)) );
7455 break;
cerion32aad402005-09-10 12:02:24 +00007456
7457 case 0x086: // vcmpequw (Compare Equal-to Unsigned W, AV p162)
cerion5b2325f2005-12-23 00:55:09 +00007458 DIP("vcmpequw%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7459 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007460 assign( vD, binop(Iop_CmpEQ32x4, mkexpr(vA), mkexpr(vB)) );
7461 break;
cerion32aad402005-09-10 12:02:24 +00007462
7463 case 0x206: // vcmpgtub (Compare Greater-than Unsigned B, AV p168)
cerion5b2325f2005-12-23 00:55:09 +00007464 DIP("vcmpgtub%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7465 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007466 assign( vD, binop(Iop_CmpGT8Ux16, mkexpr(vA), mkexpr(vB)) );
7467 break;
cerion32aad402005-09-10 12:02:24 +00007468
7469 case 0x246: // vcmpgtuh (Compare Greater-than Unsigned HW, AV p169)
cerion5b2325f2005-12-23 00:55:09 +00007470 DIP("vcmpgtuh%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7471 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007472 assign( vD, binop(Iop_CmpGT16Ux8, mkexpr(vA), mkexpr(vB)) );
7473 break;
cerion32aad402005-09-10 12:02:24 +00007474
7475 case 0x286: // vcmpgtuw (Compare Greater-than Unsigned W, AV p170)
cerion5b2325f2005-12-23 00:55:09 +00007476 DIP("vcmpgtuw%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7477 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007478 assign( vD, binop(Iop_CmpGT32Ux4, mkexpr(vA), mkexpr(vB)) );
7479 break;
cerion32aad402005-09-10 12:02:24 +00007480
7481 case 0x306: // vcmpgtsb (Compare Greater-than Signed B, AV p165)
cerion5b2325f2005-12-23 00:55:09 +00007482 DIP("vcmpgtsb%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7483 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007484 assign( vD, binop(Iop_CmpGT8Sx16, mkexpr(vA), mkexpr(vB)) );
7485 break;
cerion32aad402005-09-10 12:02:24 +00007486
7487 case 0x346: // vcmpgtsh (Compare Greater-than Signed HW, AV p166)
cerion5b2325f2005-12-23 00:55:09 +00007488 DIP("vcmpgtsh%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7489 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007490 assign( vD, binop(Iop_CmpGT16Sx8, mkexpr(vA), mkexpr(vB)) );
7491 break;
cerion32aad402005-09-10 12:02:24 +00007492
7493 case 0x386: // vcmpgtsw (Compare Greater-than Signed W, AV p167)
cerion5b2325f2005-12-23 00:55:09 +00007494 DIP("vcmpgtsw%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7495 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007496 assign( vD, binop(Iop_CmpGT32Sx4, mkexpr(vA), mkexpr(vB)) );
7497 break;
cerion32aad402005-09-10 12:02:24 +00007498
7499 default:
cerion5b2325f2005-12-23 00:55:09 +00007500 vex_printf("dis_av_cmp(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00007501 return False;
7502 }
cerion0c439222005-09-15 14:22:58 +00007503
7504 putVReg( vD_addr, mkexpr(vD) );
7505
cerion76de5cf2005-11-18 18:25:12 +00007506 if (flag_rC) {
cerion8ea0d3e2005-11-14 00:44:47 +00007507 set_AV_CR6( mkexpr(vD), True );
cerion0c439222005-09-15 14:22:58 +00007508 }
cerion32aad402005-09-10 12:02:24 +00007509 return True;
7510}
7511
7512/*
7513 AltiVec Multiply-Sum Instructions
7514*/
7515static Bool dis_av_multarith ( UInt theInstr )
7516{
cerion76de5cf2005-11-18 18:25:12 +00007517 /* VA-Form */
7518 UChar opc1 = ifieldOPC(theInstr);
7519 UChar vD_addr = ifieldRegDS(theInstr);
7520 UChar vA_addr = ifieldRegA(theInstr);
7521 UChar vB_addr = ifieldRegB(theInstr);
7522 UChar vC_addr = ifieldRegC(theInstr);
7523 UChar opc2 = toUChar( IFIELD( theInstr, 0, 6 ) );
cerion32aad402005-09-10 12:02:24 +00007524
cerion4a49b032005-11-08 16:23:07 +00007525 IRTemp vA = newTemp(Ity_V128);
7526 IRTemp vB = newTemp(Ity_V128);
7527 IRTemp vC = newTemp(Ity_V128);
sewardj197bd172005-10-12 11:34:33 +00007528 IRTemp zeros = newTemp(Ity_V128);
cerion4a49b032005-11-08 16:23:07 +00007529 IRTemp aLo = newTemp(Ity_V128);
7530 IRTemp bLo = newTemp(Ity_V128);
7531 IRTemp cLo = newTemp(Ity_V128);
7532 IRTemp zLo = newTemp(Ity_V128);
7533 IRTemp aHi = newTemp(Ity_V128);
7534 IRTemp bHi = newTemp(Ity_V128);
7535 IRTemp cHi = newTemp(Ity_V128);
7536 IRTemp zHi = newTemp(Ity_V128);
7537 IRTemp abEvn = newTemp(Ity_V128);
7538 IRTemp abOdd = newTemp(Ity_V128);
7539 IRTemp z3 = newTemp(Ity_I64);
7540 IRTemp z2 = newTemp(Ity_I64);
7541 IRTemp z1 = newTemp(Ity_I64);
7542 IRTemp z0 = newTemp(Ity_I64);
7543 IRTemp ab7, ab6, ab5, ab4, ab3, ab2, ab1, ab0;
7544 IRTemp c3, c2, c1, c0;
7545
7546 ab7 = ab6 = ab5 = ab4 = ab3 = ab2 = ab1 = ab0 = IRTemp_INVALID;
7547 c3 = c2 = c1 = c0 = IRTemp_INVALID;
7548
cerion6f1cc0f2005-09-16 16:02:11 +00007549 assign( vA, getVReg(vA_addr));
7550 assign( vB, getVReg(vB_addr));
7551 assign( vC, getVReg(vC_addr));
cerion4a49b032005-11-08 16:23:07 +00007552 assign( zeros, unop(Iop_Dup32x4, mkU32(0)) );
cerion6f1cc0f2005-09-16 16:02:11 +00007553
cerion32aad402005-09-10 12:02:24 +00007554 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00007555 vex_printf("dis_av_multarith(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00007556 return False;
7557 }
7558
7559 switch (opc2) {
cerion32aad402005-09-10 12:02:24 +00007560 /* Multiply-Add */
cerion5b2325f2005-12-23 00:55:09 +00007561 case 0x20: { // vmhaddshs (Mult Hi, Add Signed HW Saturate, AV p185)
cerion6f1cc0f2005-09-16 16:02:11 +00007562 IRTemp cSigns = newTemp(Ity_V128);
cerion5b2325f2005-12-23 00:55:09 +00007563 DIP("vmhaddshs v%d,v%d,v%d,v%d\n",
7564 vD_addr, vA_addr, vB_addr, vC_addr);
7565 assign(cSigns, binop(Iop_CmpGT16Sx8, mkexpr(zeros), mkexpr(vC)));
7566 assign(aLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vA)));
7567 assign(bLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vB)));
7568 assign(cLo, binop(Iop_InterleaveLO16x8, mkexpr(cSigns),mkexpr(vC)));
7569 assign(aHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vA)));
7570 assign(bHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vB)));
7571 assign(cHi, binop(Iop_InterleaveHI16x8, mkexpr(cSigns),mkexpr(vC)));
cerion32aad402005-09-10 12:02:24 +00007572
cerion24d06f12005-11-09 21:34:20 +00007573 assign( zLo, binop(Iop_Add32x4, mkexpr(cLo),
cerion6f1cc0f2005-09-16 16:02:11 +00007574 binop(Iop_SarN32x4,
cerion1ac656a2005-11-04 19:44:48 +00007575 binop(Iop_MullEven16Sx8,
cerion24d06f12005-11-09 21:34:20 +00007576 mkexpr(aLo), mkexpr(bLo)),
7577 mkU8(15))) );
cerion6f1cc0f2005-09-16 16:02:11 +00007578
cerion24d06f12005-11-09 21:34:20 +00007579 assign( zHi, binop(Iop_Add32x4, mkexpr(cHi),
cerion6f1cc0f2005-09-16 16:02:11 +00007580 binop(Iop_SarN32x4,
cerion1ac656a2005-11-04 19:44:48 +00007581 binop(Iop_MullEven16Sx8,
cerion24d06f12005-11-09 21:34:20 +00007582 mkexpr(aHi), mkexpr(bHi)),
7583 mkU8(15))) );
cerion6f1cc0f2005-09-16 16:02:11 +00007584
cerion5b2325f2005-12-23 00:55:09 +00007585 putVReg( vD_addr,
7586 binop(Iop_QNarrow32Sx4, mkexpr(zHi), mkexpr(zLo)) );
cerion6f1cc0f2005-09-16 16:02:11 +00007587 break;
7588 }
cerion5b2325f2005-12-23 00:55:09 +00007589 case 0x21: { // vmhraddshs (Mult High Round, Add Signed HW Saturate, AV p186)
cerion6f1cc0f2005-09-16 16:02:11 +00007590 IRTemp zKonst = newTemp(Ity_V128);
cerion6f1cc0f2005-09-16 16:02:11 +00007591 IRTemp cSigns = newTemp(Ity_V128);
cerion5b2325f2005-12-23 00:55:09 +00007592 DIP("vmhraddshs v%d,v%d,v%d,v%d\n",
7593 vD_addr, vA_addr, vB_addr, vC_addr);
7594 assign(cSigns, binop(Iop_CmpGT16Sx8, mkexpr(zeros), mkexpr(vC)) );
7595 assign(aLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vA)));
7596 assign(bLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vB)));
7597 assign(cLo, binop(Iop_InterleaveLO16x8, mkexpr(cSigns),mkexpr(vC)));
7598 assign(aHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vA)));
7599 assign(bHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vB)));
7600 assign(cHi, binop(Iop_InterleaveHI16x8, mkexpr(cSigns),mkexpr(vC)));
cerion32aad402005-09-10 12:02:24 +00007601
cerion6f1cc0f2005-09-16 16:02:11 +00007602 /* shifting our const avoids store/load version of Dup */
cerion4a49b032005-11-08 16:23:07 +00007603 assign( zKonst, binop(Iop_ShlN32x4, unop(Iop_Dup32x4, mkU32(0x1)),
7604 mkU8(14)) );
cerion6f1cc0f2005-09-16 16:02:11 +00007605
cerion24d06f12005-11-09 21:34:20 +00007606 assign( zLo, binop(Iop_Add32x4, mkexpr(cLo),
cerion6f1cc0f2005-09-16 16:02:11 +00007607 binop(Iop_SarN32x4,
7608 binop(Iop_Add32x4, mkexpr(zKonst),
cerion1ac656a2005-11-04 19:44:48 +00007609 binop(Iop_MullEven16Sx8,
cerion24d06f12005-11-09 21:34:20 +00007610 mkexpr(aLo), mkexpr(bLo))),
7611 mkU8(15))) );
cerion6f1cc0f2005-09-16 16:02:11 +00007612
cerion24d06f12005-11-09 21:34:20 +00007613 assign( zHi, binop(Iop_Add32x4, mkexpr(cHi),
cerion6f1cc0f2005-09-16 16:02:11 +00007614 binop(Iop_SarN32x4,
7615 binop(Iop_Add32x4, mkexpr(zKonst),
cerion1ac656a2005-11-04 19:44:48 +00007616 binop(Iop_MullEven16Sx8,
cerion24d06f12005-11-09 21:34:20 +00007617 mkexpr(aHi), mkexpr(bHi))),
7618 mkU8(15))) );
cerion6f1cc0f2005-09-16 16:02:11 +00007619
7620 putVReg( vD_addr, binop(Iop_QNarrow32Sx4, mkexpr(zHi), mkexpr(zLo)) );
7621 break;
7622 }
cerion5b2325f2005-12-23 00:55:09 +00007623 case 0x22: { // vmladduhm (Mult Low, Add Unsigned HW Modulo, AV p194)
7624 DIP("vmladduhm v%d,v%d,v%d,v%d\n",
7625 vD_addr, vA_addr, vB_addr, vC_addr);
7626 assign(aLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vA)));
7627 assign(bLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vB)));
7628 assign(cLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vC)));
7629 assign(aHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vA)));
7630 assign(bHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vB)));
7631 assign(cHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vC)));
7632 assign(zLo, binop(Iop_Add32x4,
7633 binop(Iop_MullEven16Ux8, mkexpr(aLo), mkexpr(bLo)),
7634 mkexpr(cLo)) );
7635 assign(zHi, binop(Iop_Add32x4,
7636 binop(Iop_MullEven16Ux8, mkexpr(aHi), mkexpr(bHi)),
7637 mkexpr(cHi)));
7638 putVReg(vD_addr, binop(Iop_Narrow32x4, mkexpr(zHi), mkexpr(zLo)));
cerion6f1cc0f2005-09-16 16:02:11 +00007639 break;
7640 }
cerion32aad402005-09-10 12:02:24 +00007641
7642
7643 /* Multiply-Sum */
cerion6f1cc0f2005-09-16 16:02:11 +00007644 case 0x24: { // vmsumubm (Multiply Sum Unsigned B Modulo, AV p204)
cerion4a49b032005-11-08 16:23:07 +00007645 IRTemp abEE, abEO, abOE, abOO;
7646 abEE = abEO = abOE = abOO = IRTemp_INVALID;
cerion5b2325f2005-12-23 00:55:09 +00007647 DIP("vmsumubm v%d,v%d,v%d,v%d\n",
7648 vD_addr, vA_addr, vB_addr, vC_addr);
cerion32aad402005-09-10 12:02:24 +00007649
cerion4a49b032005-11-08 16:23:07 +00007650 /* multiply vA,vB (unsigned, widening) */
cerion24d06f12005-11-09 21:34:20 +00007651 assign( abEvn, MK_Iop_MullOdd8Ux16( mkexpr(vA), mkexpr(vB) ));
7652 assign( abOdd, binop(Iop_MullEven8Ux16, mkexpr(vA), mkexpr(vB)) );
cerion4a49b032005-11-08 16:23:07 +00007653
7654 /* evn,odd: V128_16Ux8 -> 2 x V128_32Ux4, zero-extended */
7655 expand16Ux8( mkexpr(abEvn), &abEE, &abEO );
7656 expand16Ux8( mkexpr(abOdd), &abOE, &abOO );
7657
cerion6f1cc0f2005-09-16 16:02:11 +00007658 putVReg( vD_addr,
cerion5b2325f2005-12-23 00:55:09 +00007659 binop(Iop_Add32x4, mkexpr(vC),
7660 binop(Iop_Add32x4,
7661 binop(Iop_Add32x4, mkexpr(abEE), mkexpr(abEO)),
7662 binop(Iop_Add32x4, mkexpr(abOE), mkexpr(abOO)))) );
cerion6f1cc0f2005-09-16 16:02:11 +00007663 break;
7664 }
cerion4a49b032005-11-08 16:23:07 +00007665 case 0x25: { // vmsummbm (Multiply Sum Mixed-Sign B Modulo, AV p201)
7666 IRTemp aEvn, aOdd, bEvn, bOdd;
7667 IRTemp abEE = newTemp(Ity_V128);
7668 IRTemp abEO = newTemp(Ity_V128);
7669 IRTemp abOE = newTemp(Ity_V128);
7670 IRTemp abOO = newTemp(Ity_V128);
7671 aEvn = aOdd = bEvn = bOdd = IRTemp_INVALID;
cerion5b2325f2005-12-23 00:55:09 +00007672 DIP("vmsummbm v%d,v%d,v%d,v%d\n",
7673 vD_addr, vA_addr, vB_addr, vC_addr);
cerion32aad402005-09-10 12:02:24 +00007674
cerion4a49b032005-11-08 16:23:07 +00007675 /* sign-extend vA, zero-extend vB, for mixed-sign multiply
7676 (separating out adjacent lanes to different vectors) */
7677 expand8Sx16( mkexpr(vA), &aEvn, &aOdd );
7678 expand8Ux16( mkexpr(vB), &bEvn, &bOdd );
7679
7680 /* multiply vA, vB, again separating adjacent lanes */
cerion24d06f12005-11-09 21:34:20 +00007681 assign( abEE, MK_Iop_MullOdd16Sx8( mkexpr(aEvn), mkexpr(bEvn) ));
7682 assign( abEO, binop(Iop_MullEven16Sx8, mkexpr(aEvn), mkexpr(bEvn)) );
7683 assign( abOE, MK_Iop_MullOdd16Sx8( mkexpr(aOdd), mkexpr(bOdd) ));
7684 assign( abOO, binop(Iop_MullEven16Sx8, mkexpr(aOdd), mkexpr(bOdd)) );
cerion4a49b032005-11-08 16:23:07 +00007685
7686 /* add results together, + vC */
7687 putVReg( vD_addr,
cerion5b2325f2005-12-23 00:55:09 +00007688 binop(Iop_QAdd32Sx4, mkexpr(vC),
7689 binop(Iop_QAdd32Sx4,
7690 binop(Iop_QAdd32Sx4, mkexpr(abEE), mkexpr(abEO)),
7691 binop(Iop_QAdd32Sx4, mkexpr(abOE), mkexpr(abOO)))) );
cerion4a49b032005-11-08 16:23:07 +00007692 break;
7693 }
cerion6f1cc0f2005-09-16 16:02:11 +00007694 case 0x26: { // vmsumuhm (Multiply Sum Unsigned HW Modulo, AV p205)
cerion5b2325f2005-12-23 00:55:09 +00007695 DIP("vmsumuhm v%d,v%d,v%d,v%d\n",
7696 vD_addr, vA_addr, vB_addr, vC_addr);
cerion24d06f12005-11-09 21:34:20 +00007697 assign( abEvn, MK_Iop_MullOdd16Ux8( mkexpr(vA), mkexpr(vB) ));
7698 assign( abOdd, binop(Iop_MullEven16Ux8, mkexpr(vA), mkexpr(vB)) );
cerion6f1cc0f2005-09-16 16:02:11 +00007699 putVReg( vD_addr,
cerion5b2325f2005-12-23 00:55:09 +00007700 binop(Iop_Add32x4, mkexpr(vC),
7701 binop(Iop_Add32x4, mkexpr(abEvn), mkexpr(abOdd))) );
cerion6f1cc0f2005-09-16 16:02:11 +00007702 break;
7703 }
cerion4a49b032005-11-08 16:23:07 +00007704 case 0x27: { // vmsumuhs (Multiply Sum Unsigned HW Saturate, AV p206)
cerion5b2325f2005-12-23 00:55:09 +00007705 DIP("vmsumuhs v%d,v%d,v%d,v%d\n",
7706 vD_addr, vA_addr, vB_addr, vC_addr);
cerion4a49b032005-11-08 16:23:07 +00007707 /* widening multiply, separating lanes */
cerion24d06f12005-11-09 21:34:20 +00007708 assign( abEvn, MK_Iop_MullOdd16Ux8(mkexpr(vA), mkexpr(vB) ));
7709 assign( abOdd, binop(Iop_MullEven16Ux8, mkexpr(vA), mkexpr(vB)) );
cerion32aad402005-09-10 12:02:24 +00007710
cerion4a49b032005-11-08 16:23:07 +00007711 /* break V128 to 4xI32's, zero-extending to I64's */
7712 breakV128to4x64U( mkexpr(abEvn), &ab7, &ab5, &ab3, &ab1 );
7713 breakV128to4x64U( mkexpr(abOdd), &ab6, &ab4, &ab2, &ab0 );
7714 breakV128to4x64U( mkexpr(vC), &c3, &c2, &c1, &c0 );
7715
7716 /* add lanes */
7717 assign( z3, binop(Iop_Add64, mkexpr(c3),
7718 binop(Iop_Add64, mkexpr(ab7), mkexpr(ab6))));
7719 assign( z2, binop(Iop_Add64, mkexpr(c2),
7720 binop(Iop_Add64, mkexpr(ab5), mkexpr(ab4))));
7721 assign( z1, binop(Iop_Add64, mkexpr(c1),
7722 binop(Iop_Add64, mkexpr(ab3), mkexpr(ab2))));
7723 assign( z0, binop(Iop_Add64, mkexpr(c0),
7724 binop(Iop_Add64, mkexpr(ab1), mkexpr(ab0))));
7725
7726 /* saturate-narrow to 32bit, and combine to V128 */
7727 putVReg( vD_addr, mkV128from4x64U( mkexpr(z3), mkexpr(z2),
7728 mkexpr(z1), mkexpr(z0)) );
7729
cerion6f1cc0f2005-09-16 16:02:11 +00007730 break;
7731 }
cerion4a49b032005-11-08 16:23:07 +00007732 case 0x28: { // vmsumshm (Multiply Sum Signed HW Modulo, AV p202)
cerion5b2325f2005-12-23 00:55:09 +00007733 DIP("vmsumshm v%d,v%d,v%d,v%d\n",
7734 vD_addr, vA_addr, vB_addr, vC_addr);
cerion24d06f12005-11-09 21:34:20 +00007735 assign( abEvn, MK_Iop_MullOdd16Sx8( mkexpr(vA), mkexpr(vB) ));
7736 assign( abOdd, binop(Iop_MullEven16Sx8, mkexpr(vA), mkexpr(vB)) );
cerion4a49b032005-11-08 16:23:07 +00007737 putVReg( vD_addr,
cerion5b2325f2005-12-23 00:55:09 +00007738 binop(Iop_Add32x4, mkexpr(vC),
7739 binop(Iop_Add32x4, mkexpr(abOdd), mkexpr(abEvn))) );
cerion4a49b032005-11-08 16:23:07 +00007740 break;
7741 }
7742 case 0x29: { // vmsumshs (Multiply Sum Signed HW Saturate, AV p203)
cerion5b2325f2005-12-23 00:55:09 +00007743 DIP("vmsumshs v%d,v%d,v%d,v%d\n",
7744 vD_addr, vA_addr, vB_addr, vC_addr);
cerion4a49b032005-11-08 16:23:07 +00007745 /* widening multiply, separating lanes */
cerion24d06f12005-11-09 21:34:20 +00007746 assign( abEvn, MK_Iop_MullOdd16Sx8( mkexpr(vA), mkexpr(vB) ));
7747 assign( abOdd, binop(Iop_MullEven16Sx8, mkexpr(vA), mkexpr(vB)) );
cerion32aad402005-09-10 12:02:24 +00007748
cerion4a49b032005-11-08 16:23:07 +00007749 /* break V128 to 4xI32's, sign-extending to I64's */
7750 breakV128to4x64S( mkexpr(abEvn), &ab7, &ab5, &ab3, &ab1 );
7751 breakV128to4x64S( mkexpr(abOdd), &ab6, &ab4, &ab2, &ab0 );
7752 breakV128to4x64S( mkexpr(vC), &c3, &c2, &c1, &c0 );
7753
7754 /* add lanes */
7755 assign( z3, binop(Iop_Add64, mkexpr(c3),
7756 binop(Iop_Add64, mkexpr(ab7), mkexpr(ab6))));
7757 assign( z2, binop(Iop_Add64, mkexpr(c2),
7758 binop(Iop_Add64, mkexpr(ab5), mkexpr(ab4))));
7759 assign( z1, binop(Iop_Add64, mkexpr(c1),
7760 binop(Iop_Add64, mkexpr(ab3), mkexpr(ab2))));
7761 assign( z0, binop(Iop_Add64, mkexpr(c0),
7762 binop(Iop_Add64, mkexpr(ab1), mkexpr(ab0))));
7763
7764 /* saturate-narrow to 32bit, and combine to V128 */
7765 putVReg( vD_addr, mkV128from4x64S( mkexpr(z3), mkexpr(z2),
7766 mkexpr(z1), mkexpr(z0)) );
7767 break;
7768 }
cerion32aad402005-09-10 12:02:24 +00007769 default:
cerion5b2325f2005-12-23 00:55:09 +00007770 vex_printf("dis_av_multarith(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00007771 return False;
7772 }
7773 return True;
7774}
7775
7776/*
7777 AltiVec Shift/Rotate Instructions
7778*/
7779static Bool dis_av_shift ( UInt theInstr )
7780{
cerion76de5cf2005-11-18 18:25:12 +00007781 /* VX-Form */
7782 UChar opc1 = ifieldOPC(theInstr);
7783 UChar vD_addr = ifieldRegDS(theInstr);
7784 UChar vA_addr = ifieldRegA(theInstr);
7785 UChar vB_addr = ifieldRegB(theInstr);
7786 UInt opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00007787
cerion27b3d7e2005-09-14 20:35:47 +00007788 IRTemp vA = newTemp(Ity_V128);
7789 IRTemp vB = newTemp(Ity_V128);
7790 assign( vA, getVReg(vA_addr));
7791 assign( vB, getVReg(vB_addr));
7792
cerion32aad402005-09-10 12:02:24 +00007793 if (opc1 != 0x4){
cerion5b2325f2005-12-23 00:55:09 +00007794 vex_printf("dis_av_shift(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00007795 return False;
7796 }
7797
7798 switch (opc2) {
7799 /* Rotate */
7800 case 0x004: // vrlb (Rotate Left Integer B, AV p234)
7801 DIP("vrlb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
sewardj1bee5612005-11-10 18:10:58 +00007802 putVReg( vD_addr, binop(Iop_Rol8x16, mkexpr(vA), mkexpr(vB)) );
cerion0a7b4f42005-09-16 07:54:40 +00007803 break;
cerion32aad402005-09-10 12:02:24 +00007804
7805 case 0x044: // vrlh (Rotate Left Integer HW, AV p235)
7806 DIP("vrlh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
sewardj1bee5612005-11-10 18:10:58 +00007807 putVReg( vD_addr, binop(Iop_Rol16x8, mkexpr(vA), mkexpr(vB)) );
cerion0a7b4f42005-09-16 07:54:40 +00007808 break;
cerion32aad402005-09-10 12:02:24 +00007809
7810 case 0x084: // vrlw (Rotate Left Integer W, AV p236)
7811 DIP("vrlw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
sewardj1bee5612005-11-10 18:10:58 +00007812 putVReg( vD_addr, binop(Iop_Rol32x4, mkexpr(vA), mkexpr(vB)) );
cerion0a7b4f42005-09-16 07:54:40 +00007813 break;
cerion32aad402005-09-10 12:02:24 +00007814
7815
7816 /* Shift Left */
7817 case 0x104: // vslb (Shift Left Integer B, AV p240)
7818 DIP("vslb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007819 putVReg( vD_addr, binop(Iop_Shl8x16, mkexpr(vA), mkexpr(vB)) );
7820 break;
cerion32aad402005-09-10 12:02:24 +00007821
7822 case 0x144: // vslh (Shift Left Integer HW, AV p242)
7823 DIP("vslh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007824 putVReg( vD_addr, binop(Iop_Shl16x8, mkexpr(vA), mkexpr(vB)) );
7825 break;
cerion32aad402005-09-10 12:02:24 +00007826
7827 case 0x184: // vslw (Shift Left Integer W, AV p244)
7828 DIP("vslw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007829 putVReg( vD_addr, binop(Iop_Shl32x4, mkexpr(vA), mkexpr(vB)) );
7830 break;
cerion32aad402005-09-10 12:02:24 +00007831
cerion0a7b4f42005-09-16 07:54:40 +00007832 case 0x1C4: { // vsl (Shift Left, AV p239)
cerion0a7b4f42005-09-16 07:54:40 +00007833 IRTemp sh = newTemp(Ity_I8);
sewardj197bd172005-10-12 11:34:33 +00007834 DIP("vsl v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007835 assign( sh, binop(Iop_And8, mkU8(0x7),
7836 unop(Iop_32to8,
7837 unop(Iop_V128to32, mkexpr(vB)))) );
7838 putVReg( vD_addr,
7839 binop(Iop_ShlV128, mkexpr(vA), mkexpr(sh)) );
7840 break;
7841 }
7842 case 0x40C: { // vslo (Shift Left by Octet, AV p243)
cerion0a7b4f42005-09-16 07:54:40 +00007843 IRTemp sh = newTemp(Ity_I8);
sewardj197bd172005-10-12 11:34:33 +00007844 DIP("vslo v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007845 assign( sh, binop(Iop_And8, mkU8(0x78),
7846 unop(Iop_32to8,
7847 unop(Iop_V128to32, mkexpr(vB)))) );
7848 putVReg( vD_addr,
7849 binop(Iop_ShlV128, mkexpr(vA), mkexpr(sh)) );
7850 break;
7851 }
7852
cerion32aad402005-09-10 12:02:24 +00007853
7854 /* Shift Right */
7855 case 0x204: // vsrb (Shift Right B, AV p256)
7856 DIP("vsrb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007857 putVReg( vD_addr, binop(Iop_Shr8x16, mkexpr(vA), mkexpr(vB)) );
7858 break;
cerion32aad402005-09-10 12:02:24 +00007859
7860 case 0x244: // vsrh (Shift Right HW, AV p257)
7861 DIP("vsrh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007862 putVReg( vD_addr, binop(Iop_Shr16x8, mkexpr(vA), mkexpr(vB)) );
7863 break;
cerion32aad402005-09-10 12:02:24 +00007864
7865 case 0x284: // vsrw (Shift Right W, AV p259)
7866 DIP("vsrw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007867 putVReg( vD_addr, binop(Iop_Shr32x4, mkexpr(vA), mkexpr(vB)) );
7868 break;
cerion32aad402005-09-10 12:02:24 +00007869
cerion27b3d7e2005-09-14 20:35:47 +00007870 case 0x2C4: { // vsr (Shift Right, AV p251)
cerion27b3d7e2005-09-14 20:35:47 +00007871 IRTemp sh = newTemp(Ity_I8);
sewardj197bd172005-10-12 11:34:33 +00007872 DIP("vsr v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion27b3d7e2005-09-14 20:35:47 +00007873 assign( sh, binop(Iop_And8, mkU8(0x7),
7874 unop(Iop_32to8,
7875 unop(Iop_V128to32, mkexpr(vB)))) );
7876 putVReg( vD_addr,
7877 binop(Iop_ShrV128, mkexpr(vA), mkexpr(sh)) );
7878 break;
7879 }
cerion5b2325f2005-12-23 00:55:09 +00007880 case 0x304: // vsrab (Shift Right Alg B, AV p253)
cerion32aad402005-09-10 12:02:24 +00007881 DIP("vsrab v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007882 putVReg( vD_addr, binop(Iop_Sar8x16, mkexpr(vA), mkexpr(vB)) );
7883 break;
cerion32aad402005-09-10 12:02:24 +00007884
cerion5b2325f2005-12-23 00:55:09 +00007885 case 0x344: // vsrah (Shift Right Alg HW, AV p254)
cerion32aad402005-09-10 12:02:24 +00007886 DIP("vsrah v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007887 putVReg( vD_addr, binop(Iop_Sar16x8, mkexpr(vA), mkexpr(vB)) );
7888 break;
cerion32aad402005-09-10 12:02:24 +00007889
cerion5b2325f2005-12-23 00:55:09 +00007890 case 0x384: // vsraw (Shift Right Alg W, AV p255)
cerion32aad402005-09-10 12:02:24 +00007891 DIP("vsraw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007892 putVReg( vD_addr, binop(Iop_Sar32x4, mkexpr(vA), mkexpr(vB)) );
7893 break;
cerion32aad402005-09-10 12:02:24 +00007894
cerion0a7b4f42005-09-16 07:54:40 +00007895 case 0x44C: { // vsro (Shift Right by Octet, AV p258)
cerion0a7b4f42005-09-16 07:54:40 +00007896 IRTemp sh = newTemp(Ity_I8);
sewardj197bd172005-10-12 11:34:33 +00007897 DIP("vsro v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007898 assign( sh, binop(Iop_And8, mkU8(0x78),
7899 unop(Iop_32to8,
7900 unop(Iop_V128to32, mkexpr(vB)))) );
7901 putVReg( vD_addr,
7902 binop(Iop_ShrV128, mkexpr(vA), mkexpr(sh)) );
7903 break;
7904 }
cerion32aad402005-09-10 12:02:24 +00007905
7906 default:
cerion5b2325f2005-12-23 00:55:09 +00007907 vex_printf("dis_av_shift(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00007908 return False;
7909 }
7910 return True;
7911}
7912
7913/*
7914 AltiVec Permute Instructions
7915*/
7916static Bool dis_av_permute ( UInt theInstr )
7917{
cerion76de5cf2005-11-18 18:25:12 +00007918 /* VA-Form, VX-Form */
7919 UChar opc1 = ifieldOPC(theInstr);
7920 UChar vD_addr = ifieldRegDS(theInstr);
7921 UChar vA_addr = ifieldRegA(theInstr);
7922 UChar UIMM_5 = vA_addr;
7923 UChar vB_addr = ifieldRegB(theInstr);
7924 UChar vC_addr = ifieldRegC(theInstr);
7925 UChar b10 = ifieldBIT10(theInstr);
7926 UChar SHB_uimm4 = toUChar( IFIELD( theInstr, 6, 4 ) );
7927 UInt opc2 = toUChar( IFIELD( theInstr, 0, 6 ) );
cerion32aad402005-09-10 12:02:24 +00007928
cerion76de5cf2005-11-18 18:25:12 +00007929 UChar SIMM_8 = extend_s_5to8(UIMM_5);
cerion32aad402005-09-10 12:02:24 +00007930
cerion6e7a0ea2005-09-13 13:34:09 +00007931 IRTemp vA = newTemp(Ity_V128);
7932 IRTemp vB = newTemp(Ity_V128);
7933 IRTemp vC = newTemp(Ity_V128);
7934 assign( vA, getVReg(vA_addr));
7935 assign( vB, getVReg(vB_addr));
7936 assign( vC, getVReg(vC_addr));
7937
cerion32aad402005-09-10 12:02:24 +00007938 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00007939 vex_printf("dis_av_permute(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00007940 return False;
7941 }
7942
7943 switch (opc2) {
7944 case 0x2A: // vsel (Conditional Select, AV p238)
7945 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 +00007946 /* vD = (vA & ~vC) | (vB & vC) */
7947 putVReg( vD_addr, binop(Iop_OrV128,
7948 binop(Iop_AndV128, mkexpr(vA), unop(Iop_NotV128, mkexpr(vC))),
7949 binop(Iop_AndV128, mkexpr(vB), mkexpr(vC))) );
7950 return True;
cerion32aad402005-09-10 12:02:24 +00007951
cerion92d9d872005-09-15 21:58:50 +00007952 case 0x2B: { // vperm (Permute, AV p218)
cerion92d9d872005-09-15 21:58:50 +00007953 /* limited to two args for IR, so have to play games... */
sewardjdc1f9132005-10-22 12:49:49 +00007954 IRTemp a_perm = newTemp(Ity_V128);
7955 IRTemp b_perm = newTemp(Ity_V128);
7956 IRTemp mask = newTemp(Ity_V128);
7957 IRTemp vC_andF = newTemp(Ity_V128);
cerion5b2325f2005-12-23 00:55:09 +00007958 DIP("vperm v%d,v%d,v%d,v%d\n",
7959 vD_addr, vA_addr, vB_addr, vC_addr);
sewardjdc1f9132005-10-22 12:49:49 +00007960 /* Limit the Perm8x16 steering values to 0 .. 15 as that is what
7961 IR specifies, and also to hide irrelevant bits from
7962 memcheck */
cerion5b2325f2005-12-23 00:55:09 +00007963 assign( vC_andF,
7964 binop(Iop_AndV128, mkexpr(vC),
7965 unop(Iop_Dup8x16, mkU8(0xF))) );
7966 assign( a_perm,
7967 binop(Iop_Perm8x16, mkexpr(vA), mkexpr(vC_andF)) );
7968 assign( b_perm,
7969 binop(Iop_Perm8x16, mkexpr(vB), mkexpr(vC_andF)) );
cerion92d9d872005-09-15 21:58:50 +00007970 // mask[i8] = (vC[i8]_4 == 1) ? 0xFF : 0x0
7971 assign( mask, binop(Iop_SarN8x16,
7972 binop(Iop_ShlN8x16, mkexpr(vC), mkU8(3)),
7973 mkU8(7)) );
7974 // dst = (a & ~mask) | (b & mask)
7975 putVReg( vD_addr, binop(Iop_OrV128,
7976 binop(Iop_AndV128, mkexpr(a_perm),
7977 unop(Iop_NotV128, mkexpr(mask))),
7978 binop(Iop_AndV128, mkexpr(b_perm),
7979 mkexpr(mask))) );
7980 return True;
7981 }
cerion32aad402005-09-10 12:02:24 +00007982 case 0x2C: // vsldoi (Shift Left Double by Octet Imm, AV p241)
7983 if (b10 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00007984 vex_printf("dis_av_permute(ppc)(vsldoi)\n");
cerion32aad402005-09-10 12:02:24 +00007985 return False;
7986 }
cerion5b2325f2005-12-23 00:55:09 +00007987 DIP("vsldoi v%d,v%d,v%d,%d\n",
7988 vD_addr, vA_addr, vB_addr, SHB_uimm4);
cerion92d9d872005-09-15 21:58:50 +00007989 if (SHB_uimm4 == 0)
7990 putVReg( vD_addr, mkexpr(vA) );
7991 else
7992 putVReg( vD_addr,
cerion5b2325f2005-12-23 00:55:09 +00007993 binop(Iop_OrV128,
7994 binop(Iop_ShlV128, mkexpr(vA), mkU8(SHB_uimm4*8)),
7995 binop(Iop_ShrV128, mkexpr(vB), mkU8((16-SHB_uimm4)*8))) );
cerion92d9d872005-09-15 21:58:50 +00007996 return True;
cerion32aad402005-09-10 12:02:24 +00007997
7998 default:
7999 break; // Fall through...
8000 }
8001
cerion76de5cf2005-11-18 18:25:12 +00008002 opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00008003 switch (opc2) {
8004
8005 /* Merge */
8006 case 0x00C: // vmrghb (Merge High B, AV p195)
8007 DIP("vmrghb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion92d9d872005-09-15 21:58:50 +00008008 putVReg( vD_addr,
8009 binop(Iop_InterleaveHI8x16, mkexpr(vA), mkexpr(vB)) );
8010 break;
cerion32aad402005-09-10 12:02:24 +00008011
8012 case 0x04C: // vmrghh (Merge High HW, AV p196)
8013 DIP("vmrghh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion92d9d872005-09-15 21:58:50 +00008014 putVReg( vD_addr,
8015 binop(Iop_InterleaveHI16x8, mkexpr(vA), mkexpr(vB)) );
8016 break;
cerion32aad402005-09-10 12:02:24 +00008017
8018 case 0x08C: // vmrghw (Merge High W, AV p197)
8019 DIP("vmrghw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion92d9d872005-09-15 21:58:50 +00008020 putVReg( vD_addr,
8021 binop(Iop_InterleaveHI32x4, mkexpr(vA), mkexpr(vB)) );
8022 break;
cerion32aad402005-09-10 12:02:24 +00008023
8024 case 0x10C: // vmrglb (Merge Low B, AV p198)
8025 DIP("vmrglb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion92d9d872005-09-15 21:58:50 +00008026 putVReg( vD_addr,
8027 binop(Iop_InterleaveLO8x16, mkexpr(vA), mkexpr(vB)) );
8028 break;
cerion32aad402005-09-10 12:02:24 +00008029
8030 case 0x14C: // vmrglh (Merge Low HW, AV p199)
8031 DIP("vmrglh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion92d9d872005-09-15 21:58:50 +00008032 putVReg( vD_addr,
8033 binop(Iop_InterleaveLO16x8, mkexpr(vA), mkexpr(vB)) );
8034 break;
cerion32aad402005-09-10 12:02:24 +00008035
8036 case 0x18C: // vmrglw (Merge Low W, AV p200)
8037 DIP("vmrglw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion92d9d872005-09-15 21:58:50 +00008038 putVReg( vD_addr,
8039 binop(Iop_InterleaveLO32x4, mkexpr(vA), mkexpr(vB)) );
8040 break;
8041
cerion32aad402005-09-10 12:02:24 +00008042
8043 /* Splat */
cerion92d9d872005-09-15 21:58:50 +00008044 case 0x20C: { // vspltb (Splat Byte, AV p245)
cerion92d9d872005-09-15 21:58:50 +00008045 /* vD = Dup8x16( vB[UIMM_5] ) */
sewardjd1470942005-10-22 02:01:16 +00008046 UChar sh_uimm = (15 - (UIMM_5 & 15)) * 8;
sewardj197bd172005-10-12 11:34:33 +00008047 DIP("vspltb v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
cerion92d9d872005-09-15 21:58:50 +00008048 putVReg( vD_addr, unop(Iop_Dup8x16,
8049 unop(Iop_32to8, unop(Iop_V128to32,
8050 binop(Iop_ShrV128, mkexpr(vB), mkU8(sh_uimm))))) );
8051 break;
8052 }
8053 case 0x24C: { // vsplth (Splat Half Word, AV p246)
sewardjd1470942005-10-22 02:01:16 +00008054 UChar sh_uimm = (7 - (UIMM_5 & 7)) * 16;
sewardj197bd172005-10-12 11:34:33 +00008055 DIP("vsplth v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
cerion92d9d872005-09-15 21:58:50 +00008056 putVReg( vD_addr, unop(Iop_Dup16x8,
8057 unop(Iop_32to16, unop(Iop_V128to32,
8058 binop(Iop_ShrV128, mkexpr(vB), mkU8(sh_uimm))))) );
8059 break;
8060 }
cerion27b3d7e2005-09-14 20:35:47 +00008061 case 0x28C: { // vspltw (Splat Word, AV p250)
cerion27b3d7e2005-09-14 20:35:47 +00008062 /* vD = Dup32x4( vB[UIMM_5] ) */
sewardjd1470942005-10-22 02:01:16 +00008063 UChar sh_uimm = (3 - (UIMM_5 & 3)) * 32;
sewardj197bd172005-10-12 11:34:33 +00008064 DIP("vspltw v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
cerion27b3d7e2005-09-14 20:35:47 +00008065 putVReg( vD_addr, unop(Iop_Dup32x4,
8066 unop(Iop_V128to32,
8067 binop(Iop_ShrV128, mkexpr(vB), mkU8(sh_uimm)))) );
8068 break;
8069 }
cerion32aad402005-09-10 12:02:24 +00008070 case 0x30C: // vspltisb (Splat Immediate Signed B, AV p247)
8071 DIP("vspltisb v%d,%d\n", vD_addr, (Char)SIMM_8);
cerion92d9d872005-09-15 21:58:50 +00008072 putVReg( vD_addr, unop(Iop_Dup8x16, mkU8(SIMM_8)) );
8073 break;
cerion32aad402005-09-10 12:02:24 +00008074
8075 case 0x34C: // vspltish (Splat Immediate Signed HW, AV p248)
8076 DIP("vspltish v%d,%d\n", vD_addr, (Char)SIMM_8);
cerion5b2325f2005-12-23 00:55:09 +00008077 putVReg( vD_addr,
8078 unop(Iop_Dup16x8, mkU16(extend_s_8to32(SIMM_8))) );
cerion92d9d872005-09-15 21:58:50 +00008079 break;
cerion32aad402005-09-10 12:02:24 +00008080
8081 case 0x38C: // vspltisw (Splat Immediate Signed W, AV p249)
8082 DIP("vspltisw v%d,%d\n", vD_addr, (Char)SIMM_8);
cerion5b2325f2005-12-23 00:55:09 +00008083 putVReg( vD_addr,
8084 unop(Iop_Dup32x4, mkU32(extend_s_8to32(SIMM_8))) );
cerion92d9d872005-09-15 21:58:50 +00008085 break;
cerion32aad402005-09-10 12:02:24 +00008086
8087 default:
cerion5b2325f2005-12-23 00:55:09 +00008088 vex_printf("dis_av_permute(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00008089 return False;
8090 }
8091 return True;
8092}
8093
8094/*
8095 AltiVec Pack/Unpack Instructions
8096*/
8097static Bool dis_av_pack ( UInt theInstr )
8098{
cerion76de5cf2005-11-18 18:25:12 +00008099 /* VX-Form */
8100 UChar opc1 = ifieldOPC(theInstr);
8101 UChar vD_addr = ifieldRegDS(theInstr);
8102 UChar vA_addr = ifieldRegA(theInstr);
8103 UChar vB_addr = ifieldRegB(theInstr);
8104 UInt opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00008105
sewardj197bd172005-10-12 11:34:33 +00008106 IRTemp signs = IRTemp_INVALID;
8107 IRTemp zeros = IRTemp_INVALID;
cerion76de5cf2005-11-18 18:25:12 +00008108 IRTemp vA = newTemp(Ity_V128);
8109 IRTemp vB = newTemp(Ity_V128);
cerion3c052792005-09-16 07:13:44 +00008110 assign( vA, getVReg(vA_addr));
8111 assign( vB, getVReg(vB_addr));
8112
cerion32aad402005-09-10 12:02:24 +00008113 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00008114 vex_printf("dis_av_pack(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00008115 return False;
8116 }
8117
8118 switch (opc2) {
8119 /* Packing */
8120 case 0x00E: // vpkuhum (Pack Unsigned HW Unsigned Modulo, AV p224)
8121 DIP("vpkuhum v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
sewardj1bee5612005-11-10 18:10:58 +00008122 putVReg( vD_addr, binop(Iop_Narrow16x8, mkexpr(vA), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008123 return True;
cerion32aad402005-09-10 12:02:24 +00008124
8125 case 0x04E: // vpkuwum (Pack Unsigned W Unsigned Modulo, AV p226)
8126 DIP("vpkuwum v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
sewardj1bee5612005-11-10 18:10:58 +00008127 putVReg( vD_addr, binop(Iop_Narrow32x4, mkexpr(vA), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008128 return True;
cerion32aad402005-09-10 12:02:24 +00008129
8130 case 0x08E: // vpkuhus (Pack Unsigned HW Unsigned Saturate, AV p225)
8131 DIP("vpkuhus v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00008132 putVReg( vD_addr,
8133 binop(Iop_QNarrow16Ux8, mkexpr(vA), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008134 // TODO: set VSCR[SAT]
8135 return True;
cerion32aad402005-09-10 12:02:24 +00008136
8137 case 0x0CE: // vpkuwus (Pack Unsigned W Unsigned Saturate, AV p227)
8138 DIP("vpkuwus v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00008139 putVReg( vD_addr,
8140 binop(Iop_QNarrow32Ux4, mkexpr(vA), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008141 // TODO: set VSCR[SAT]
8142 return True;
cerion32aad402005-09-10 12:02:24 +00008143
cerion3c052792005-09-16 07:13:44 +00008144 case 0x10E: { // vpkshus (Pack Signed HW Unsigned Saturate, AV p221)
cerion3c052792005-09-16 07:13:44 +00008145 // This insn does a signed->unsigned saturating conversion.
8146 // Conversion done here, then uses unsigned->unsigned vpk insn:
8147 // => UnsignedSaturatingNarrow( x & ~ (x >>s 15) )
8148 IRTemp vA_tmp = newTemp(Ity_V128);
8149 IRTemp vB_tmp = newTemp(Ity_V128);
sewardj197bd172005-10-12 11:34:33 +00008150 DIP("vpkshus v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00008151 assign( vA_tmp, binop(Iop_AndV128, mkexpr(vA),
8152 unop(Iop_NotV128,
8153 binop(Iop_SarN16x8,
8154 mkexpr(vA), mkU8(15)))) );
8155 assign( vB_tmp, binop(Iop_AndV128, mkexpr(vB),
8156 unop(Iop_NotV128,
8157 binop(Iop_SarN16x8,
8158 mkexpr(vB), mkU8(15)))) );
8159 putVReg( vD_addr, binop(Iop_QNarrow16Ux8,
8160 mkexpr(vA_tmp), mkexpr(vB_tmp)) );
8161 // TODO: set VSCR[SAT]
8162 return True;
8163 }
8164 case 0x14E: { // vpkswus (Pack Signed W Unsigned Saturate, AV p223)
cerion3c052792005-09-16 07:13:44 +00008165 // This insn does a signed->unsigned saturating conversion.
8166 // Conversion done here, then uses unsigned->unsigned vpk insn:
8167 // => UnsignedSaturatingNarrow( x & ~ (x >>s 31) )
8168 IRTemp vA_tmp = newTemp(Ity_V128);
8169 IRTemp vB_tmp = newTemp(Ity_V128);
sewardj197bd172005-10-12 11:34:33 +00008170 DIP("vpkswus v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00008171 assign( vA_tmp, binop(Iop_AndV128, mkexpr(vA),
8172 unop(Iop_NotV128,
8173 binop(Iop_SarN32x4,
8174 mkexpr(vA), mkU8(31)))) );
8175 assign( vB_tmp, binop(Iop_AndV128, mkexpr(vB),
8176 unop(Iop_NotV128,
8177 binop(Iop_SarN32x4,
8178 mkexpr(vB), mkU8(31)))) );
8179 putVReg( vD_addr, binop(Iop_QNarrow32Ux4,
8180 mkexpr(vA_tmp), mkexpr(vB_tmp)) );
8181 // TODO: set VSCR[SAT]
8182 return True;
8183 }
cerion32aad402005-09-10 12:02:24 +00008184 case 0x18E: // vpkshss (Pack Signed HW Signed Saturate, AV p220)
8185 DIP("vpkshss v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00008186 putVReg( vD_addr,
8187 binop(Iop_QNarrow16Sx8, mkexpr(vA), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008188 // TODO: set VSCR[SAT]
8189 return True;
cerion32aad402005-09-10 12:02:24 +00008190
8191 case 0x1CE: // vpkswss (Pack Signed W Signed Saturate, AV p222)
8192 DIP("vpkswss v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00008193 putVReg( vD_addr,
8194 binop(Iop_QNarrow32Sx4, mkexpr(vA), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008195 // TODO: set VSCR[SAT]
8196 return True;
cerion32aad402005-09-10 12:02:24 +00008197
cerion3c052792005-09-16 07:13:44 +00008198 case 0x30E: { // vpkpx (Pack Pixel, AV p219)
cerion3c052792005-09-16 07:13:44 +00008199 /* CAB: Worth a new primop? */
cerion5b2325f2005-12-23 00:55:09 +00008200 /* Using shifts to compact pixel elements, then packing them */
cerion3c052792005-09-16 07:13:44 +00008201 IRTemp a1 = newTemp(Ity_V128);
8202 IRTemp a2 = newTemp(Ity_V128);
8203 IRTemp a3 = newTemp(Ity_V128);
8204 IRTemp a_tmp = newTemp(Ity_V128);
8205 IRTemp b1 = newTemp(Ity_V128);
8206 IRTemp b2 = newTemp(Ity_V128);
8207 IRTemp b3 = newTemp(Ity_V128);
8208 IRTemp b_tmp = newTemp(Ity_V128);
sewardj197bd172005-10-12 11:34:33 +00008209 DIP("vpkpx v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00008210 assign( a1, binop(Iop_ShlN16x8,
8211 binop(Iop_ShrN32x4, mkexpr(vA), mkU8(19)),
8212 mkU8(10)) );
8213 assign( a2, binop(Iop_ShlN16x8,
8214 binop(Iop_ShrN16x8, mkexpr(vA), mkU8(11)),
8215 mkU8(5)) );
8216 assign( a3, binop(Iop_ShrN16x8,
8217 binop(Iop_ShlN16x8, mkexpr(vA), mkU8(8)),
8218 mkU8(11)) );
8219 assign( a_tmp, binop(Iop_OrV128, mkexpr(a1),
8220 binop(Iop_OrV128, mkexpr(a2), mkexpr(a3))) );
8221
8222 assign( b1, binop(Iop_ShlN16x8,
8223 binop(Iop_ShrN32x4, mkexpr(vB), mkU8(19)),
8224 mkU8(10)) );
8225 assign( b2, binop(Iop_ShlN16x8,
8226 binop(Iop_ShrN16x8, mkexpr(vB), mkU8(11)),
8227 mkU8(5)) );
8228 assign( b3, binop(Iop_ShrN16x8,
8229 binop(Iop_ShlN16x8, mkexpr(vB), mkU8(8)),
8230 mkU8(11)) );
8231 assign( b_tmp, binop(Iop_OrV128, mkexpr(b1),
8232 binop(Iop_OrV128, mkexpr(b2), mkexpr(b3))) );
8233
sewardj1bee5612005-11-10 18:10:58 +00008234 putVReg( vD_addr, binop(Iop_Narrow32x4,
cerion3c052792005-09-16 07:13:44 +00008235 mkexpr(a_tmp), mkexpr(b_tmp)) );
8236 return True;
8237 }
cerion32aad402005-09-10 12:02:24 +00008238
8239 default:
8240 break; // Fall through...
8241 }
8242
8243
8244 if (vA_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00008245 vex_printf("dis_av_pack(ppc)(vA_addr)\n");
cerion32aad402005-09-10 12:02:24 +00008246 return False;
8247 }
8248
sewardj197bd172005-10-12 11:34:33 +00008249 signs = newTemp(Ity_V128);
8250 zeros = newTemp(Ity_V128);
cerion3c052792005-09-16 07:13:44 +00008251 assign( zeros, unop(Iop_Dup32x4, mkU32(0)) );
8252
cerion32aad402005-09-10 12:02:24 +00008253 switch (opc2) {
8254 /* Unpacking */
cerion3c052792005-09-16 07:13:44 +00008255 case 0x20E: { // vupkhsb (Unpack High Signed B, AV p277)
cerion32aad402005-09-10 12:02:24 +00008256 DIP("vupkhsb v%d,v%d\n", vD_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00008257 assign( signs, binop(Iop_CmpGT8Sx16, mkexpr(zeros), mkexpr(vB)) );
cerion5b2325f2005-12-23 00:55:09 +00008258 putVReg( vD_addr,
8259 binop(Iop_InterleaveHI8x16, mkexpr(signs), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008260 break;
8261 }
8262 case 0x24E: { // vupkhsh (Unpack High Signed HW, AV p278)
cerion32aad402005-09-10 12:02:24 +00008263 DIP("vupkhsh v%d,v%d\n", vD_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00008264 assign( signs, binop(Iop_CmpGT16Sx8, mkexpr(zeros), mkexpr(vB)) );
cerion5b2325f2005-12-23 00:55:09 +00008265 putVReg( vD_addr,
8266 binop(Iop_InterleaveHI16x8, mkexpr(signs), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008267 break;
8268 }
8269 case 0x28E: { // vupklsb (Unpack Low Signed B, AV p280)
cerion32aad402005-09-10 12:02:24 +00008270 DIP("vupklsb v%d,v%d\n", vD_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00008271 assign( signs, binop(Iop_CmpGT8Sx16, mkexpr(zeros), mkexpr(vB)) );
cerion5b2325f2005-12-23 00:55:09 +00008272 putVReg( vD_addr,
8273 binop(Iop_InterleaveLO8x16, mkexpr(signs), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008274 break;
8275 }
8276 case 0x2CE: { // vupklsh (Unpack Low Signed HW, AV p281)
cerion32aad402005-09-10 12:02:24 +00008277 DIP("vupklsh v%d,v%d\n", vD_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00008278 assign( signs, binop(Iop_CmpGT16Sx8, mkexpr(zeros), mkexpr(vB)) );
cerion5b2325f2005-12-23 00:55:09 +00008279 putVReg( vD_addr,
8280 binop(Iop_InterleaveLO16x8, mkexpr(signs), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008281 break;
8282 }
8283 case 0x34E: { // vupkhpx (Unpack High Pixel16, AV p276)
cerion3c052792005-09-16 07:13:44 +00008284 /* CAB: Worth a new primop? */
8285 /* Using shifts to isolate pixel elements, then expanding them */
8286 IRTemp z0 = newTemp(Ity_V128);
8287 IRTemp z1 = newTemp(Ity_V128);
8288 IRTemp z01 = newTemp(Ity_V128);
8289 IRTemp z2 = newTemp(Ity_V128);
8290 IRTemp z3 = newTemp(Ity_V128);
8291 IRTemp z23 = newTemp(Ity_V128);
sewardj197bd172005-10-12 11:34:33 +00008292 DIP("vupkhpx v%d,v%d\n", vD_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00008293 assign( z0, binop(Iop_ShlN16x8,
8294 binop(Iop_SarN16x8, mkexpr(vB), mkU8(15)),
8295 mkU8(8)) );
8296 assign( z1, binop(Iop_ShrN16x8,
8297 binop(Iop_ShlN16x8, mkexpr(vB), mkU8(1)),
8298 mkU8(11)) );
8299 assign( z01, binop(Iop_InterleaveHI16x8, mkexpr(zeros),
8300 binop(Iop_OrV128, mkexpr(z0), mkexpr(z1))) );
8301 assign( z2, binop(Iop_ShrN16x8,
8302 binop(Iop_ShlN16x8,
8303 binop(Iop_ShrN16x8, mkexpr(vB), mkU8(5)),
8304 mkU8(11)),
8305 mkU8(3)) );
8306 assign( z3, binop(Iop_ShrN16x8,
8307 binop(Iop_ShlN16x8, mkexpr(vB), mkU8(11)),
8308 mkU8(11)) );
8309 assign( z23, binop(Iop_InterleaveHI16x8, mkexpr(zeros),
8310 binop(Iop_OrV128, mkexpr(z2), mkexpr(z3))) );
cerion5b2325f2005-12-23 00:55:09 +00008311 putVReg( vD_addr,
8312 binop(Iop_OrV128,
8313 binop(Iop_ShlN32x4, mkexpr(z01), mkU8(16)),
8314 mkexpr(z23)) );
cerion3c052792005-09-16 07:13:44 +00008315 break;
8316 }
8317 case 0x3CE: { // vupklpx (Unpack Low Pixel16, AV p279)
cerion3c052792005-09-16 07:13:44 +00008318 /* identical to vupkhpx, except interleaving LO */
8319 IRTemp z0 = newTemp(Ity_V128);
8320 IRTemp z1 = newTemp(Ity_V128);
8321 IRTemp z01 = newTemp(Ity_V128);
8322 IRTemp z2 = newTemp(Ity_V128);
8323 IRTemp z3 = newTemp(Ity_V128);
8324 IRTemp z23 = newTemp(Ity_V128);
sewardj197bd172005-10-12 11:34:33 +00008325 DIP("vupklpx v%d,v%d\n", vD_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00008326 assign( z0, binop(Iop_ShlN16x8,
8327 binop(Iop_SarN16x8, mkexpr(vB), mkU8(15)),
8328 mkU8(8)) );
8329 assign( z1, binop(Iop_ShrN16x8,
8330 binop(Iop_ShlN16x8, mkexpr(vB), mkU8(1)),
8331 mkU8(11)) );
8332 assign( z01, binop(Iop_InterleaveLO16x8, mkexpr(zeros),
8333 binop(Iop_OrV128, mkexpr(z0), mkexpr(z1))) );
8334 assign( z2, binop(Iop_ShrN16x8,
8335 binop(Iop_ShlN16x8,
8336 binop(Iop_ShrN16x8, mkexpr(vB), mkU8(5)),
8337 mkU8(11)),
8338 mkU8(3)) );
8339 assign( z3, binop(Iop_ShrN16x8,
8340 binop(Iop_ShlN16x8, mkexpr(vB), mkU8(11)),
8341 mkU8(11)) );
8342 assign( z23, binop(Iop_InterleaveLO16x8, mkexpr(zeros),
8343 binop(Iop_OrV128, mkexpr(z2), mkexpr(z3))) );
cerion5b2325f2005-12-23 00:55:09 +00008344 putVReg( vD_addr,
8345 binop(Iop_OrV128,
8346 binop(Iop_ShlN32x4, mkexpr(z01), mkU8(16)),
8347 mkexpr(z23)) );
cerion3c052792005-09-16 07:13:44 +00008348 break;
8349 }
cerion32aad402005-09-10 12:02:24 +00008350 default:
cerion5b2325f2005-12-23 00:55:09 +00008351 vex_printf("dis_av_pack(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00008352 return False;
8353 }
8354 return True;
8355}
8356
8357
8358/*
8359 AltiVec Floating Point Arithmetic Instructions
8360*/
8361static Bool dis_av_fp_arith ( UInt theInstr )
8362{
cerion76de5cf2005-11-18 18:25:12 +00008363 /* VA-Form */
8364 UChar opc1 = ifieldOPC(theInstr);
8365 UChar vD_addr = ifieldRegDS(theInstr);
8366 UChar vA_addr = ifieldRegA(theInstr);
8367 UChar vB_addr = ifieldRegB(theInstr);
8368 UChar vC_addr = ifieldRegC(theInstr);
cerion32aad402005-09-10 12:02:24 +00008369 UInt opc2=0;
8370
cerion8ea0d3e2005-11-14 00:44:47 +00008371 IRTemp vA = newTemp(Ity_V128);
8372 IRTemp vB = newTemp(Ity_V128);
8373 IRTemp vC = newTemp(Ity_V128);
8374 assign( vA, getVReg(vA_addr));
8375 assign( vB, getVReg(vB_addr));
8376 assign( vC, getVReg(vC_addr));
8377
cerion32aad402005-09-10 12:02:24 +00008378 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00008379 vex_printf("dis_av_fp_arith(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00008380 return False;
8381 }
8382
cerion76de5cf2005-11-18 18:25:12 +00008383 opc2 = IFIELD( theInstr, 0, 6 );
cerion32aad402005-09-10 12:02:24 +00008384 switch (opc2) {
8385 case 0x2E: // vmaddfp (Multiply Add FP, AV p177)
cerion5b2325f2005-12-23 00:55:09 +00008386 DIP("vmaddfp v%d,v%d,v%d,v%d\n",
8387 vD_addr, vA_addr, vC_addr, vB_addr);
8388 putVReg( vD_addr,
8389 binop(Iop_Add32Fx4, mkexpr(vB),
8390 binop(Iop_Mul32Fx4, mkexpr(vA), mkexpr(vC))) );
cerionf3f173c2005-11-14 02:37:44 +00008391 return True;
cerion32aad402005-09-10 12:02:24 +00008392
cerionf3f173c2005-11-14 02:37:44 +00008393 case 0x2F: { // vnmsubfp (Negative Multiply-Subtract FP, AV p215)
cerion5b2325f2005-12-23 00:55:09 +00008394 DIP("vnmsubfp v%d,v%d,v%d,v%d\n",
8395 vD_addr, vA_addr, vC_addr, vB_addr);
8396 putVReg( vD_addr,
8397 binop(Iop_Sub32Fx4,
8398 mkexpr(vB),
8399 binop(Iop_Mul32Fx4, mkexpr(vA), mkexpr(vC))) );
cerionf3f173c2005-11-14 02:37:44 +00008400 return True;
8401 }
cerion32aad402005-09-10 12:02:24 +00008402
8403 default:
8404 break; // Fall through...
8405 }
8406
cerion76de5cf2005-11-18 18:25:12 +00008407 opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00008408 switch (opc2) {
8409 case 0x00A: // vaddfp (Add FP, AV p137)
8410 DIP("vaddfp v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008411 putVReg( vD_addr, binop(Iop_Add32Fx4, mkexpr(vA), mkexpr(vB)) );
8412 return True;
cerion32aad402005-09-10 12:02:24 +00008413
8414 case 0x04A: // vsubfp (Subtract FP, AV p261)
8415 DIP("vsubfp v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008416 putVReg( vD_addr, binop(Iop_Sub32Fx4, mkexpr(vA), mkexpr(vB)) );
8417 return True;
cerion32aad402005-09-10 12:02:24 +00008418
8419 case 0x40A: // vmaxfp (Maximum FP, AV p178)
8420 DIP("vmaxfp v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008421 putVReg( vD_addr, binop(Iop_Max32Fx4, mkexpr(vA), mkexpr(vB)) );
8422 return True;
cerion32aad402005-09-10 12:02:24 +00008423
8424 case 0x44A: // vminfp (Minimum FP, AV p187)
8425 DIP("vminfp v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008426 putVReg( vD_addr, binop(Iop_Min32Fx4, mkexpr(vA), mkexpr(vB)) );
8427 return True;
cerion32aad402005-09-10 12:02:24 +00008428
8429 default:
8430 break; // Fall through...
8431 }
8432
8433
8434 if (vA_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00008435 vex_printf("dis_av_fp_arith(ppc)(vA_addr)\n");
cerion32aad402005-09-10 12:02:24 +00008436 return False;
8437 }
8438
8439 switch (opc2) {
8440 case 0x10A: // vrefp (Reciprocal Esimate FP, AV p228)
8441 DIP("vrefp v%d,v%d\n", vD_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008442 putVReg( vD_addr, unop(Iop_Recip32Fx4, mkexpr(vB)) );
8443 return True;
cerion32aad402005-09-10 12:02:24 +00008444
cerion5b2325f2005-12-23 00:55:09 +00008445 case 0x14A: // vrsqrtefp (Reciprocal Sqrt Estimate FP, AV p237)
cerion32aad402005-09-10 12:02:24 +00008446 DIP("vrsqrtefp v%d,v%d\n", vD_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008447 putVReg( vD_addr, unop(Iop_RSqrt32Fx4, mkexpr(vB)) );
8448 return True;
cerion32aad402005-09-10 12:02:24 +00008449
8450 case 0x18A: // vexptefp (2 Raised to the Exp Est FP, AV p173)
8451 DIP("vexptefp v%d,v%d\n", vD_addr, vB_addr);
8452 DIP(" => not implemented\n");
8453 return False;
8454
8455 case 0x1CA: // vlogefp (Log2 Estimate FP, AV p175)
8456 DIP("vlogefp v%d,v%d\n", vD_addr, vB_addr);
8457 DIP(" => not implemented\n");
8458 return False;
8459
8460 default:
cerion5b2325f2005-12-23 00:55:09 +00008461 vex_printf("dis_av_fp_arith(ppc)(opc2=0x%x)\n",opc2);
cerion32aad402005-09-10 12:02:24 +00008462 return False;
8463 }
8464 return True;
8465}
8466
8467/*
8468 AltiVec Floating Point Compare Instructions
8469*/
8470static Bool dis_av_fp_cmp ( UInt theInstr )
8471{
cerion76de5cf2005-11-18 18:25:12 +00008472 /* VXR-Form */
8473 UChar opc1 = ifieldOPC(theInstr);
8474 UChar vD_addr = ifieldRegDS(theInstr);
8475 UChar vA_addr = ifieldRegA(theInstr);
8476 UChar vB_addr = ifieldRegB(theInstr);
8477 UChar flag_rC = ifieldBIT10(theInstr);
8478 UInt opc2 = IFIELD( theInstr, 0, 10 );
cerion32aad402005-09-10 12:02:24 +00008479
cerion8ea0d3e2005-11-14 00:44:47 +00008480 Bool cmp_bounds = False;
8481
8482 IRTemp vA = newTemp(Ity_V128);
8483 IRTemp vB = newTemp(Ity_V128);
8484 IRTemp vD = newTemp(Ity_V128);
8485 assign( vA, getVReg(vA_addr));
8486 assign( vB, getVReg(vB_addr));
8487
cerion32aad402005-09-10 12:02:24 +00008488 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00008489 vex_printf("dis_av_fp_cmp(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00008490 return False;
8491 }
8492
8493 switch (opc2) {
8494 case 0x0C6: // vcmpeqfp (Compare Equal-to FP, AV p159)
cerion5b2325f2005-12-23 00:55:09 +00008495 DIP("vcmpeqfp%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
8496 vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008497 assign( vD, binop(Iop_CmpEQ32Fx4, mkexpr(vA), mkexpr(vB)) );
8498 break;
cerion32aad402005-09-10 12:02:24 +00008499
cerion5b2325f2005-12-23 00:55:09 +00008500 case 0x1C6: // vcmpgefp (Compare Greater-than-or-Equal-to, AV p163)
8501 DIP("vcmpgefp%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
8502 vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008503 assign( vD, binop(Iop_CmpGE32Fx4, mkexpr(vA), mkexpr(vB)) );
8504 break;
cerion32aad402005-09-10 12:02:24 +00008505
8506 case 0x2C6: // vcmpgtfp (Compare Greater-than FP, AV p164)
cerion5b2325f2005-12-23 00:55:09 +00008507 DIP("vcmpgtfp%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
8508 vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008509 assign( vD, binop(Iop_CmpGT32Fx4, mkexpr(vA), mkexpr(vB)) );
8510 break;
cerion32aad402005-09-10 12:02:24 +00008511
cerion8ea0d3e2005-11-14 00:44:47 +00008512 case 0x3C6: { // vcmpbfp (Compare Bounds FP, AV p157)
8513 IRTemp gt = newTemp(Ity_V128);
8514 IRTemp lt = newTemp(Ity_V128);
8515 IRTemp zeros = newTemp(Ity_V128);
cerion5b2325f2005-12-23 00:55:09 +00008516 DIP("vcmpbfp%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
8517 vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008518 cmp_bounds = True;
8519 assign( zeros, unop(Iop_Dup32x4, mkU32(0)) );
8520
8521 /* Note: making use of fact that the ppc backend for compare insns
cerion5b2325f2005-12-23 00:55:09 +00008522 return zero'd lanes if either of the corresponding arg lanes is
8523 a nan.
cerion8ea0d3e2005-11-14 00:44:47 +00008524
8525 Perhaps better to have an irop Iop_isNan32Fx4, but then we'd
8526 need this for the other compares too (vcmpeqfp etc)...
8527 Better still, tighten down the spec for compare irops.
8528 */
8529 assign( gt, unop(Iop_NotV128,
8530 binop(Iop_CmpLE32Fx4, mkexpr(vA), mkexpr(vB))) );
8531 assign( lt, unop(Iop_NotV128,
8532 binop(Iop_CmpGE32Fx4, mkexpr(vA),
cerion5b2325f2005-12-23 00:55:09 +00008533 binop(Iop_Sub32Fx4, mkexpr(zeros),
8534 mkexpr(vB)))) );
cerion8ea0d3e2005-11-14 00:44:47 +00008535
8536 // finally, just shift gt,lt to correct position
8537 assign( vD, binop(Iop_ShlN32x4,
8538 binop(Iop_OrV128,
8539 binop(Iop_AndV128, mkexpr(gt),
8540 unop(Iop_Dup32x4, mkU32(0x2))),
8541 binop(Iop_AndV128, mkexpr(lt),
8542 unop(Iop_Dup32x4, mkU32(0x1)))),
8543 mkU8(30)) );
8544 break;
8545 }
cerion32aad402005-09-10 12:02:24 +00008546
8547 default:
cerion5b2325f2005-12-23 00:55:09 +00008548 vex_printf("dis_av_fp_cmp(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00008549 return False;
8550 }
cerion8ea0d3e2005-11-14 00:44:47 +00008551
8552 putVReg( vD_addr, mkexpr(vD) );
8553
cerion76de5cf2005-11-18 18:25:12 +00008554 if (flag_rC) {
cerion8ea0d3e2005-11-14 00:44:47 +00008555 set_AV_CR6( mkexpr(vD), !cmp_bounds );
8556 }
cerion32aad402005-09-10 12:02:24 +00008557 return True;
8558}
8559
8560/*
8561 AltiVec Floating Point Convert/Round Instructions
8562*/
8563static Bool dis_av_fp_convert ( UInt theInstr )
8564{
cerion76de5cf2005-11-18 18:25:12 +00008565 /* VX-Form */
8566 UChar opc1 = ifieldOPC(theInstr);
8567 UChar vD_addr = ifieldRegDS(theInstr);
8568 UChar UIMM_5 = ifieldRegA(theInstr);
8569 UChar vB_addr = ifieldRegB(theInstr);
8570 UInt opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00008571
cerion76de5cf2005-11-18 18:25:12 +00008572 IRTemp vB = newTemp(Ity_V128);
8573 IRTemp vScale = newTemp(Ity_V128);
ceriond963eb42005-11-16 18:02:58 +00008574 IRTemp vInvScale = newTemp(Ity_V128);
sewardj41a7b702005-11-18 22:18:23 +00008575
8576 float scale, inv_scale;
8577
ceriond963eb42005-11-16 18:02:58 +00008578 assign( vB, getVReg(vB_addr));
8579
8580 /* scale = 2^UIMM, cast to float, reinterpreted as uint */
sewardj41a7b702005-11-18 22:18:23 +00008581 scale = (float)( (unsigned int) 1<<UIMM_5 );
sewardj2ead5222005-11-23 03:53:45 +00008582 assign( vScale, unop(Iop_Dup32x4, mkU32( float_to_bits(scale) )) );
sewardj41a7b702005-11-18 22:18:23 +00008583 inv_scale = 1/scale;
cerion5b2325f2005-12-23 00:55:09 +00008584 assign( vInvScale,
8585 unop(Iop_Dup32x4, mkU32( float_to_bits(inv_scale) )) );
ceriond963eb42005-11-16 18:02:58 +00008586
cerion32aad402005-09-10 12:02:24 +00008587 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00008588 vex_printf("dis_av_fp_convert(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00008589 return False;
8590 }
8591
8592 switch (opc2) {
8593 case 0x30A: // vcfux (Convert from Unsigned Fixed-Point W, AV p156)
8594 DIP("vcfux v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
ceriond963eb42005-11-16 18:02:58 +00008595 putVReg( vD_addr, binop(Iop_Mul32Fx4,
8596 unop(Iop_I32UtoFx4, mkexpr(vB)),
8597 mkexpr(vInvScale)) );
8598 return True;
cerion32aad402005-09-10 12:02:24 +00008599
8600 case 0x34A: // vcfsx (Convert from Signed Fixed-Point W, AV p155)
8601 DIP("vcfsx v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
ceriond963eb42005-11-16 18:02:58 +00008602
8603 putVReg( vD_addr, binop(Iop_Mul32Fx4,
8604 unop(Iop_I32StoFx4, mkexpr(vB)),
8605 mkexpr(vInvScale)) );
8606 return True;
cerion32aad402005-09-10 12:02:24 +00008607
8608 case 0x38A: // vctuxs (Convert to Unsigned Fixed-Point W Saturate, AV p172)
8609 DIP("vctuxs v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
ceriond963eb42005-11-16 18:02:58 +00008610 putVReg( vD_addr,
8611 unop(Iop_QFtoI32Ux4_RZ,
8612 binop(Iop_Mul32Fx4, mkexpr(vB), mkexpr(vScale))) );
8613 return True;
cerion32aad402005-09-10 12:02:24 +00008614
8615 case 0x3CA: // vctsxs (Convert to Signed Fixed-Point W Saturate, AV p171)
8616 DIP("vctsxs v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
ceriond963eb42005-11-16 18:02:58 +00008617 putVReg( vD_addr,
8618 unop(Iop_QFtoI32Sx4_RZ,
8619 binop(Iop_Mul32Fx4, mkexpr(vB), mkexpr(vScale))) );
8620 return True;
cerion32aad402005-09-10 12:02:24 +00008621
8622 default:
8623 break; // Fall through...
8624 }
8625
8626 if (UIMM_5 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00008627 vex_printf("dis_av_fp_convert(ppc)(UIMM_5)\n");
cerion32aad402005-09-10 12:02:24 +00008628 return False;
8629 }
8630
8631 switch (opc2) {
8632 case 0x20A: // vrfin (Round to FP Integer Nearest, AV p231)
8633 DIP("vrfin v%d,v%d\n", vD_addr, vB_addr);
ceriond963eb42005-11-16 18:02:58 +00008634 putVReg( vD_addr, unop(Iop_RoundF32x4_RN, mkexpr(vB)) );
8635 break;
cerion32aad402005-09-10 12:02:24 +00008636
8637 case 0x24A: // vrfiz (Round to FP Integer toward zero, AV p233)
8638 DIP("vrfiz v%d,v%d\n", vD_addr, vB_addr);
ceriond963eb42005-11-16 18:02:58 +00008639 putVReg( vD_addr, unop(Iop_RoundF32x4_RZ, mkexpr(vB)) );
8640 break;
cerion32aad402005-09-10 12:02:24 +00008641
8642 case 0x28A: // vrfip (Round to FP Integer toward +inf, AV p232)
8643 DIP("vrfip v%d,v%d\n", vD_addr, vB_addr);
ceriond963eb42005-11-16 18:02:58 +00008644 putVReg( vD_addr, unop(Iop_RoundF32x4_RP, mkexpr(vB)) );
8645 break;
cerion32aad402005-09-10 12:02:24 +00008646
8647 case 0x2CA: // vrfim (Round to FP Integer toward -inf, AV p230)
8648 DIP("vrfim v%d,v%d\n", vD_addr, vB_addr);
ceriond963eb42005-11-16 18:02:58 +00008649 putVReg( vD_addr, unop(Iop_RoundF32x4_RM, mkexpr(vB)) );
8650 break;
cerion32aad402005-09-10 12:02:24 +00008651
8652 default:
cerion5b2325f2005-12-23 00:55:09 +00008653 vex_printf("dis_av_fp_convert(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00008654 return False;
8655 }
8656 return True;
8657}
cerion3d870a32005-03-18 12:23:33 +00008658
8659
cerion91ad5362005-01-27 23:02:41 +00008660
8661
8662
8663
cerion896a1372005-01-25 12:24:25 +00008664/*------------------------------------------------------------*/
8665/*--- Disassemble a single instruction ---*/
8666/*------------------------------------------------------------*/
8667
8668/* Disassemble a single instruction into IR. The instruction
sewardj9e6491a2005-07-02 19:24:10 +00008669 is located in host memory at &guest_code[delta]. */
8670
8671static
cerion5b2325f2005-12-23 00:55:09 +00008672DisResult disInstr_PPC_WRK (
sewardj9e6491a2005-07-02 19:24:10 +00008673 Bool put_IP,
sewardjc716aea2006-01-17 01:48:46 +00008674 Bool (*resteerOkFn) ( /*opaque*/void*, Addr64 ),
8675 void* callback_opaque,
sewardj9e6491a2005-07-02 19:24:10 +00008676 Long delta64,
8677 VexArchInfo* archinfo
8678 )
cerion896a1372005-01-25 12:24:25 +00008679{
sewardj9e6491a2005-07-02 19:24:10 +00008680 UChar opc1;
8681 UInt opc2;
8682 DisResult dres;
cerion896a1372005-01-25 12:24:25 +00008683 UInt theInstr;
ceriond953ebb2005-11-29 13:27:20 +00008684 IRType ty = mode64 ? Ity_I64 : Ity_I32;
sewardj5117ce12006-01-27 21:20:15 +00008685 Bool allow_F = False;
8686 Bool allow_V = False;
8687 Bool allow_FX = False;
8688 Bool allow_GX = False;
8689 UInt hwcaps = archinfo->hwcaps;
8690 Long delta;
cerion896a1372005-01-25 12:24:25 +00008691
sewardj059601a2005-11-13 00:53:05 +00008692 /* What insn variants are we supporting today? */
sewardj5117ce12006-01-27 21:20:15 +00008693 if (mode64) {
8694 allow_F = True;
8695 allow_V = (0 != (hwcaps & VEX_HWCAPS_PPC64_V));
8696 allow_FX = (0 != (hwcaps & VEX_HWCAPS_PPC64_FX));
8697 allow_GX = (0 != (hwcaps & VEX_HWCAPS_PPC64_GX));
8698 } else {
8699 allow_F = (0 != (hwcaps & VEX_HWCAPS_PPC32_F));
8700 allow_V = (0 != (hwcaps & VEX_HWCAPS_PPC32_V));
8701 allow_FX = (0 != (hwcaps & VEX_HWCAPS_PPC32_FX));
8702 allow_GX = (0 != (hwcaps & VEX_HWCAPS_PPC32_GX));
8703 }
sewardj059601a2005-11-13 00:53:05 +00008704
sewardj9e6491a2005-07-02 19:24:10 +00008705 /* The running delta */
sewardj5117ce12006-01-27 21:20:15 +00008706 delta = (Long)mkSzAddr(ty, (ULong)delta64);
sewardj9e6491a2005-07-02 19:24:10 +00008707
8708 /* Set result defaults. */
8709 dres.whatNext = Dis_Continue;
8710 dres.len = 0;
8711 dres.continueAt = 0;
cerion896a1372005-01-25 12:24:25 +00008712
cerion1515db92005-01-25 17:21:23 +00008713 /* At least this is simple on PPC32: insns are all 4 bytes long, and
cerion896a1372005-01-25 12:24:25 +00008714 4-aligned. So just fish the whole thing out of memory right now
8715 and have done. */
cerioncf004462005-01-31 15:24:55 +00008716 theInstr = getUIntBigendianly( (UChar*)(&guest_code[delta]) );
cerion896a1372005-01-25 12:24:25 +00008717
sewardj5117ce12006-01-27 21:20:15 +00008718 if (0) vex_printf("insn: 0x%x\n", theInstr);
cerionf0de28c2005-12-13 20:21:11 +00008719
sewardj1eb7e6b2006-01-12 21:13:14 +00008720 DIP("\t0x%llx: ", (ULong)guest_CIA_curr_instr);
sewardjb51f0f42005-07-18 11:38:02 +00008721
8722 /* We may be asked to update the guest CIA before going further. */
8723 if (put_IP)
cerion2831b002005-11-30 19:55:22 +00008724 putGST( PPC_GST_CIA, mkSzImm(ty, guest_CIA_curr_instr) );
cerion896a1372005-01-25 12:24:25 +00008725
sewardjce02aa72006-01-12 12:27:58 +00008726 /* Spot "Special" instructions (see comment at top of file). */
sewardj1eb7e6b2006-01-12 21:13:14 +00008727 {
sewardjce02aa72006-01-12 12:27:58 +00008728 UChar* code = (UChar*)(guest_code + delta);
sewardj1eb7e6b2006-01-12 21:13:14 +00008729 /* Spot the 16-byte preamble:
8730 32-bit mode:
8731 54001800 rlwinm 0,0,3,0,0
8732 54006800 rlwinm 0,0,13,0,0
8733 5400E800 rlwinm 0,0,29,0,0
8734 54009800 rlwinm 0,0,19,0,0
8735 64-bit mode:
8736 78001800 rotldi 0,0,3
8737 78006800 rotldi 0,0,13
8738 7800E802 rotldi 0,0,61
8739 78009802 rotldi 0,0,51
cerion896a1372005-01-25 12:24:25 +00008740 */
sewardj1eb7e6b2006-01-12 21:13:14 +00008741 UInt word1 = mode64 ? 0x78001800 : 0x54001800;
8742 UInt word2 = mode64 ? 0x78006800 : 0x54006800;
8743 UInt word3 = mode64 ? 0x7800E802 : 0x5400E800;
8744 UInt word4 = mode64 ? 0x78009802 : 0x54009800;
8745 if (getUIntBigendianly(code+ 0) == word1 &&
8746 getUIntBigendianly(code+ 4) == word2 &&
8747 getUIntBigendianly(code+ 8) == word3 &&
8748 getUIntBigendianly(code+12) == word4) {
sewardjce02aa72006-01-12 12:27:58 +00008749 /* Got a "Special" instruction preamble. Which one is it? */
8750 if (getUIntBigendianly(code+16) == 0x7C210B78 /* or 1,1,1 */) {
8751 /* %R3 = client_request ( %R4 ) */
8752 DIP("r3 = client_request ( %%r4 )\n");
8753 delta += 20;
8754 irbb->next = mkSzImm( ty, guest_CIA_bbstart + delta );
8755 irbb->jumpkind = Ijk_ClientReq;
8756 dres.whatNext = Dis_StopHere;
8757 goto decode_success;
8758 }
8759 else
8760 if (getUIntBigendianly(code+16) == 0x7C421378 /* or 2,2,2 */) {
8761 /* %R3 = guest_NRADDR */
8762 DIP("r3 = guest_NRADDR\n");
8763 delta += 20;
8764 dres.len = 20;
8765 putIReg(3, IRExpr_Get( OFFB_NRADDR, ty ));
8766 goto decode_success;
8767 }
8768 else
8769 if (getUIntBigendianly(code+16) == 0x7C631B78 /* or 3,3,3 */) {
8770 /* branch-and-link-to-noredir %R11 */
8771 DIP("branch-and-link-to-noredir r11\n");
8772 delta += 20;
sewardj1eb7e6b2006-01-12 21:13:14 +00008773 putGST( PPC_GST_LR, mkSzImm(ty, guest_CIA_bbstart + (Long)delta) );
sewardjce02aa72006-01-12 12:27:58 +00008774 irbb->next = getIReg(11);
8775 irbb->jumpkind = Ijk_NoRedir;
8776 dres.whatNext = Dis_StopHere;
8777 goto decode_success;
8778 }
sewardj5ff11dd2006-01-20 14:19:25 +00008779 else
8780 if (mode64
8781 && getUIntBigendianly(code+16) == 0x7C842378 /* or 4,4,4 */) {
8782 /* %R3 = guest_NRADDR_GPR2 */
8783 DIP("r3 = guest_NRADDR_GPR2\n");
8784 delta += 20;
8785 dres.len = 20;
8786 vassert(ty == Ity_I64);
8787 putIReg(3, IRExpr_Get( OFFB64_NRADDR_GPR2, ty ));
8788 goto decode_success;
8789 }
sewardjce02aa72006-01-12 12:27:58 +00008790 /* We don't know what it is. Set opc1/opc2 so decode_failure
8791 can print the insn following the Special-insn preamble. */
8792 theInstr = getUIntBigendianly(code+16);
8793 opc1 = ifieldOPC(theInstr);
8794 opc2 = ifieldOPClo10(theInstr);
8795 goto decode_failure;
8796 /*NOTREACHED*/
cerion896a1372005-01-25 12:24:25 +00008797 }
8798 }
8799
cerion76de5cf2005-11-18 18:25:12 +00008800 opc1 = ifieldOPC(theInstr);
sewardjb51f0f42005-07-18 11:38:02 +00008801 opc2 = ifieldOPClo10(theInstr);
cerion932ad942005-01-30 10:18:50 +00008802
cerion91ad5362005-01-27 23:02:41 +00008803 // Note: all 'reserved' bits must be cleared, else invalid
8804 switch (opc1) {
cerion896a1372005-01-25 12:24:25 +00008805
cerione9d361a2005-03-04 17:35:29 +00008806 /* Integer Arithmetic Instructions */
8807 case 0x0C: case 0x0D: case 0x0E: // addic, addic., addi
8808 case 0x0F: case 0x07: case 0x08: // addis, mulli, subfic
8809 if (dis_int_arith( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008810 goto decode_failure;
cerion7aa4bbc2005-01-29 09:32:07 +00008811
cerione9d361a2005-03-04 17:35:29 +00008812 /* Integer Compare Instructions */
8813 case 0x0B: case 0x0A: // cmpi, cmpli
8814 if (dis_int_cmp( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008815 goto decode_failure;
cerion7aa4bbc2005-01-29 09:32:07 +00008816
cerione9d361a2005-03-04 17:35:29 +00008817 /* Integer Logical Instructions */
8818 case 0x1C: case 0x1D: case 0x18: // andi., andis., ori
8819 case 0x19: case 0x1A: case 0x1B: // oris, xori, xoris
8820 if (dis_int_logic( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008821 goto decode_failure;
cerion7aa4bbc2005-01-29 09:32:07 +00008822
cerione9d361a2005-03-04 17:35:29 +00008823 /* Integer Rotate Instructions */
8824 case 0x14: case 0x15: case 0x17: // rlwimi, rlwinm, rlwnm
8825 if (dis_int_rot( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008826 goto decode_failure;
cerion645c9302005-01-31 10:09:59 +00008827
cerionf0de28c2005-12-13 20:21:11 +00008828 /* 64bit Integer Rotate Instructions */
8829 case 0x1E: // rldcl, rldcr, rldic, rldicl, rldicr, rldimi
8830 if (dis_int_rot( theInstr )) goto decode_success;
8831 goto decode_failure;
8832
cerione9d361a2005-03-04 17:35:29 +00008833 /* Integer Load Instructions */
8834 case 0x22: case 0x23: case 0x2A: // lbz, lbzu, lha
8835 case 0x2B: case 0x28: case 0x29: // lhau, lhz, lhzu
8836 case 0x20: case 0x21: // lwz, lwzu
8837 if (dis_int_load( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008838 goto decode_failure;
cerion645c9302005-01-31 10:09:59 +00008839
cerione9d361a2005-03-04 17:35:29 +00008840 /* Integer Store Instructions */
8841 case 0x26: case 0x27: case 0x2C: // stb, stbu, sth
8842 case 0x2D: case 0x24: case 0x25: // sthu, stw, stwu
8843 if (dis_int_store( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008844 goto decode_failure;
ceriond23be4e2005-01-31 07:23:07 +00008845
sewardj7787af42005-08-04 18:32:19 +00008846 /* Integer Load and Store Multiple Instructions */
8847 case 0x2E: case 0x2F: // lmw, stmw
8848 if (dis_int_ldst_mult( theInstr )) goto decode_success;
8849 goto decode_failure;
cerion645c9302005-01-31 10:09:59 +00008850
cerione9d361a2005-03-04 17:35:29 +00008851 /* Branch Instructions */
8852 case 0x12: case 0x10: // b, bc
sewardjc716aea2006-01-17 01:48:46 +00008853 if (dis_branch(theInstr, &dres, resteerOkFn, callback_opaque))
8854 goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008855 goto decode_failure;
cerion896a1372005-01-25 12:24:25 +00008856
cerione9d361a2005-03-04 17:35:29 +00008857 /* System Linkage Instructions */
cerion8c3adda2005-01-31 11:54:05 +00008858 case 0x11: // sc
sewardj9e6491a2005-07-02 19:24:10 +00008859 if (dis_syslink(theInstr, &dres)) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008860 goto decode_failure;
cerion26d07b22005-02-02 17:13:28 +00008861
sewardj334870d2006-02-07 16:42:39 +00008862 /* Trap Instructions */
8863 case 0x02: case 0x03: // tdi, twi
8864 if (dis_trapi(theInstr, &dres)) goto decode_success;
8865 goto decode_failure;
cerion8c3adda2005-01-31 11:54:05 +00008866
cerion3d870a32005-03-18 12:23:33 +00008867 /* Floating Point Load Instructions */
cerion094d1392005-06-20 13:45:57 +00008868 case 0x30: case 0x31: case 0x32: // lfs, lfsu, lfd
8869 case 0x33: // lfdu
sewardj5117ce12006-01-27 21:20:15 +00008870 if (!allow_F) goto decode_noF;
cerion3d870a32005-03-18 12:23:33 +00008871 if (dis_fp_load( theInstr )) goto decode_success;
cerione9d361a2005-03-04 17:35:29 +00008872 goto decode_failure;
cerion995bc362005-02-03 11:03:31 +00008873
cerion3d870a32005-03-18 12:23:33 +00008874 /* Floating Point Store Instructions */
8875 case 0x34: case 0x35: case 0x36: // stfsx, stfsux, stfdx
8876 case 0x37: // stfdux
sewardj5117ce12006-01-27 21:20:15 +00008877 if (!allow_F) goto decode_noF;
cerion3d870a32005-03-18 12:23:33 +00008878 if (dis_fp_store( theInstr )) goto decode_success;
8879 goto decode_failure;
8880
cerionf0de28c2005-12-13 20:21:11 +00008881 /* 64bit Integer Loads */
8882 case 0x3A: // ld, ldu, lwa
8883 if (!mode64) goto decode_failure;
8884 if (dis_int_load( theInstr )) goto decode_success;
8885 goto decode_failure;
8886
sewardje14bb9f2005-07-22 09:39:02 +00008887 case 0x3B:
sewardj5117ce12006-01-27 21:20:15 +00008888 if (!allow_F) goto decode_noF;
cerion76de5cf2005-11-18 18:25:12 +00008889 opc2 = IFIELD(theInstr, 1, 5);
sewardje14bb9f2005-07-22 09:39:02 +00008890 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00008891 /* Floating Point Arith Instructions */
8892 case 0x12: case 0x14: case 0x15: // fdivs, fsubs, fadds
sewardj5117ce12006-01-27 21:20:15 +00008893 case 0x19: // fmuls
ceriond953ebb2005-11-29 13:27:20 +00008894 if (dis_fp_arith(theInstr)) goto decode_success;
8895 goto decode_failure;
sewardj5117ce12006-01-27 21:20:15 +00008896 case 0x16: // fsqrts
8897 if (!allow_FX) goto decode_noFX;
8898 if (dis_fp_arith(theInstr)) goto decode_success;
8899 goto decode_failure;
8900 case 0x18: // fres
8901 if (!allow_GX) goto decode_noGX;
8902 if (dis_fp_arith(theInstr)) goto decode_success;
8903 goto decode_failure;
8904
ceriond953ebb2005-11-29 13:27:20 +00008905 /* Floating Point Mult-Add Instructions */
8906 case 0x1C: case 0x1D: case 0x1E: // fmsubs, fmadds, fnmsubs
8907 case 0x1F: // fnmadds
8908 if (dis_fp_multadd(theInstr)) goto decode_success;
8909 goto decode_failure;
sewardj79fd33f2006-01-29 17:07:57 +00008910
8911 case 0x1A: // frsqrtes
8912 if (!allow_GX) goto decode_noGX;
8913 if (dis_fp_arith(theInstr)) goto decode_success;
8914 goto decode_failure;
ceriond953ebb2005-11-29 13:27:20 +00008915
8916 default:
8917 goto decode_failure;
sewardje14bb9f2005-07-22 09:39:02 +00008918 }
8919 break;
cerion3d870a32005-03-18 12:23:33 +00008920
cerionf0de28c2005-12-13 20:21:11 +00008921 /* 64bit Integer Stores */
8922 case 0x3E: // std, stdu
8923 if (!mode64) goto decode_failure;
8924 if (dis_int_store( theInstr )) goto decode_success;
8925 goto decode_failure;
8926
cerion3d870a32005-03-18 12:23:33 +00008927 case 0x3F:
sewardj5117ce12006-01-27 21:20:15 +00008928 if (!allow_F) goto decode_noF;
cerion5b2325f2005-12-23 00:55:09 +00008929 /* Instrs using opc[1:5] never overlap instrs using opc[1:10],
cerion3d870a32005-03-18 12:23:33 +00008930 so we can simply fall through the first switch statement */
8931
cerion76de5cf2005-11-18 18:25:12 +00008932 opc2 = IFIELD(theInstr, 1, 5);
cerion3d870a32005-03-18 12:23:33 +00008933 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00008934 /* Floating Point Arith Instructions */
sewardj5117ce12006-01-27 21:20:15 +00008935 case 0x12: case 0x14: case 0x15: // fdiv, fsub, fadd
8936 case 0x19: // fmul
8937 if (dis_fp_arith(theInstr)) goto decode_success;
8938 goto decode_failure;
8939 case 0x16: // fsqrt
8940 if (!allow_FX) goto decode_noFX;
8941 if (dis_fp_arith(theInstr)) goto decode_success;
8942 goto decode_failure;
8943 case 0x17: case 0x1A: // fsel, frsqrte
8944 if (!allow_GX) goto decode_noGX;
ceriond953ebb2005-11-29 13:27:20 +00008945 if (dis_fp_arith(theInstr)) goto decode_success;
8946 goto decode_failure;
sewardje14bb9f2005-07-22 09:39:02 +00008947
ceriond953ebb2005-11-29 13:27:20 +00008948 /* Floating Point Mult-Add Instructions */
8949 case 0x1C: case 0x1D: case 0x1E: // fmsub, fmadd, fnmsub
8950 case 0x1F: // fnmadd
8951 if (dis_fp_multadd(theInstr)) goto decode_success;
8952 goto decode_failure;
sewardj79fd33f2006-01-29 17:07:57 +00008953
8954 case 0x18: // fre
8955 if (!allow_GX) goto decode_noGX;
8956 if (dis_fp_arith(theInstr)) goto decode_success;
8957 goto decode_failure;
ceriond953ebb2005-11-29 13:27:20 +00008958
8959 default:
8960 break; // Fall through
cerion3d870a32005-03-18 12:23:33 +00008961 }
8962
cerion76de5cf2005-11-18 18:25:12 +00008963 opc2 = IFIELD(theInstr, 1, 10);
sewardje14bb9f2005-07-22 09:39:02 +00008964 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00008965 /* Floating Point Compare Instructions */
8966 case 0x000: // fcmpu
8967 case 0x020: // fcmpo
8968 if (dis_fp_cmp(theInstr)) goto decode_success;
8969 goto decode_failure;
cerion2831b002005-11-30 19:55:22 +00008970
ceriond953ebb2005-11-29 13:27:20 +00008971 /* Floating Point Rounding/Conversion Instructions */
8972 case 0x00C: // frsp
8973 case 0x00E: // fctiw
8974 case 0x00F: // fctiwz
sewardj6be67232006-01-24 19:00:05 +00008975 case 0x32E: // fctid
8976 case 0x32F: // fctidz
8977 case 0x34E: // fcfid
ceriond953ebb2005-11-29 13:27:20 +00008978 if (dis_fp_round(theInstr)) goto decode_success;
8979 goto decode_failure;
8980
8981 /* Floating Point Move Instructions */
8982 case 0x028: // fneg
8983 case 0x048: // fmr
8984 case 0x088: // fnabs
8985 case 0x108: // fabs
8986 if (dis_fp_move( theInstr )) goto decode_success;
8987 goto decode_failure;
sewardje14bb9f2005-07-22 09:39:02 +00008988
ceriond953ebb2005-11-29 13:27:20 +00008989 /* Floating Point Status/Control Register Instructions */
cerion3ea49ee2006-01-04 10:53:00 +00008990 case 0x026: // mtfsb1
sewardj6be67232006-01-24 19:00:05 +00008991 /* case 0x040: // mcrfs */
ceriond953ebb2005-11-29 13:27:20 +00008992 case 0x046: // mtfsb0
8993 case 0x086: // mtfsfi
8994 case 0x247: // mffs
8995 case 0x2C7: // mtfsf
8996 if (dis_fp_scr( theInstr )) goto decode_success;
8997 goto decode_failure;
cerionf0de28c2005-12-13 20:21:11 +00008998
ceriond953ebb2005-11-29 13:27:20 +00008999 default:
9000 goto decode_failure;
sewardje14bb9f2005-07-22 09:39:02 +00009001 }
cerion3d870a32005-03-18 12:23:33 +00009002 break;
ceriond953ebb2005-11-29 13:27:20 +00009003
cerion91ad5362005-01-27 23:02:41 +00009004 case 0x13:
cerionb85e8bb2005-02-16 08:54:33 +00009005 switch (opc2) {
cerion91ad5362005-01-27 23:02:41 +00009006
ceriond953ebb2005-11-29 13:27:20 +00009007 /* Condition Register Logical Instructions */
9008 case 0x101: case 0x081: case 0x121: // crand, crandc, creqv
9009 case 0x0E1: case 0x021: case 0x1C1: // crnand, crnor, cror
9010 case 0x1A1: case 0x0C1: case 0x000: // crorc, crxor, mcrf
9011 if (dis_cond_logic( theInstr )) goto decode_success;
9012 goto decode_failure;
cerionb85e8bb2005-02-16 08:54:33 +00009013
ceriond953ebb2005-11-29 13:27:20 +00009014 /* Branch Instructions */
9015 case 0x210: case 0x010: // bcctr, bclr
sewardjc716aea2006-01-17 01:48:46 +00009016 if (dis_branch(theInstr, &dres, resteerOkFn, callback_opaque))
9017 goto decode_success;
ceriond953ebb2005-11-29 13:27:20 +00009018 goto decode_failure;
9019
9020 /* Memory Synchronization Instructions */
9021 case 0x096: // isync
9022 if (dis_memsync( theInstr )) goto decode_success;
9023 goto decode_failure;
9024
9025 default:
9026 goto decode_failure;
cerionb85e8bb2005-02-16 08:54:33 +00009027 }
9028 break;
cerion91ad5362005-01-27 23:02:41 +00009029
9030
cerionb85e8bb2005-02-16 08:54:33 +00009031 case 0x1F:
cerione9d361a2005-03-04 17:35:29 +00009032
9033 /* For arith instns, bit10 is the OE flag (overflow enable) */
9034
cerion76de5cf2005-11-18 18:25:12 +00009035 opc2 = IFIELD(theInstr, 1, 9);
cerionb85e8bb2005-02-16 08:54:33 +00009036 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00009037 /* Integer Arithmetic Instructions */
9038 case 0x10A: case 0x00A: case 0x08A: // add, addc, adde
9039 case 0x0EA: case 0x0CA: case 0x1EB: // addme, addze, divw
9040 case 0x1CB: case 0x04B: case 0x00B: // divwu, mulhw, mulhwu
9041 case 0x0EB: case 0x068: case 0x028: // mullw, neg, subf
9042 case 0x008: case 0x088: case 0x0E8: // subfc, subfe, subfme
9043 case 0x0C8: // subfze
9044 if (dis_int_arith( theInstr )) goto decode_success;
9045 goto decode_failure;
cerionf0de28c2005-12-13 20:21:11 +00009046
9047 /* 64bit Integer Arithmetic */
9048 case 0x009: case 0x049: case 0x0E9: // mulhdu, mulhd, mulld
9049 case 0x1C9: case 0x1E9: // divdu, divd
9050 if (!mode64) goto decode_failure;
9051 if (dis_int_arith( theInstr )) goto decode_success;
9052 goto decode_failure;
9053
ceriond953ebb2005-11-29 13:27:20 +00009054 default:
9055 break; // Fall through...
cerionb85e8bb2005-02-16 08:54:33 +00009056 }
cerion91ad5362005-01-27 23:02:41 +00009057
cerione9d361a2005-03-04 17:35:29 +00009058 /* All remaining opcodes use full 10 bits. */
9059
cerion76de5cf2005-11-18 18:25:12 +00009060 opc2 = IFIELD(theInstr, 1, 10);
cerionb85e8bb2005-02-16 08:54:33 +00009061 switch (opc2) {
cerione9d361a2005-03-04 17:35:29 +00009062 /* Integer Compare Instructions */
9063 case 0x000: case 0x020: // cmp, cmpl
9064 if (dis_int_cmp( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009065 goto decode_failure;
cerion7aa4bbc2005-01-29 09:32:07 +00009066
cerione9d361a2005-03-04 17:35:29 +00009067 /* Integer Logical Instructions */
9068 case 0x01C: case 0x03C: case 0x01A: // and, andc, cntlzw
9069 case 0x11C: case 0x3BA: case 0x39A: // eqv, extsb, extsh
9070 case 0x1DC: case 0x07C: case 0x1BC: // nand, nor, or
9071 case 0x19C: case 0x13C: // orc, xor
9072 if (dis_int_logic( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009073 goto decode_failure;
cerion932ad942005-01-30 10:18:50 +00009074
cerionf0de28c2005-12-13 20:21:11 +00009075 /* 64bit Integer Logical Instructions */
cerion07b07a92005-12-22 14:32:35 +00009076 case 0x3DA: case 0x03A: // extsw, cntlzd
cerionf0de28c2005-12-13 20:21:11 +00009077 if (!mode64) goto decode_failure;
9078 if (dis_int_logic( theInstr )) goto decode_success;
9079 goto decode_failure;
9080
cerione9d361a2005-03-04 17:35:29 +00009081 /* Integer Shift Instructions */
9082 case 0x018: case 0x318: case 0x338: // slw, sraw, srawi
9083 case 0x218: // srw
9084 if (dis_int_shift( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009085 goto decode_failure;
cerion7aa4bbc2005-01-29 09:32:07 +00009086
cerionf0de28c2005-12-13 20:21:11 +00009087 /* 64bit Integer Shift Instructions */
9088 case 0x01B: case 0x31A: // sld, srad
cerion07b07a92005-12-22 14:32:35 +00009089 case 0x33A: case 0x33B: // sradi
cerionf0de28c2005-12-13 20:21:11 +00009090 case 0x21B: // srd
9091 if (!mode64) goto decode_failure;
9092 if (dis_int_shift( theInstr )) goto decode_success;
9093 goto decode_failure;
9094
cerione9d361a2005-03-04 17:35:29 +00009095 /* Integer Load Instructions */
9096 case 0x057: case 0x077: case 0x157: // lbzx, lbzux, lhax
9097 case 0x177: case 0x117: case 0x137: // lhaux, lhzx, lhzux
9098 case 0x017: case 0x037: // lwzx, lwzux
9099 if (dis_int_load( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009100 goto decode_failure;
cerion7aa4bbc2005-01-29 09:32:07 +00009101
cerionf0de28c2005-12-13 20:21:11 +00009102 /* 64bit Integer Load Instructions */
9103 case 0x035: case 0x015: // ldux, ldx
9104 case 0x175: case 0x155: // lwaux, lwax
9105 if (!mode64) goto decode_failure;
9106 if (dis_int_load( theInstr )) goto decode_success;
9107 goto decode_failure;
9108
sewardjb51f0f42005-07-18 11:38:02 +00009109 /* Integer Store Instructions */
cerione9d361a2005-03-04 17:35:29 +00009110 case 0x0F7: case 0x0D7: case 0x1B7: // stbux, stbx, sthux
9111 case 0x197: case 0x0B7: case 0x097: // sthx, stwux, stwx
9112 if (dis_int_store( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009113 goto decode_failure;
cerion91ad5362005-01-27 23:02:41 +00009114
cerionf0de28c2005-12-13 20:21:11 +00009115 /* 64bit Integer Store Instructions */
9116 case 0x0B5: case 0x095: // stdux, stdx
9117 if (!mode64) goto decode_failure;
9118 if (dis_int_store( theInstr )) goto decode_success;
9119 goto decode_failure;
9120
sewardj602857d2005-09-06 09:10:09 +00009121 /* Integer Load and Store with Byte Reverse Instructions */
9122 case 0x316: case 0x216: case 0x396: // lhbrx, lwbrx, sthbrx
9123 case 0x296: // stwbrx
9124 if (dis_int_ldst_rev( theInstr )) goto decode_success;
9125 goto decode_failure;
9126
sewardj87e651f2005-09-09 08:31:18 +00009127 /* Integer Load and Store String Instructions */
9128 case 0x255: case 0x215: case 0x2D5: // lswi, lswx, stswi
9129 case 0x295: { // stswx
9130 Bool stopHere = False;
9131 Bool ok = dis_int_ldst_str( theInstr, &stopHere );
9132 if (!ok) goto decode_failure;
9133 if (stopHere) {
cerion2831b002005-11-30 19:55:22 +00009134 irbb->next = mkSzImm(ty, nextInsnAddr());
sewardj87e651f2005-09-09 08:31:18 +00009135 irbb->jumpkind = Ijk_Boring;
ceriond953ebb2005-11-29 13:27:20 +00009136 dres.whatNext = Dis_StopHere;
sewardj87e651f2005-09-09 08:31:18 +00009137 }
9138 goto decode_success;
9139 }
cerion645c9302005-01-31 10:09:59 +00009140
cerione9d361a2005-03-04 17:35:29 +00009141 /* Memory Synchronization Instructions */
9142 case 0x356: case 0x014: case 0x096: // eieio, lwarx, stwcx.
9143 case 0x256: // sync
9144 if (dis_memsync( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009145 goto decode_failure;
9146
cerionf0de28c2005-12-13 20:21:11 +00009147 /* 64bit Memory Synchronization Instructions */
9148 case 0x054: case 0x0D6: // ldarx, stdcx.
9149 if (!mode64) goto decode_failure;
9150 if (dis_memsync( theInstr )) goto decode_success;
9151 goto decode_failure;
9152
cerione9d361a2005-03-04 17:35:29 +00009153 /* Processor Control Instructions */
9154 case 0x200: case 0x013: case 0x153: // mcrxr, mfcr, mfspr
9155 case 0x173: case 0x090: case 0x1D3: // mftb, mtcrf, mtspr
9156 if (dis_proc_ctl( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009157 goto decode_failure;
cerion645c9302005-01-31 10:09:59 +00009158
cerione9d361a2005-03-04 17:35:29 +00009159 /* Cache Management Instructions */
9160 case 0x2F6: case 0x056: case 0x036: // dcba, dcbf, dcbst
9161 case 0x116: case 0x0F6: case 0x3F6: // dcbt, dcbtst, dcbz
9162 case 0x3D6: // icbi
sewardj9e6491a2005-07-02 19:24:10 +00009163 if (dis_cache_manage( theInstr, &dres, archinfo ))
sewardjd94b73a2005-06-30 12:08:48 +00009164 goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009165 goto decode_failure;
ceriond23be4e2005-01-31 07:23:07 +00009166
sewardjb51f0f42005-07-18 11:38:02 +00009167//zz /* External Control Instructions */
9168//zz case 0x136: case 0x1B6: // eciwx, ecowx
9169//zz DIP("external control op => not implemented\n");
9170//zz goto decode_failure;
9171//zz
9172//zz /* Trap Instructions */
9173//zz case 0x004: // tw
9174//zz DIP("trap op (tw) => not implemented\n");
9175//zz goto decode_failure;
cerionf0de28c2005-12-13 20:21:11 +00009176//zz case 0x044: // td
9177//zz DIP("trap op (td) => not implemented\n");
9178//zz goto decode_failure;
sewardje14bb9f2005-07-22 09:39:02 +00009179
9180 /* Floating Point Load Instructions */
9181 case 0x217: case 0x237: case 0x257: // lfsx, lfsux, lfdx
9182 case 0x277: // lfdux
sewardj5117ce12006-01-27 21:20:15 +00009183 if (!allow_F) goto decode_noF;
sewardje14bb9f2005-07-22 09:39:02 +00009184 if (dis_fp_load( theInstr )) goto decode_success;
9185 goto decode_failure;
9186
9187 /* Floating Point Store Instructions */
9188 case 0x297: case 0x2B7: case 0x2D7: // stfs, stfsu, stfd
sewardj5117ce12006-01-27 21:20:15 +00009189 case 0x2F7: // stfdu, stfiwx
9190 if (!allow_F) goto decode_noF;
sewardje14bb9f2005-07-22 09:39:02 +00009191 if (dis_fp_store( theInstr )) goto decode_success;
9192 goto decode_failure;
sewardj5117ce12006-01-27 21:20:15 +00009193 case 0x3D7: // stfiwx
9194 if (!allow_F) goto decode_noF;
9195 if (!allow_GX) goto decode_noGX;
9196 if (dis_fp_store( theInstr )) goto decode_success;
9197 goto decode_failure;
sewardje14bb9f2005-07-22 09:39:02 +00009198
cerion32aad402005-09-10 12:02:24 +00009199 /* AltiVec instructions */
9200
9201 /* AV Cache Control - Data streams */
9202 case 0x156: case 0x176: case 0x336: // dst, dstst, dss
sewardj5117ce12006-01-27 21:20:15 +00009203 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009204 if (dis_av_datastream( theInstr )) goto decode_success;
9205 goto decode_failure;
ceriona982c052005-06-28 17:23:09 +00009206
9207 /* AV Load */
9208 case 0x006: case 0x026: // lvsl, lvsr
9209 case 0x007: case 0x027: case 0x047: // lvebx, lvehx, lvewx
9210 case 0x067: case 0x167: // lvx, lvxl
sewardj5117ce12006-01-27 21:20:15 +00009211 if (!allow_V) goto decode_noV;
ceriona982c052005-06-28 17:23:09 +00009212 if (dis_av_load( theInstr )) goto decode_success;
9213 goto decode_failure;
9214
9215 /* AV Store */
9216 case 0x087: case 0x0A7: case 0x0C7: // stvebx, stvehx, stvewx
9217 case 0x0E7: case 0x1E7: // stvx, stvxl
sewardj5117ce12006-01-27 21:20:15 +00009218 if (!allow_V) goto decode_noV;
ceriona982c052005-06-28 17:23:09 +00009219 if (dis_av_store( theInstr )) goto decode_success;
9220 goto decode_failure;
9221
9222 default:
9223 goto decode_failure;
9224 }
9225 break;
9226
9227
cerion32aad402005-09-10 12:02:24 +00009228 case 0x04:
9229 /* AltiVec instructions */
9230
cerion76de5cf2005-11-18 18:25:12 +00009231 opc2 = IFIELD(theInstr, 0, 6);
cerion32aad402005-09-10 12:02:24 +00009232 switch (opc2) {
9233 /* AV Mult-Add, Mult-Sum */
9234 case 0x20: case 0x21: case 0x22: // vmhaddshs, vmhraddshs, vmladduhm
9235 case 0x24: case 0x25: case 0x26: // vmsumubm, vmsummbm, vmsumuhm
9236 case 0x27: case 0x28: case 0x29: // vmsumuhs, vmsumshm, vmsumshs
sewardj5117ce12006-01-27 21:20:15 +00009237 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009238 if (dis_av_multarith( theInstr )) goto decode_success;
9239 goto decode_failure;
9240
9241 /* AV Permutations */
9242 case 0x2A: // vsel
9243 case 0x2B: // vperm
cerion32aad402005-09-10 12:02:24 +00009244 case 0x2C: // vsldoi
sewardj5117ce12006-01-27 21:20:15 +00009245 if (!allow_V) goto decode_noV;
cerion92d9d872005-09-15 21:58:50 +00009246 if (dis_av_permute( theInstr )) goto decode_success;
cerion32aad402005-09-10 12:02:24 +00009247 goto decode_failure;
9248
9249 /* AV Floating Point Mult-Add/Sub */
9250 case 0x2E: case 0x2F: // vmaddfp, vnmsubfp
sewardj5117ce12006-01-27 21:20:15 +00009251 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009252 if (dis_av_fp_arith( theInstr )) goto decode_success;
9253 goto decode_failure;
9254
9255 default:
9256 break; // Fall through...
9257 }
9258
cerion76de5cf2005-11-18 18:25:12 +00009259 opc2 = IFIELD(theInstr, 0, 11);
cerion32aad402005-09-10 12:02:24 +00009260 switch (opc2) {
9261 /* AV Arithmetic */
9262 case 0x180: // vaddcuw
9263 case 0x000: case 0x040: case 0x080: // vaddubm, vadduhm, vadduwm
9264 case 0x200: case 0x240: case 0x280: // vaddubs, vadduhs, vadduws
9265 case 0x300: case 0x340: case 0x380: // vaddsbs, vaddshs, vaddsws
9266 case 0x580: // vsubcuw
9267 case 0x400: case 0x440: case 0x480: // vsububm, vsubuhm, vsubuwm
9268 case 0x600: case 0x640: case 0x680: // vsububs, vsubuhs, vsubuws
9269 case 0x700: case 0x740: case 0x780: // vsubsbs, vsubshs, vsubsws
9270 case 0x402: case 0x442: case 0x482: // vavgub, vavguh, vavguw
9271 case 0x502: case 0x542: case 0x582: // vavgsb, vavgsh, vavgsw
9272 case 0x002: case 0x042: case 0x082: // vmaxub, vmaxuh, vmaxuw
9273 case 0x102: case 0x142: case 0x182: // vmaxsb, vmaxsh, vmaxsw
9274 case 0x202: case 0x242: case 0x282: // vminub, vminuh, vminuw
9275 case 0x302: case 0x342: case 0x382: // vminsb, vminsh, vminsw
9276 case 0x008: case 0x048: // vmuloub, vmulouh
9277 case 0x108: case 0x148: // vmulosb, vmulosh
9278 case 0x208: case 0x248: // vmuleub, vmuleuh
9279 case 0x308: case 0x348: // vmulesb, vmulesh
9280 case 0x608: case 0x708: case 0x648: // vsum4ubs, vsum4sbs, vsum4shs
9281 case 0x688: case 0x788: // vsum2sws, vsumsws
sewardj5117ce12006-01-27 21:20:15 +00009282 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009283 if (dis_av_arith( theInstr )) goto decode_success;
9284 goto decode_failure;
9285
9286 /* AV Rotate, Shift */
9287 case 0x004: case 0x044: case 0x084: // vrlb, vrlh, vrlw
9288 case 0x104: case 0x144: case 0x184: // vslb, vslh, vslw
9289 case 0x204: case 0x244: case 0x284: // vsrb, vsrh, vsrw
9290 case 0x304: case 0x344: case 0x384: // vsrab, vsrah, vsraw
9291 case 0x1C4: case 0x2C4: // vsl, vsr
9292 case 0x40C: case 0x44C: // vslo, vsro
sewardj5117ce12006-01-27 21:20:15 +00009293 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009294 if (dis_av_shift( theInstr )) goto decode_success;
9295 goto decode_failure;
9296
9297 /* AV Logic */
9298 case 0x404: case 0x444: case 0x484: // vand, vandc, vor
9299 case 0x4C4: case 0x504: // vxor, vnor
sewardj5117ce12006-01-27 21:20:15 +00009300 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009301 if (dis_av_logic( theInstr )) goto decode_success;
9302 goto decode_failure;
9303
9304 /* AV Processor Control */
9305 case 0x604: case 0x644: // mfvscr, mtvscr
sewardj5117ce12006-01-27 21:20:15 +00009306 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009307 if (dis_av_procctl( theInstr )) goto decode_success;
9308 goto decode_failure;
9309
9310 /* AV Floating Point Arithmetic */
9311 case 0x00A: case 0x04A: // vaddfp, vsubfp
9312 case 0x10A: case 0x14A: case 0x18A: // vrefp, vrsqrtefp, vexptefp
9313 case 0x1CA: // vlogefp
9314 case 0x40A: case 0x44A: // vmaxfp, vminfp
sewardj5117ce12006-01-27 21:20:15 +00009315 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009316 if (dis_av_fp_arith( theInstr )) goto decode_success;
9317 goto decode_failure;
9318
9319 /* AV Floating Point Round/Convert */
9320 case 0x20A: case 0x24A: case 0x28A: // vrfin, vrfiz, vrfip
9321 case 0x2CA: // vrfim
9322 case 0x30A: case 0x34A: case 0x38A: // vcfux, vcfsx, vctuxs
9323 case 0x3CA: // vctsxs
sewardj5117ce12006-01-27 21:20:15 +00009324 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009325 if (dis_av_fp_convert( theInstr )) goto decode_success;
9326 goto decode_failure;
9327
9328 /* AV Merge, Splat */
9329 case 0x00C: case 0x04C: case 0x08C: // vmrghb, vmrghh, vmrghw
9330 case 0x10C: case 0x14C: case 0x18C: // vmrglb, vmrglh, vmrglw
9331 case 0x20C: case 0x24C: case 0x28C: // vspltb, vsplth, vspltw
9332 case 0x30C: case 0x34C: case 0x38C: // vspltisb, vspltish, vspltisw
sewardj5117ce12006-01-27 21:20:15 +00009333 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009334 if (dis_av_permute( theInstr )) goto decode_success;
9335 goto decode_failure;
9336
9337 /* AV Pack, Unpack */
9338 case 0x00E: case 0x04E: case 0x08E: // vpkuhum, vpkuwum, vpkuhus
9339 case 0x0CE: // vpkuwus
9340 case 0x10E: case 0x14E: case 0x18E: // vpkshus, vpkswus, vpkshss
9341 case 0x1CE: // vpkswss
9342 case 0x20E: case 0x24E: case 0x28E: // vupkhsb, vupkhsh, vupklsb
9343 case 0x2CE: // vupklsh
9344 case 0x30E: case 0x34E: case 0x3CE: // vpkpx, vupkhpx, vupklpx
sewardj5117ce12006-01-27 21:20:15 +00009345 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009346 if (dis_av_pack( theInstr )) goto decode_success;
9347 goto decode_failure;
9348
9349 default:
9350 break; // Fall through...
9351 }
9352
cerion76de5cf2005-11-18 18:25:12 +00009353 opc2 = IFIELD(theInstr, 0, 10);
cerion32aad402005-09-10 12:02:24 +00009354 switch (opc2) {
9355
9356 /* AV Compare */
9357 case 0x006: case 0x046: case 0x086: // vcmpequb, vcmpequh, vcmpequw
9358 case 0x206: case 0x246: case 0x286: // vcmpgtub, vcmpgtuh, vcmpgtuw
9359 case 0x306: case 0x346: case 0x386: // vcmpgtsb, vcmpgtsh, vcmpgtsw
sewardj5117ce12006-01-27 21:20:15 +00009360 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009361 if (dis_av_cmp( theInstr )) goto decode_success;
9362 goto decode_failure;
9363
9364 /* AV Floating Point Compare */
9365 case 0x0C6: case 0x1C6: case 0x2C6: // vcmpeqfp, vcmpgefp, vcmpgtfp
9366 case 0x3C6: // vcmpbfp
sewardj5117ce12006-01-27 21:20:15 +00009367 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009368 if (dis_av_fp_cmp( theInstr )) goto decode_success;
9369 goto decode_failure;
9370
9371 default:
9372 goto decode_failure;
9373 }
9374 break;
cerion7aa4bbc2005-01-29 09:32:07 +00009375
cerion896a1372005-01-25 12:24:25 +00009376 default:
cerion5b2325f2005-12-23 00:55:09 +00009377 goto decode_failure;
9378
sewardj5117ce12006-01-27 21:20:15 +00009379 decode_noF:
9380 vassert(!allow_F);
9381 vex_printf("disInstr(ppc): declined to decode an FP insn.\n");
9382 goto decode_failure;
9383 decode_noV:
9384 vassert(!allow_V);
9385 vex_printf("disInstr(ppc): declined to decode an AltiVec insn.\n");
9386 goto decode_failure;
9387 decode_noFX:
sewardj7c545862006-01-27 21:52:19 +00009388 vassert(!allow_FX);
sewardj5117ce12006-01-27 21:20:15 +00009389 vex_printf("disInstr(ppc): "
sewardjb183b852006-02-03 16:08:03 +00009390 "declined to decode a GeneralPurpose-Optional insn.\n");
sewardj5117ce12006-01-27 21:20:15 +00009391 goto decode_failure;
9392 decode_noGX:
sewardj7c545862006-01-27 21:52:19 +00009393 vassert(!allow_GX);
sewardj5117ce12006-01-27 21:20:15 +00009394 vex_printf("disInstr(ppc): "
9395 "declined to decode a Graphics-Optional insn.\n");
cerion5b2325f2005-12-23 00:55:09 +00009396 goto decode_failure;
9397
cerion896a1372005-01-25 12:24:25 +00009398 decode_failure:
9399 /* All decode failures end up here. */
cerion225a0342005-09-12 20:49:09 +00009400 opc2 = (theInstr) & 0x7FF;
cerion5b2325f2005-12-23 00:55:09 +00009401 vex_printf("disInstr(ppc): unhandled instruction: "
cerion896a1372005-01-25 12:24:25 +00009402 "0x%x\n", theInstr);
sewardjc7cd2142005-09-09 22:31:49 +00009403 vex_printf(" primary %d(0x%x), secondary %u(0x%x)\n",
sewardjb51f0f42005-07-18 11:38:02 +00009404 opc1, opc1, opc2, opc2);
cerion995bc362005-02-03 11:03:31 +00009405
sewardj01a9e802005-02-01 20:46:00 +00009406 /* Tell the dispatcher that this insn cannot be decoded, and so has
9407 not been executed, and (is currently) the next to be executed.
9408 CIA should be up-to-date since it made so at the start of each
9409 insn, but nevertheless be paranoid and update it again right
9410 now. */
cerion2831b002005-11-30 19:55:22 +00009411 putGST( PPC_GST_CIA, mkSzImm(ty, guest_CIA_curr_instr) );
9412 irbb->next = mkSzImm(ty, guest_CIA_curr_instr);
sewardj01a9e802005-02-01 20:46:00 +00009413 irbb->jumpkind = Ijk_NoDecode;
ceriond953ebb2005-11-29 13:27:20 +00009414 dres.whatNext = Dis_StopHere;
9415 dres.len = 0;
sewardj9e6491a2005-07-02 19:24:10 +00009416 return dres;
cerion896a1372005-01-25 12:24:25 +00009417
9418 } /* switch (opc) for the main (primary) opcode switch. */
9419
9420 decode_success:
9421 /* All decode successes end up here. */
cerion896a1372005-01-25 12:24:25 +00009422 DIP("\n");
9423
sewardjce02aa72006-01-12 12:27:58 +00009424 if (dres.len == 0) {
9425 dres.len = 4;
9426 } else {
9427 vassert(dres.len == 20);
9428 }
sewardj9e6491a2005-07-02 19:24:10 +00009429 return dres;
cerion896a1372005-01-25 12:24:25 +00009430}
9431
9432#undef DIP
9433#undef DIS
9434
sewardj9e6491a2005-07-02 19:24:10 +00009435
9436/*------------------------------------------------------------*/
9437/*--- Top-level fn ---*/
9438/*------------------------------------------------------------*/
9439
9440/* Disassemble a single instruction into IR. The instruction
9441 is located in host memory at &guest_code[delta]. */
9442
cerion5b2325f2005-12-23 00:55:09 +00009443DisResult disInstr_PPC ( IRBB* irbb_IN,
9444 Bool put_IP,
sewardjc716aea2006-01-17 01:48:46 +00009445 Bool (*resteerOkFn) ( void*, Addr64 ),
9446 void* callback_opaque,
cerion5b2325f2005-12-23 00:55:09 +00009447 UChar* guest_code_IN,
9448 Long delta,
9449 Addr64 guest_IP,
sewardja5f55da2006-04-30 23:37:32 +00009450 VexArch guest_arch,
cerion5b2325f2005-12-23 00:55:09 +00009451 VexArchInfo* archinfo,
9452 Bool host_bigendian_IN )
sewardj9e6491a2005-07-02 19:24:10 +00009453{
sewardj5df65bb2005-11-29 14:47:04 +00009454 IRType ty;
9455 DisResult dres;
sewardj5117ce12006-01-27 21:20:15 +00009456 UInt mask32, mask64;
9457 UInt hwcaps_guest = archinfo->hwcaps;
9458
sewardja5f55da2006-04-30 23:37:32 +00009459 vassert(guest_arch == VexArchPPC32 || guest_arch == VexArchPPC64);
sewardj5df65bb2005-11-29 14:47:04 +00009460
sewardja5f55da2006-04-30 23:37:32 +00009461 /* global -- ick */
9462 mode64 = guest_arch == VexArchPPC64;
9463 ty = mode64 ? Ity_I64 : Ity_I32;
9464
9465 /* do some sanity checks */
sewardj5117ce12006-01-27 21:20:15 +00009466 mask32 = VEX_HWCAPS_PPC32_F | VEX_HWCAPS_PPC32_V
9467 | VEX_HWCAPS_PPC32_FX | VEX_HWCAPS_PPC32_GX;
9468
sewardj5117ce12006-01-27 21:20:15 +00009469 mask64 = VEX_HWCAPS_PPC64_V
9470 | VEX_HWCAPS_PPC64_FX | VEX_HWCAPS_PPC64_GX;
9471
sewardja5f55da2006-04-30 23:37:32 +00009472 if (mode64) {
9473 vassert((hwcaps_guest & mask32) == 0);
9474 } else {
9475 vassert((hwcaps_guest & mask64) == 0);
9476 }
sewardj9e6491a2005-07-02 19:24:10 +00009477
9478 /* Set globals (see top of this file) */
9479 guest_code = guest_code_IN;
9480 irbb = irbb_IN;
9481 host_is_bigendian = host_bigendian_IN;
ceriond953ebb2005-11-29 13:27:20 +00009482
cerion2831b002005-11-30 19:55:22 +00009483 guest_CIA_curr_instr = mkSzAddr(ty, guest_IP);
9484 guest_CIA_bbstart = mkSzAddr(ty, guest_IP - delta);
sewardj9e6491a2005-07-02 19:24:10 +00009485
sewardjc716aea2006-01-17 01:48:46 +00009486 dres = disInstr_PPC_WRK ( put_IP, resteerOkFn, callback_opaque,
cerion5b2325f2005-12-23 00:55:09 +00009487 delta, archinfo );
sewardj9e6491a2005-07-02 19:24:10 +00009488
9489 return dres;
9490}
9491
9492
sewardjc808ef72005-08-18 11:50:43 +00009493/*------------------------------------------------------------*/
9494/*--- Unused stuff ---*/
9495/*------------------------------------------------------------*/
9496
9497///* A potentially more memcheck-friendly implementation of Clz32, with
9498// the boundary case Clz32(0) = 32, which is what ppc requires. */
9499//
9500//static IRExpr* /* :: Ity_I32 */ verbose_Clz32 ( IRTemp arg )
9501//{
9502// /* Welcome ... to SSA R Us. */
9503// IRTemp n1 = newTemp(Ity_I32);
9504// IRTemp n2 = newTemp(Ity_I32);
9505// IRTemp n3 = newTemp(Ity_I32);
9506// IRTemp n4 = newTemp(Ity_I32);
9507// IRTemp n5 = newTemp(Ity_I32);
9508// IRTemp n6 = newTemp(Ity_I32);
9509// IRTemp n7 = newTemp(Ity_I32);
9510// IRTemp n8 = newTemp(Ity_I32);
9511// IRTemp n9 = newTemp(Ity_I32);
9512// IRTemp n10 = newTemp(Ity_I32);
9513// IRTemp n11 = newTemp(Ity_I32);
9514// IRTemp n12 = newTemp(Ity_I32);
9515//
9516// /* First, propagate the most significant 1-bit into all lower
9517// positions in the word. */
9518// /* unsigned int clz ( unsigned int n )
9519// {
9520// n |= (n >> 1);
9521// n |= (n >> 2);
9522// n |= (n >> 4);
9523// n |= (n >> 8);
9524// n |= (n >> 16);
9525// return bitcount(~n);
9526// }
9527// */
9528// assign(n1, mkexpr(arg));
9529// assign(n2, binop(Iop_Or32, mkexpr(n1), binop(Iop_Shr32, mkexpr(n1), mkU8(1))));
9530// assign(n3, binop(Iop_Or32, mkexpr(n2), binop(Iop_Shr32, mkexpr(n2), mkU8(2))));
9531// assign(n4, binop(Iop_Or32, mkexpr(n3), binop(Iop_Shr32, mkexpr(n3), mkU8(4))));
9532// assign(n5, binop(Iop_Or32, mkexpr(n4), binop(Iop_Shr32, mkexpr(n4), mkU8(8))));
9533// assign(n6, binop(Iop_Or32, mkexpr(n5), binop(Iop_Shr32, mkexpr(n5), mkU8(16))));
9534// /* This gives a word of the form 0---01---1. Now invert it, giving
9535// a word of the form 1---10---0, then do a population-count idiom
9536// (to count the 1s, which is the number of leading zeroes, or 32
9537// if the original word was 0. */
9538// assign(n7, unop(Iop_Not32, mkexpr(n6)));
9539//
9540// /* unsigned int bitcount ( unsigned int n )
9541// {
9542// n = n - ((n >> 1) & 0x55555555);
9543// n = (n & 0x33333333) + ((n >> 2) & 0x33333333);
9544// n = (n + (n >> 4)) & 0x0F0F0F0F;
9545// n = n + (n >> 8);
9546// n = (n + (n >> 16)) & 0x3F;
9547// return n;
9548// }
9549// */
9550// assign(n8,
9551// binop(Iop_Sub32,
9552// mkexpr(n7),
9553// binop(Iop_And32,
9554// binop(Iop_Shr32, mkexpr(n7), mkU8(1)),
9555// mkU32(0x55555555))));
9556// assign(n9,
9557// binop(Iop_Add32,
9558// binop(Iop_And32, mkexpr(n8), mkU32(0x33333333)),
9559// binop(Iop_And32,
9560// binop(Iop_Shr32, mkexpr(n8), mkU8(2)),
9561// mkU32(0x33333333))));
9562// assign(n10,
9563// binop(Iop_And32,
9564// binop(Iop_Add32,
9565// mkexpr(n9),
9566// binop(Iop_Shr32, mkexpr(n9), mkU8(4))),
9567// mkU32(0x0F0F0F0F)));
9568// assign(n11,
9569// binop(Iop_Add32,
9570// mkexpr(n10),
9571// binop(Iop_Shr32, mkexpr(n10), mkU8(8))));
9572// assign(n12,
9573// binop(Iop_Add32,
9574// mkexpr(n11),
9575// binop(Iop_Shr32, mkexpr(n11), mkU8(16))));
9576// return
9577// binop(Iop_And32, mkexpr(n12), mkU32(0x3F));
9578//}
9579
cerion896a1372005-01-25 12:24:25 +00009580/*--------------------------------------------------------------------*/
ceriond0eae2d2005-12-23 11:43:01 +00009581/*--- end guest-ppc/toIR.c ---*/
cerion896a1372005-01-25 12:24:25 +00009582/*--------------------------------------------------------------------*/