blob: 92cc7d0ddec53d142b5531dabf365687654dbb41 [file] [log] [blame]
cerion896a1372005-01-25 12:24:25 +00001
2/*--------------------------------------------------------------------*/
3/*--- ---*/
sewardjcef7d3e2009-07-02 12:21:59 +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
sewardjcef7d3e2009-07-02 12:21:59 +000013 Copyright (C) 2004-2009 OpenWorks LLP. All rights reserved.
cerion896a1372005-01-25 12:24:25 +000014
sewardj7bd6ffe2005-08-03 16:07:36 +000015 This library is made available under a dual licensing scheme.
cerion896a1372005-01-25 12:24:25 +000016
sewardj7bd6ffe2005-08-03 16:07:36 +000017 If you link LibVEX against other code all of which is itself
18 licensed under the GNU General Public License, version 2 dated June
19 1991 ("GPL v2"), then you may use LibVEX under the terms of the GPL
20 v2, as appearing in the file LICENSE.GPL. If the file LICENSE.GPL
21 is missing, you can obtain a copy of the GPL v2 from the Free
22 Software Foundation Inc., 51 Franklin St, Fifth Floor, Boston, MA
23 02110-1301, USA.
24
25 For any other uses of LibVEX, you must first obtain a commercial
26 license from OpenWorks LLP. Please contact info@open-works.co.uk
27 for information about commercial licensing.
28
29 This software is provided by OpenWorks LLP "as is" and any express
30 or implied warranties, including, but not limited to, the implied
31 warranties of merchantability and fitness for a particular purpose
32 are disclaimed. In no event shall OpenWorks LLP be liable for any
33 direct, indirect, incidental, special, exemplary, or consequential
34 damages (including, but not limited to, procurement of substitute
35 goods or services; loss of use, data, or profits; or business
36 interruption) however caused and on any theory of liability,
37 whether in contract, strict liability, or tort (including
38 negligence or otherwise) arising in any way out of the use of this
39 software, even if advised of the possibility of such damage.
cerion896a1372005-01-25 12:24:25 +000040
41 Neither the names of the U.S. Department of Energy nor the
42 University of California nor the names of its contributors may be
43 used to endorse or promote products derived from this software
44 without prior written permission.
cerion896a1372005-01-25 12:24:25 +000045*/
46
cerionedf7fc52005-11-18 20:57:41 +000047/* TODO 18/Nov/05:
sewardjb51f0f42005-07-18 11:38:02 +000048
sewardjcf8986c2006-01-18 04:14:52 +000049 Spot rld... cases which are simply left/right shifts and emit
50 Shl64/Shr64 accordingly.
sewardje14bb9f2005-07-22 09:39:02 +000051
cerionedf7fc52005-11-18 20:57:41 +000052 Altivec
53 - datastream insns
54 - lvxl,stvxl: load/store with 'least recently used' hint
55 - vexptefp, vlogefp
sewardj87e651f2005-09-09 08:31:18 +000056
57 LIMITATIONS:
58
59 Various, including:
60
61 - Some invalid forms of lswi and lswx are accepted when they should
62 not be.
63
cerionedf7fc52005-11-18 20:57:41 +000064 - Floating Point:
65 - All exceptions disabled in FPSCR
66 - condition codes not set in FPSCR
cerion76de5cf2005-11-18 18:25:12 +000067
cerionedf7fc52005-11-18 20:57:41 +000068 - Altivec floating point:
69 - vmaddfp, vnmsubfp
70 Because we're using Java/IEEE mode (FPSCR[NJ]), rather than the
71 system default of Non-Java mode, we get some small errors
72 (lowest bit only).
73 This is because Non-Java mode brutally hacks denormalised results
74 to zero, whereas we keep maximum accuracy. However, using
75 Non-Java mode would give us more inaccuracy, as our intermediate
76 results would then be zeroed, too.
sewardjcf8986c2006-01-18 04:14:52 +000077
sewardjaca070a2006-10-17 00:28:22 +000078 - AbiHints for the stack red zone are only emitted for
sewardjcf8986c2006-01-18 04:14:52 +000079 unconditional calls and returns (bl, blr). They should also be
80 emitted for conditional calls and returns, but we don't have a
81 way to express that right now. Ah well.
sewardjb51f0f42005-07-18 11:38:02 +000082*/
83
sewardjce02aa72006-01-12 12:27:58 +000084/* "Special" instructions.
85
sewardj09e88d12006-01-27 16:05:49 +000086 This instruction decoder can decode four special instructions
sewardjce02aa72006-01-12 12:27:58 +000087 which mean nothing natively (are no-ops as far as regs/mem are
88 concerned) but have meaning for supporting Valgrind. A special
sewardj1eb7e6b2006-01-12 21:13:14 +000089 instruction is flagged by a 16-byte preamble:
90
91 32-bit mode: 54001800 54006800 5400E800 54009800
92 (rlwinm 0,0,3,0,0; rlwinm 0,0,13,0,0;
93 rlwinm 0,0,29,0,0; rlwinm 0,0,19,0,0)
94
95 64-bit mode: 78001800 78006800 7800E802 78009802
96 (rotldi 0,0,3; rotldi 0,0,13;
97 rotldi 0,0,61; rotldi 0,0,51)
98
99 Following that, one of the following 3 are allowed
sewardjce02aa72006-01-12 12:27:58 +0000100 (standard interpretation in parentheses):
101
102 7C210B78 (or 1,1,1) %R3 = client_request ( %R4 )
103 7C421378 (or 2,2,2) %R3 = guest_NRADDR
104 7C631B78 (or 3,3,3) branch-and-link-to-noredir %R11
sewardjaca070a2006-10-17 00:28:22 +0000105 7C842378 (or 4,4,4) %R3 = guest_NRADDR_GPR2
sewardjce02aa72006-01-12 12:27:58 +0000106
107 Any other bytes following the 16-byte preamble are illegal and
108 constitute a failure in instruction decoding. This all assumes
109 that the preamble will never occur except in specific code
110 fragments designed for Valgrind to catch.
111*/
112
sewardjb51f0f42005-07-18 11:38:02 +0000113
cerion5b2325f2005-12-23 00:55:09 +0000114/* Translates PPC32/64 code to IR. */
cerion896a1372005-01-25 12:24:25 +0000115
cerion645c9302005-01-31 10:09:59 +0000116/* References
ceriona982c052005-06-28 17:23:09 +0000117
118#define PPC32
cerion645c9302005-01-31 10:09:59 +0000119 "PowerPC Microprocessor Family:
ceriond953ebb2005-11-29 13:27:20 +0000120 The Programming Environments Manual for 32-Bit Microprocessors"
cerione9d361a2005-03-04 17:35:29 +0000121 02/21/2000
122 http://www-3.ibm.com/chips/techlib/techlib.nsf/techdocs/852569B20050FF778525699600719DF2
123
ceriond953ebb2005-11-29 13:27:20 +0000124#define PPC64
125 "PowerPC Microprocessor Family:
126 Programming Environments Manual for 64-Bit Microprocessors"
127 06/10/2003
128 http://www-3.ibm.com/chips/techlib/techlib.nsf/techdocs/F7E732FF811F783187256FDD004D3797
129
ceriona982c052005-06-28 17:23:09 +0000130#define AV
131 "PowerPC Microprocessor Family:
132 AltiVec(TM) Technology Programming Environments Manual"
133 07/10/2003
134 http://www-3.ibm.com/chips/techlib/techlib.nsf/techdocs/FBFA164F824370F987256D6A006F424D
cerion645c9302005-01-31 10:09:59 +0000135*/
136
cerion896a1372005-01-25 12:24:25 +0000137#include "libvex_basictypes.h"
138#include "libvex_ir.h"
139#include "libvex.h"
cerion1515db92005-01-25 17:21:23 +0000140#include "libvex_guest_ppc32.h"
ceriond953ebb2005-11-29 13:27:20 +0000141#include "libvex_guest_ppc64.h"
cerion896a1372005-01-25 12:24:25 +0000142
sewardjcef7d3e2009-07-02 12:21:59 +0000143#include "main_util.h"
144#include "main_globals.h"
145#include "guest_generic_bb_to_IR.h"
146#include "guest_ppc_defs.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
sewardjdd40fdf2006-12-24 02:20:24 +0000171/* The IRSB* into which we're generating code. */
172static IRSB* irsb;
cerion896a1372005-01-25 12:24:25 +0000173
sewardj5df65bb2005-11-29 14:47:04 +0000174/* Is our guest binary 32 or 64bit? Set at each call to
cerion5b2325f2005-12-23 00:55:09 +0000175 disInstr_PPC below. */
sewardj5df65bb2005-11-29 14:47:04 +0000176static Bool mode64 = False;
ceriond953ebb2005-11-29 13:27:20 +0000177
cerion4c4f5ef2006-01-02 14:41:50 +0000178// Given a pointer to a function as obtained by "& functionname" in C,
179// produce a pointer to the actual entry point for the function. For
180// most platforms it's the identity function. Unfortunately, on
sewardjaca070a2006-10-17 00:28:22 +0000181// ppc64-linux it isn't (sigh) and ditto for ppc32-aix5 and
182// ppc64-aix5.
sewardjdd40fdf2006-12-24 02:20:24 +0000183static void* fnptr_to_fnentry( VexAbiInfo* vbi, void* f )
cerion4c4f5ef2006-01-02 14:41:50 +0000184{
sewardjdd40fdf2006-12-24 02:20:24 +0000185 if (vbi->host_ppc_calls_use_fndescrs) {
sewardjaca070a2006-10-17 00:28:22 +0000186 /* f is a pointer to a 3-word function descriptor, of which the
187 first word is the entry address. */
188 /* note, this is correct even with cross-jitting, since this is
189 purely a host issue, not a guest one. */
190 HWord* fdescr = (HWord*)f;
191 return (void*)(fdescr[0]);
192 } else {
193 /* Simple; "& f" points directly at the code for f. */
194 return f;
195 }
cerion4c4f5ef2006-01-02 14:41:50 +0000196}
197
cerion896a1372005-01-25 12:24:25 +0000198
199/*------------------------------------------------------------*/
200/*--- Debugging output ---*/
201/*------------------------------------------------------------*/
202
203#define DIP(format, args...) \
204 if (vex_traceflags & VEX_TRACE_FE) \
205 vex_printf(format, ## args)
206
207#define DIS(buf, format, args...) \
208 if (vex_traceflags & VEX_TRACE_FE) \
209 vex_sprintf(buf, format, ## args)
210
211
cerion896a1372005-01-25 12:24:25 +0000212/*------------------------------------------------------------*/
ceriond953ebb2005-11-29 13:27:20 +0000213/*--- Offsets of various parts of the ppc32/64 guest state ---*/
cerion896a1372005-01-25 12:24:25 +0000214/*------------------------------------------------------------*/
215
cerion5b2325f2005-12-23 00:55:09 +0000216#define offsetofPPCGuestState(_x) \
217 (mode64 ? offsetof(VexGuestPPC64State, _x) : \
218 offsetof(VexGuestPPC32State, _x))
cerion91ad5362005-01-27 23:02:41 +0000219
sewardjaca070a2006-10-17 00:28:22 +0000220#define OFFB_CIA offsetofPPCGuestState(guest_CIA)
sewardje86310f2009-03-19 22:21:40 +0000221#define OFFB_IP_AT_SYSCALL offsetofPPCGuestState(guest_IP_AT_SYSCALL)
sewardjaca070a2006-10-17 00:28:22 +0000222#define OFFB_SPRG3_RO offsetofPPCGuestState(guest_SPRG3_RO)
223#define OFFB_LR offsetofPPCGuestState(guest_LR)
224#define OFFB_CTR offsetofPPCGuestState(guest_CTR)
225#define OFFB_XER_SO offsetofPPCGuestState(guest_XER_SO)
226#define OFFB_XER_OV offsetofPPCGuestState(guest_XER_OV)
227#define OFFB_XER_CA offsetofPPCGuestState(guest_XER_CA)
228#define OFFB_XER_BC offsetofPPCGuestState(guest_XER_BC)
229#define OFFB_FPROUND offsetofPPCGuestState(guest_FPROUND)
230#define OFFB_VRSAVE offsetofPPCGuestState(guest_VRSAVE)
231#define OFFB_VSCR offsetofPPCGuestState(guest_VSCR)
232#define OFFB_EMWARN offsetofPPCGuestState(guest_EMWARN)
233#define OFFB_TISTART offsetofPPCGuestState(guest_TISTART)
234#define OFFB_TILEN offsetofPPCGuestState(guest_TILEN)
sewardjaca070a2006-10-17 00:28:22 +0000235#define OFFB_NRADDR offsetofPPCGuestState(guest_NRADDR)
236#define OFFB_NRADDR_GPR2 offsetofPPCGuestState(guest_NRADDR_GPR2)
sewardj5ff11dd2006-01-20 14:19:25 +0000237
cerion91ad5362005-01-27 23:02:41 +0000238
cerion38674602005-02-08 02:19:25 +0000239/*------------------------------------------------------------*/
sewardjb51f0f42005-07-18 11:38:02 +0000240/*--- Extract instruction fields --- */
cerion38674602005-02-08 02:19:25 +0000241/*------------------------------------------------------------*/
cerione9d361a2005-03-04 17:35:29 +0000242
cerion76de5cf2005-11-18 18:25:12 +0000243/* Extract field from insn, given idx (zero = lsb) and field length */
244#define IFIELD( insn, idx, len ) ((insn >> idx) & ((1<<len)-1))
245
sewardjb51f0f42005-07-18 11:38:02 +0000246/* Extract primary opcode, instr[31:26] */
cerion76de5cf2005-11-18 18:25:12 +0000247static UChar ifieldOPC( UInt instr ) {
248 return toUChar( IFIELD( instr, 26, 6 ) );
sewardjb51f0f42005-07-18 11:38:02 +0000249}
cerione9d361a2005-03-04 17:35:29 +0000250
cerion76de5cf2005-11-18 18:25:12 +0000251/* Extract 10-bit secondary opcode, instr[10:1] */
sewardjb51f0f42005-07-18 11:38:02 +0000252static UInt ifieldOPClo10 ( UInt instr) {
cerion76de5cf2005-11-18 18:25:12 +0000253 return IFIELD( instr, 1, 10 );
254}
255
256/* Extract 9-bit secondary opcode, instr[9:1] */
257static UInt ifieldOPClo9 ( UInt instr) {
258 return IFIELD( instr, 1, 9 );
259}
260
261/* Extract 5-bit secondary opcode, instr[5:1] */
262static UInt ifieldOPClo5 ( UInt instr) {
263 return IFIELD( instr, 1, 5 );
sewardjb51f0f42005-07-18 11:38:02 +0000264}
cerione9d361a2005-03-04 17:35:29 +0000265
sewardjb51f0f42005-07-18 11:38:02 +0000266/* Extract RD (destination register) field, instr[25:21] */
cerion76de5cf2005-11-18 18:25:12 +0000267static UChar ifieldRegDS( UInt instr ) {
268 return toUChar( IFIELD( instr, 21, 5 ) );
sewardjb51f0f42005-07-18 11:38:02 +0000269}
cerion094d1392005-06-20 13:45:57 +0000270
cerion76de5cf2005-11-18 18:25:12 +0000271/* Extract RA (1st source register) field, instr[20:16] */
272static UChar ifieldRegA ( UInt instr ) {
273 return toUChar( IFIELD( instr, 16, 5 ) );
sewardjb51f0f42005-07-18 11:38:02 +0000274}
275
cerion76de5cf2005-11-18 18:25:12 +0000276/* Extract RB (2nd source register) field, instr[15:11] */
277static UChar ifieldRegB ( UInt instr ) {
278 return toUChar( IFIELD( instr, 11, 5 ) );
sewardjb51f0f42005-07-18 11:38:02 +0000279}
280
cerion76de5cf2005-11-18 18:25:12 +0000281/* Extract RC (3rd source register) field, instr[10:6] */
282static UChar ifieldRegC ( UInt instr ) {
283 return toUChar( IFIELD( instr, 6, 5 ) );
sewardjb51f0f42005-07-18 11:38:02 +0000284}
285
cerion76de5cf2005-11-18 18:25:12 +0000286/* Extract 2nd lowest bit, instr[1] */
287static UChar ifieldBIT10 ( UInt instr ) {
288 return toUChar( IFIELD( instr, 10, 1 ) );
289}
290
291/* Extract 2nd lowest bit, instr[1] */
292static UChar ifieldBIT1 ( UInt instr ) {
293 return toUChar( IFIELD( instr, 1, 1 ) );
294}
295
296/* Extract lowest bit, instr[0] */
297static UChar ifieldBIT0 ( UInt instr ) {
298 return toUChar( instr & 0x1 );
299}
300
301/* Extract unsigned bottom half, instr[15:0] */
302static UInt ifieldUIMM16 ( UInt instr ) {
303 return instr & 0xFFFF;
304}
305
ceriond953ebb2005-11-29 13:27:20 +0000306/* Extract unsigned bottom 26 bits, instr[25:0] */
307static UInt ifieldUIMM26 ( UInt instr ) {
308 return instr & 0x3FFFFFF;
cerion76de5cf2005-11-18 18:25:12 +0000309}
310
sewardjb51f0f42005-07-18 11:38:02 +0000311
cerionedf7fc52005-11-18 20:57:41 +0000312/*------------------------------------------------------------*/
ceriond953ebb2005-11-29 13:27:20 +0000313/*--- Guest-state identifiers ---*/
cerionedf7fc52005-11-18 20:57:41 +0000314/*------------------------------------------------------------*/
sewardje14bb9f2005-07-22 09:39:02 +0000315
cerione9d361a2005-03-04 17:35:29 +0000316typedef enum {
ceriond953ebb2005-11-29 13:27:20 +0000317 PPC_GST_CIA, // Current Instruction Address
318 PPC_GST_LR, // Link Register
319 PPC_GST_CTR, // Count Register
320 PPC_GST_XER, // Overflow, carry flags, byte count
321 PPC_GST_CR, // Condition Register
322 PPC_GST_FPSCR, // Floating Point Status/Control Register
323 PPC_GST_VRSAVE, // Vector Save/Restore Register
324 PPC_GST_VSCR, // Vector Status and Control Register
325 PPC_GST_EMWARN, // Emulation warnings
326 PPC_GST_TISTART,// For icbi: start of area to invalidate
327 PPC_GST_TILEN, // For icbi: length of area to invalidate
sewardje86310f2009-03-19 22:21:40 +0000328 PPC_GST_IP_AT_SYSCALL, // the CIA of the most recently executed SC insn
sewardjaca070a2006-10-17 00:28:22 +0000329 PPC_GST_SPRG3_RO, // SPRG3
ceriond953ebb2005-11-29 13:27:20 +0000330 PPC_GST_MAX
331} PPC_GST;
cerione9d361a2005-03-04 17:35:29 +0000332
cerionedf7fc52005-11-18 20:57:41 +0000333#define MASK_FPSCR_RN 0x3
334#define MASK_VSCR_VALID 0x00010001
sewardje14bb9f2005-07-22 09:39:02 +0000335
cerionedf7fc52005-11-18 20:57:41 +0000336
337/*------------------------------------------------------------*/
338/*--- FP Helpers ---*/
339/*------------------------------------------------------------*/
340
sewardj2ead5222005-11-23 03:53:45 +0000341/* Produce the 32-bit pattern corresponding to the supplied
342 float. */
343static UInt float_to_bits ( Float f )
344{
345 union { UInt i; Float f; } u;
346 vassert(4 == sizeof(UInt));
347 vassert(4 == sizeof(Float));
348 vassert(4 == sizeof(u));
349 u.f = f;
350 return u.i;
351}
352
cerion38674602005-02-08 02:19:25 +0000353
cerion38674602005-02-08 02:19:25 +0000354/*------------------------------------------------------------*/
355/*--- Misc Helpers ---*/
356/*------------------------------------------------------------*/
357
cerionf0de28c2005-12-13 20:21:11 +0000358/* Generate mask with 1's from 'begin' through 'end',
359 wrapping if begin > end.
360 begin->end works from right to left, 0=lsb
361*/
ceriond953ebb2005-11-29 13:27:20 +0000362static UInt MASK32( UInt begin, UInt end )
cerion38674602005-02-08 02:19:25 +0000363{
sewardj63327402006-01-25 03:26:27 +0000364 UInt m1, m2, mask;
ceriond953ebb2005-11-29 13:27:20 +0000365 vassert(begin < 32);
366 vassert(end < 32);
sewardj63327402006-01-25 03:26:27 +0000367 m1 = ((UInt)(-1)) << begin;
368 m2 = ((UInt)(-1)) << end << 1;
369 mask = m1 ^ m2;
cerionb85e8bb2005-02-16 08:54:33 +0000370 if (begin > end) mask = ~mask; // wrap mask
371 return mask;
cerion38674602005-02-08 02:19:25 +0000372}
373
cerion5b2325f2005-12-23 00:55:09 +0000374/* ditto for 64bit mask */
ceriond953ebb2005-11-29 13:27:20 +0000375static ULong MASK64( UInt begin, UInt end )
376{
sewardj63327402006-01-25 03:26:27 +0000377 ULong m1, m2, mask;
ceriond953ebb2005-11-29 13:27:20 +0000378 vassert(begin < 64);
379 vassert(end < 64);
sewardj63327402006-01-25 03:26:27 +0000380 m1 = ((ULong)(-1)) << begin;
381 m2 = ((ULong)(-1)) << end << 1;
382 mask = m1 ^ m2;
ceriond953ebb2005-11-29 13:27:20 +0000383 if (begin > end) mask = ~mask; // wrap mask
384 return mask;
385}
386
cerionf0de28c2005-12-13 20:21:11 +0000387static Addr64 nextInsnAddr( void )
388{
389 return guest_CIA_curr_instr + 4;
390}
ceriond953ebb2005-11-29 13:27:20 +0000391
cerion896a1372005-01-25 12:24:25 +0000392
cerion896a1372005-01-25 12:24:25 +0000393/*------------------------------------------------------------*/
394/*--- Helper bits and pieces for deconstructing the ---*/
ceriond953ebb2005-11-29 13:27:20 +0000395/*--- ppc32/64 insn stream. ---*/
cerion896a1372005-01-25 12:24:25 +0000396/*------------------------------------------------------------*/
397
sewardjdd40fdf2006-12-24 02:20:24 +0000398/* Add a statement to the list held by "irsb". */
cerion896a1372005-01-25 12:24:25 +0000399static void stmt ( IRStmt* st )
400{
sewardjdd40fdf2006-12-24 02:20:24 +0000401 addStmtToIRSB( irsb, st );
cerion896a1372005-01-25 12:24:25 +0000402}
403
cerion896a1372005-01-25 12:24:25 +0000404/* Generate a new temporary of the given type. */
405static IRTemp newTemp ( IRType ty )
406{
sewardj496a58d2005-03-20 18:44:44 +0000407 vassert(isPlausibleIRType(ty));
sewardjdd40fdf2006-12-24 02:20:24 +0000408 return newIRTemp( irsb->tyenv, ty );
cerion896a1372005-01-25 12:24:25 +0000409}
cerion896a1372005-01-25 12:24:25 +0000410
cerion32aad402005-09-10 12:02:24 +0000411/* Various simple conversions */
412
413static UChar extend_s_5to8 ( UChar x )
414{
415 return toUChar((((Int)x) << 27) >> 27);
416}
417
cerion92d9d872005-09-15 21:58:50 +0000418static UInt extend_s_8to32( UChar x )
419{
420 return (UInt)((((Int)x) << 24) >> 24);
421}
cerion91ad5362005-01-27 23:02:41 +0000422
cerion896a1372005-01-25 12:24:25 +0000423static UInt extend_s_16to32 ( UInt x )
424{
425 return (UInt)((((Int)x) << 16) >> 16);
426}
cerion896a1372005-01-25 12:24:25 +0000427
ceriond953ebb2005-11-29 13:27:20 +0000428static ULong extend_s_16to64 ( UInt x )
429{
430 return (ULong)((((Long)x) << 48) >> 48);
431}
432
433static ULong extend_s_26to64 ( UInt x )
434{
435 return (ULong)((((Long)x) << 38) >> 38);
436}
437
438static ULong extend_s_32to64 ( UInt x )
439{
440 return (ULong)((((Long)x) << 32) >> 32);
441}
442
sewardj684aa952005-01-30 12:52:14 +0000443/* Do a big-endian load of a 32-bit word, regardless of the endianness
444 of the underlying host. */
cerioncf004462005-01-31 15:24:55 +0000445static UInt getUIntBigendianly ( UChar* p )
sewardj684aa952005-01-30 12:52:14 +0000446{
cerioncf004462005-01-31 15:24:55 +0000447 UInt w = 0;
sewardj684aa952005-01-30 12:52:14 +0000448 w = (w << 8) | p[0];
449 w = (w << 8) | p[1];
450 w = (w << 8) | p[2];
451 w = (w << 8) | p[3];
452 return w;
453}
454
cerion896a1372005-01-25 12:24:25 +0000455
456/*------------------------------------------------------------*/
457/*--- Helpers for constructing IR. ---*/
458/*------------------------------------------------------------*/
459
cerion896a1372005-01-25 12:24:25 +0000460static void assign ( IRTemp dst, IRExpr* e )
461{
sewardjdd40fdf2006-12-24 02:20:24 +0000462 stmt( IRStmt_WrTmp(dst, e) );
cerion896a1372005-01-25 12:24:25 +0000463}
464
sewardje9d8a262009-07-01 08:06:34 +0000465/* This generates a normal (non store-conditional) store. */
cerionae694622005-01-28 17:52:47 +0000466static void storeBE ( IRExpr* addr, IRExpr* data )
cerion896a1372005-01-25 12:24:25 +0000467{
sewardje9d8a262009-07-01 08:06:34 +0000468 IRType tyA = typeOfIRExpr(irsb->tyenv, addr);
469 vassert(tyA == Ity_I32 || tyA == Ity_I64);
470 stmt( IRStmt_Store(Iend_BE, IRTemp_INVALID, addr, data) );
cerion896a1372005-01-25 12:24:25 +0000471}
472
473static IRExpr* unop ( IROp op, IRExpr* a )
474{
475 return IRExpr_Unop(op, a);
476}
477
478static IRExpr* binop ( IROp op, IRExpr* a1, IRExpr* a2 )
479{
480 return IRExpr_Binop(op, a1, a2);
481}
482
sewardjb183b852006-02-03 16:08:03 +0000483static IRExpr* triop ( IROp op, IRExpr* a1, IRExpr* a2, IRExpr* a3 )
484{
485 return IRExpr_Triop(op, a1, a2, a3);
486}
487
sewardj40c80262006-02-08 19:30:46 +0000488static IRExpr* qop ( IROp op, IRExpr* a1, IRExpr* a2,
489 IRExpr* a3, IRExpr* a4 )
490{
491 return IRExpr_Qop(op, a1, a2, a3, a4);
492}
493
cerion896a1372005-01-25 12:24:25 +0000494static IRExpr* mkexpr ( IRTemp tmp )
495{
sewardjdd40fdf2006-12-24 02:20:24 +0000496 return IRExpr_RdTmp(tmp);
cerion896a1372005-01-25 12:24:25 +0000497}
498
sewardj684c0372005-02-07 02:33:58 +0000499static IRExpr* mkU8 ( UChar i )
cerion896a1372005-01-25 12:24:25 +0000500{
cerion896a1372005-01-25 12:24:25 +0000501 return IRExpr_Const(IRConst_U8(i));
502}
cerion896a1372005-01-25 12:24:25 +0000503
cerion92d9d872005-09-15 21:58:50 +0000504static IRExpr* mkU16 ( UInt i )
505{
506 return IRExpr_Const(IRConst_U16(i));
507}
508
cerion896a1372005-01-25 12:24:25 +0000509static IRExpr* mkU32 ( UInt i )
510{
511 return IRExpr_Const(IRConst_U32(i));
512}
513
cerion4a49b032005-11-08 16:23:07 +0000514static IRExpr* mkU64 ( ULong i )
515{
516 return IRExpr_Const(IRConst_U64(i));
517}
518
sewardje9d8a262009-07-01 08:06:34 +0000519/* This generates a normal (non load-linked) load. */
cerionae694622005-01-28 17:52:47 +0000520static IRExpr* loadBE ( IRType ty, IRExpr* data )
cerion896a1372005-01-25 12:24:25 +0000521{
sewardje9d8a262009-07-01 08:06:34 +0000522 return IRExpr_Load(False, Iend_BE, ty, data);
523}
524
525/* And this, a linked load. */
526static IRExpr* loadlinkedBE ( IRType ty, IRExpr* data )
527{
528 if (mode64) {
529 vassert(ty == Ity_I32 || ty == Ity_I64);
530 } else {
531 vassert(ty == Ity_I32);
532 }
533 return IRExpr_Load(True, Iend_BE, ty, data);
cerion896a1372005-01-25 12:24:25 +0000534}
cerion896a1372005-01-25 12:24:25 +0000535
sewardj20ef5472005-07-21 14:48:31 +0000536static IRExpr* mkOR1 ( IRExpr* arg1, IRExpr* arg2 )
537{
sewardjdd40fdf2006-12-24 02:20:24 +0000538 vassert(typeOfIRExpr(irsb->tyenv, arg1) == Ity_I1);
539 vassert(typeOfIRExpr(irsb->tyenv, arg2) == Ity_I1);
cerion5b2325f2005-12-23 00:55:09 +0000540 return unop(Iop_32to1, binop(Iop_Or32, unop(Iop_1Uto32, arg1),
541 unop(Iop_1Uto32, arg2)));
sewardj20ef5472005-07-21 14:48:31 +0000542}
543
544static IRExpr* mkAND1 ( IRExpr* arg1, IRExpr* arg2 )
545{
sewardjdd40fdf2006-12-24 02:20:24 +0000546 vassert(typeOfIRExpr(irsb->tyenv, arg1) == Ity_I1);
547 vassert(typeOfIRExpr(irsb->tyenv, arg2) == Ity_I1);
cerion5b2325f2005-12-23 00:55:09 +0000548 return unop(Iop_32to1, binop(Iop_And32, unop(Iop_1Uto32, arg1),
549 unop(Iop_1Uto32, arg2)));
sewardj20ef5472005-07-21 14:48:31 +0000550}
sewardjb51f0f42005-07-18 11:38:02 +0000551
cerion4a49b032005-11-08 16:23:07 +0000552/* expand V128_8Ux16 to 2x V128_16Ux8's */
cerion5b2325f2005-12-23 00:55:09 +0000553static void expand8Ux16( IRExpr* vIn,
554 /*OUTs*/ IRTemp* vEvn, IRTemp* vOdd )
cerion4a49b032005-11-08 16:23:07 +0000555{
556 IRTemp ones8x16 = newTemp(Ity_V128);
557
sewardjdd40fdf2006-12-24 02:20:24 +0000558 vassert(typeOfIRExpr(irsb->tyenv, vIn) == Ity_V128);
cerion4a49b032005-11-08 16:23:07 +0000559 vassert(vEvn && *vEvn == IRTemp_INVALID);
560 vassert(vOdd && *vOdd == IRTemp_INVALID);
561 *vEvn = newTemp(Ity_V128);
562 *vOdd = newTemp(Ity_V128);
563
564 assign( ones8x16, unop(Iop_Dup8x16, mkU8(0x1)) );
cerion24d06f12005-11-09 21:34:20 +0000565 assign( *vOdd, binop(Iop_MullEven8Ux16, mkexpr(ones8x16), vIn) );
566 assign( *vEvn, binop(Iop_MullEven8Ux16, mkexpr(ones8x16),
567 binop(Iop_ShrV128, vIn, mkU8(8))) );
cerion4a49b032005-11-08 16:23:07 +0000568}
569
570/* expand V128_8Sx16 to 2x V128_16Sx8's */
cerion5b2325f2005-12-23 00:55:09 +0000571static void expand8Sx16( IRExpr* vIn,
572 /*OUTs*/ IRTemp* vEvn, IRTemp* vOdd )
cerion4a49b032005-11-08 16:23:07 +0000573{
574 IRTemp ones8x16 = newTemp(Ity_V128);
575
sewardjdd40fdf2006-12-24 02:20:24 +0000576 vassert(typeOfIRExpr(irsb->tyenv, vIn) == Ity_V128);
cerion4a49b032005-11-08 16:23:07 +0000577 vassert(vEvn && *vEvn == IRTemp_INVALID);
578 vassert(vOdd && *vOdd == IRTemp_INVALID);
579 *vEvn = newTemp(Ity_V128);
580 *vOdd = newTemp(Ity_V128);
581
582 assign( ones8x16, unop(Iop_Dup8x16, mkU8(0x1)) );
cerion24d06f12005-11-09 21:34:20 +0000583 assign( *vOdd, binop(Iop_MullEven8Sx16, mkexpr(ones8x16), vIn) );
584 assign( *vEvn, binop(Iop_MullEven8Sx16, mkexpr(ones8x16),
585 binop(Iop_ShrV128, vIn, mkU8(8))) );
cerion4a49b032005-11-08 16:23:07 +0000586}
587
588/* expand V128_16Uto8 to 2x V128_32Ux4's */
cerion5b2325f2005-12-23 00:55:09 +0000589static void expand16Ux8( IRExpr* vIn,
590 /*OUTs*/ IRTemp* vEvn, IRTemp* vOdd )
cerion4a49b032005-11-08 16:23:07 +0000591{
592 IRTemp ones16x8 = newTemp(Ity_V128);
593
sewardjdd40fdf2006-12-24 02:20:24 +0000594 vassert(typeOfIRExpr(irsb->tyenv, vIn) == Ity_V128);
cerion4a49b032005-11-08 16:23:07 +0000595 vassert(vEvn && *vEvn == IRTemp_INVALID);
596 vassert(vOdd && *vOdd == IRTemp_INVALID);
597 *vEvn = newTemp(Ity_V128);
598 *vOdd = newTemp(Ity_V128);
599
600 assign( ones16x8, unop(Iop_Dup16x8, mkU16(0x1)) );
cerion24d06f12005-11-09 21:34:20 +0000601 assign( *vOdd, binop(Iop_MullEven16Ux8, mkexpr(ones16x8), vIn) );
602 assign( *vEvn, binop(Iop_MullEven16Ux8, mkexpr(ones16x8),
603 binop(Iop_ShrV128, vIn, mkU8(16))) );
cerion4a49b032005-11-08 16:23:07 +0000604}
605
606/* expand V128_16Sto8 to 2x V128_32Sx4's */
cerion5b2325f2005-12-23 00:55:09 +0000607static void expand16Sx8( IRExpr* vIn,
608 /*OUTs*/ IRTemp* vEvn, IRTemp* vOdd )
cerion4a49b032005-11-08 16:23:07 +0000609{
610 IRTemp ones16x8 = newTemp(Ity_V128);
611
sewardjdd40fdf2006-12-24 02:20:24 +0000612 vassert(typeOfIRExpr(irsb->tyenv, vIn) == Ity_V128);
cerion4a49b032005-11-08 16:23:07 +0000613 vassert(vEvn && *vEvn == IRTemp_INVALID);
614 vassert(vOdd && *vOdd == IRTemp_INVALID);
615 *vEvn = newTemp(Ity_V128);
616 *vOdd = newTemp(Ity_V128);
617
618 assign( ones16x8, unop(Iop_Dup16x8, mkU16(0x1)) );
cerion24d06f12005-11-09 21:34:20 +0000619 assign( *vOdd, binop(Iop_MullEven16Sx8, mkexpr(ones16x8), vIn) );
620 assign( *vEvn, binop(Iop_MullEven16Sx8, mkexpr(ones16x8),
621 binop(Iop_ShrV128, vIn, mkU8(16))) );
cerion4a49b032005-11-08 16:23:07 +0000622}
623
624/* break V128 to 4xI32's, then sign-extend to I64's */
625static void breakV128to4x64S( IRExpr* t128,
626 /*OUTs*/
627 IRTemp* t3, IRTemp* t2,
628 IRTemp* t1, IRTemp* t0 )
629{
630 IRTemp hi64 = newTemp(Ity_I64);
631 IRTemp lo64 = newTemp(Ity_I64);
632
sewardjdd40fdf2006-12-24 02:20:24 +0000633 vassert(typeOfIRExpr(irsb->tyenv, t128) == Ity_V128);
cerion4a49b032005-11-08 16:23:07 +0000634 vassert(t0 && *t0 == IRTemp_INVALID);
635 vassert(t1 && *t1 == IRTemp_INVALID);
636 vassert(t2 && *t2 == IRTemp_INVALID);
637 vassert(t3 && *t3 == IRTemp_INVALID);
638 *t0 = newTemp(Ity_I64);
639 *t1 = newTemp(Ity_I64);
640 *t2 = newTemp(Ity_I64);
641 *t3 = newTemp(Ity_I64);
642
643 assign( hi64, unop(Iop_V128HIto64, t128) );
644 assign( lo64, unop(Iop_V128to64, t128) );
645 assign( *t3, unop(Iop_32Sto64, unop(Iop_64HIto32, mkexpr(hi64))) );
646 assign( *t2, unop(Iop_32Sto64, unop(Iop_64to32, mkexpr(hi64))) );
647 assign( *t1, unop(Iop_32Sto64, unop(Iop_64HIto32, mkexpr(lo64))) );
648 assign( *t0, unop(Iop_32Sto64, unop(Iop_64to32, mkexpr(lo64))) );
649}
650
651/* break V128 to 4xI32's, then zero-extend to I64's */
652static void breakV128to4x64U ( IRExpr* t128,
653 /*OUTs*/
654 IRTemp* t3, IRTemp* t2,
655 IRTemp* t1, IRTemp* t0 )
656{
657 IRTemp hi64 = newTemp(Ity_I64);
658 IRTemp lo64 = newTemp(Ity_I64);
659
sewardjdd40fdf2006-12-24 02:20:24 +0000660 vassert(typeOfIRExpr(irsb->tyenv, t128) == Ity_V128);
cerion4a49b032005-11-08 16:23:07 +0000661 vassert(t0 && *t0 == IRTemp_INVALID);
662 vassert(t1 && *t1 == IRTemp_INVALID);
663 vassert(t2 && *t2 == IRTemp_INVALID);
664 vassert(t3 && *t3 == IRTemp_INVALID);
665 *t0 = newTemp(Ity_I64);
666 *t1 = newTemp(Ity_I64);
667 *t2 = newTemp(Ity_I64);
668 *t3 = newTemp(Ity_I64);
669
670 assign( hi64, unop(Iop_V128HIto64, t128) );
671 assign( lo64, unop(Iop_V128to64, t128) );
672 assign( *t3, unop(Iop_32Uto64, unop(Iop_64HIto32, mkexpr(hi64))) );
673 assign( *t2, unop(Iop_32Uto64, unop(Iop_64to32, mkexpr(hi64))) );
674 assign( *t1, unop(Iop_32Uto64, unop(Iop_64HIto32, mkexpr(lo64))) );
675 assign( *t0, unop(Iop_32Uto64, unop(Iop_64to32, mkexpr(lo64))) );
676}
677
678/* Signed saturating narrow 64S to 32 */
679static IRExpr* mkQNarrow64Sto32 ( IRExpr* t64 )
680{
681 IRTemp hi32 = newTemp(Ity_I32);
682 IRTemp lo32 = newTemp(Ity_I32);
683
sewardjdd40fdf2006-12-24 02:20:24 +0000684 vassert(typeOfIRExpr(irsb->tyenv, t64) == Ity_I64);
cerion4a49b032005-11-08 16:23:07 +0000685
686 assign( hi32, unop(Iop_64HIto32, t64));
687 assign( lo32, unop(Iop_64to32, t64));
688
689 return IRExpr_Mux0X(
690 /* if (hi32 == (lo32 >>s 31)) */
691 unop(Iop_1Uto8,
692 binop(Iop_CmpEQ32, mkexpr(hi32),
693 binop( Iop_Sar32, mkexpr(lo32), mkU8(31)))),
694 /* else: sign dep saturate: 1->0x80000000, 0->0x7FFFFFFF */
695 binop(Iop_Add32, mkU32(0x7FFFFFFF),
696 binop(Iop_Shr32, mkexpr(hi32), mkU8(31))),
697 /* then: within signed-32 range: lo half good enough */
698 mkexpr(lo32) );
699}
700
701/* Unsigned saturating narrow 64S to 32 */
702static IRExpr* mkQNarrow64Uto32 ( IRExpr* t64 )
703{
704 IRTemp hi32 = newTemp(Ity_I32);
705 IRTemp lo32 = newTemp(Ity_I32);
706
sewardjdd40fdf2006-12-24 02:20:24 +0000707 vassert(typeOfIRExpr(irsb->tyenv, t64) == Ity_I64);
cerion4a49b032005-11-08 16:23:07 +0000708
709 assign( hi32, unop(Iop_64HIto32, t64));
710 assign( lo32, unop(Iop_64to32, t64));
711
712 return IRExpr_Mux0X(
713 /* if (top 32 bits of t64 are 0) */
714 unop(Iop_1Uto8, binop(Iop_CmpEQ32, mkexpr(hi32), mkU32(0))),
715 /* else: positive saturate -> 0xFFFFFFFF */
716 mkU32(0xFFFFFFFF),
717 /* then: within unsigned-32 range: lo half good enough */
718 mkexpr(lo32) );
719}
720
721/* Signed saturate narrow 64->32, combining to V128 */
722static IRExpr* mkV128from4x64S ( IRExpr* t3, IRExpr* t2,
723 IRExpr* t1, IRExpr* t0 )
724{
sewardjdd40fdf2006-12-24 02:20:24 +0000725 vassert(typeOfIRExpr(irsb->tyenv, t3) == Ity_I64);
726 vassert(typeOfIRExpr(irsb->tyenv, t2) == Ity_I64);
727 vassert(typeOfIRExpr(irsb->tyenv, t1) == Ity_I64);
728 vassert(typeOfIRExpr(irsb->tyenv, t0) == Ity_I64);
cerion4a49b032005-11-08 16:23:07 +0000729 return binop(Iop_64HLtoV128,
730 binop(Iop_32HLto64,
731 mkQNarrow64Sto32( t3 ),
732 mkQNarrow64Sto32( t2 )),
733 binop(Iop_32HLto64,
734 mkQNarrow64Sto32( t1 ),
735 mkQNarrow64Sto32( t0 )));
736}
737
738/* Unsigned saturate narrow 64->32, combining to V128 */
739static IRExpr* mkV128from4x64U ( IRExpr* t3, IRExpr* t2,
740 IRExpr* t1, IRExpr* t0 )
741{
sewardjdd40fdf2006-12-24 02:20:24 +0000742 vassert(typeOfIRExpr(irsb->tyenv, t3) == Ity_I64);
743 vassert(typeOfIRExpr(irsb->tyenv, t2) == Ity_I64);
744 vassert(typeOfIRExpr(irsb->tyenv, t1) == Ity_I64);
745 vassert(typeOfIRExpr(irsb->tyenv, t0) == Ity_I64);
cerion4a49b032005-11-08 16:23:07 +0000746 return binop(Iop_64HLtoV128,
747 binop(Iop_32HLto64,
748 mkQNarrow64Uto32( t3 ),
749 mkQNarrow64Uto32( t2 )),
750 binop(Iop_32HLto64,
751 mkQNarrow64Uto32( t1 ),
752 mkQNarrow64Uto32( t0 )));
753}
754
cerion24d06f12005-11-09 21:34:20 +0000755/* Simulate irops Iop_MullOdd*, since we don't have them */
756#define MK_Iop_MullOdd8Ux16( expr_vA, expr_vB ) \
757 binop(Iop_MullEven8Ux16, \
758 binop(Iop_ShrV128, expr_vA, mkU8(8)), \
759 binop(Iop_ShrV128, expr_vB, mkU8(8)))
760
761#define MK_Iop_MullOdd8Sx16( expr_vA, expr_vB ) \
762 binop(Iop_MullEven8Sx16, \
763 binop(Iop_ShrV128, expr_vA, mkU8(8)), \
764 binop(Iop_ShrV128, expr_vB, mkU8(8)))
765
766#define MK_Iop_MullOdd16Ux8( expr_vA, expr_vB ) \
767 binop(Iop_MullEven16Ux8, \
768 binop(Iop_ShrV128, expr_vA, mkU8(16)), \
769 binop(Iop_ShrV128, expr_vB, mkU8(16)))
770
771#define MK_Iop_MullOdd16Sx8( expr_vA, expr_vB ) \
772 binop(Iop_MullEven16Sx8, \
773 binop(Iop_ShrV128, expr_vA, mkU8(16)), \
774 binop(Iop_ShrV128, expr_vB, mkU8(16)))
775
cerion59b2c312005-12-17 11:28:53 +0000776static IRExpr* /* :: Ity_I64 */ mk64lo32Sto64 ( IRExpr* src )
ceriond953ebb2005-11-29 13:27:20 +0000777{
sewardjdd40fdf2006-12-24 02:20:24 +0000778 vassert(typeOfIRExpr(irsb->tyenv, src) == Ity_I64);
ceriond953ebb2005-11-29 13:27:20 +0000779 return unop(Iop_32Sto64, unop(Iop_64to32, src));
780}
781
cerion59b2c312005-12-17 11:28:53 +0000782static IRExpr* /* :: Ity_I64 */ mk64lo32Uto64 ( IRExpr* src )
cerionbb01b7c2005-12-16 13:40:18 +0000783{
sewardjdd40fdf2006-12-24 02:20:24 +0000784 vassert(typeOfIRExpr(irsb->tyenv, src) == Ity_I64);
cerionbb01b7c2005-12-16 13:40:18 +0000785 return unop(Iop_32Uto64, unop(Iop_64to32, src));
786}
787
cerion2831b002005-11-30 19:55:22 +0000788static IROp mkSzOp ( IRType ty, IROp op8 )
ceriond953ebb2005-11-29 13:27:20 +0000789{
790 Int adj;
791 vassert(ty == Ity_I8 || ty == Ity_I16 ||
792 ty == Ity_I32 || ty == Ity_I64);
793 vassert(op8 == Iop_Add8 || op8 == Iop_Sub8 || op8 == Iop_Mul8 ||
794 op8 == Iop_Or8 || op8 == Iop_And8 || op8 == Iop_Xor8 ||
795 op8 == Iop_Shl8 || op8 == Iop_Shr8 || op8 == Iop_Sar8 ||
796 op8 == Iop_CmpEQ8 || op8 == Iop_CmpNE8 ||
sewardjeb17e492007-08-25 23:07:44 +0000797 op8 == Iop_Not8 );
ceriond953ebb2005-11-29 13:27:20 +0000798 adj = ty==Ity_I8 ? 0 : (ty==Ity_I16 ? 1 : (ty==Ity_I32 ? 2 : 3));
799 return adj + op8;
800}
801
cerion5b2325f2005-12-23 00:55:09 +0000802/* Make sure we get valid 32 and 64bit addresses */
cerion2831b002005-11-30 19:55:22 +0000803static Addr64 mkSzAddr ( IRType ty, Addr64 addr )
ceriond953ebb2005-11-29 13:27:20 +0000804{
805 vassert(ty == Ity_I32 || ty == Ity_I64);
806 return ( ty == Ity_I64 ?
807 (Addr64)addr :
808 (Addr64)extend_s_32to64( toUInt(addr) ) );
809}
810
811/* sz, ULong -> IRExpr */
cerion2831b002005-11-30 19:55:22 +0000812static IRExpr* mkSzImm ( IRType ty, ULong imm64 )
ceriond953ebb2005-11-29 13:27:20 +0000813{
814 vassert(ty == Ity_I32 || ty == Ity_I64);
815 return ty == Ity_I64 ? mkU64(imm64) : mkU32((UInt)imm64);
816}
817
818/* sz, ULong -> IRConst */
cerion2831b002005-11-30 19:55:22 +0000819static IRConst* mkSzConst ( IRType ty, ULong imm64 )
ceriond953ebb2005-11-29 13:27:20 +0000820{
821 vassert(ty == Ity_I32 || ty == Ity_I64);
822 return ( ty == Ity_I64 ?
823 IRConst_U64(imm64) :
824 IRConst_U32((UInt)imm64) );
825}
826
827/* Sign extend imm16 -> IRExpr* */
cerion2831b002005-11-30 19:55:22 +0000828static IRExpr* mkSzExtendS16 ( IRType ty, UInt imm16 )
ceriond953ebb2005-11-29 13:27:20 +0000829{
830 vassert(ty == Ity_I32 || ty == Ity_I64);
831 return ( ty == Ity_I64 ?
832 mkU64(extend_s_16to64(imm16)) :
833 mkU32(extend_s_16to32(imm16)) );
834}
835
836/* Sign extend imm32 -> IRExpr* */
cerion2831b002005-11-30 19:55:22 +0000837static IRExpr* mkSzExtendS32 ( IRType ty, UInt imm32 )
ceriond953ebb2005-11-29 13:27:20 +0000838{
839 vassert(ty == Ity_I32 || ty == Ity_I64);
840 return ( ty == Ity_I64 ?
841 mkU64(extend_s_32to64(imm32)) :
842 mkU32(imm32) );
843}
844
845/* IR narrows I32/I64 -> I8/I16/I32 */
sewardje9d8a262009-07-01 08:06:34 +0000846static IRExpr* mkNarrowTo8 ( IRType ty, IRExpr* src )
ceriond953ebb2005-11-29 13:27:20 +0000847{
848 vassert(ty == Ity_I32 || ty == Ity_I64);
849 return ty == Ity_I64 ? unop(Iop_64to8, src) : unop(Iop_32to8, src);
850}
851
sewardje9d8a262009-07-01 08:06:34 +0000852static IRExpr* mkNarrowTo16 ( IRType ty, IRExpr* src )
ceriond953ebb2005-11-29 13:27:20 +0000853{
854 vassert(ty == Ity_I32 || ty == Ity_I64);
855 return ty == Ity_I64 ? unop(Iop_64to16, src) : unop(Iop_32to16, src);
856}
857
sewardje9d8a262009-07-01 08:06:34 +0000858static IRExpr* mkNarrowTo32 ( IRType ty, IRExpr* src )
ceriond953ebb2005-11-29 13:27:20 +0000859{
860 vassert(ty == Ity_I32 || ty == Ity_I64);
861 return ty == Ity_I64 ? unop(Iop_64to32, src) : src;
862}
863
864/* Signed/Unsigned IR widens I8/I16/I32 -> I32/I64 */
sewardje9d8a262009-07-01 08:06:34 +0000865static IRExpr* mkWidenFrom8 ( IRType ty, IRExpr* src, Bool sined )
ceriond953ebb2005-11-29 13:27:20 +0000866{
ceriond953ebb2005-11-29 13:27:20 +0000867 IROp op;
sewardj63327402006-01-25 03:26:27 +0000868 vassert(ty == Ity_I32 || ty == Ity_I64);
ceriond953ebb2005-11-29 13:27:20 +0000869 if (sined) op = (ty==Ity_I32) ? Iop_8Sto32 : Iop_8Sto64;
870 else op = (ty==Ity_I32) ? Iop_8Uto32 : Iop_8Uto64;
871 return unop(op, src);
872}
873
sewardje9d8a262009-07-01 08:06:34 +0000874static IRExpr* mkWidenFrom16 ( IRType ty, IRExpr* src, Bool sined )
ceriond953ebb2005-11-29 13:27:20 +0000875{
ceriond953ebb2005-11-29 13:27:20 +0000876 IROp op;
sewardj63327402006-01-25 03:26:27 +0000877 vassert(ty == Ity_I32 || ty == Ity_I64);
ceriond953ebb2005-11-29 13:27:20 +0000878 if (sined) op = (ty==Ity_I32) ? Iop_16Sto32 : Iop_16Sto64;
879 else op = (ty==Ity_I32) ? Iop_16Uto32 : Iop_16Uto64;
880 return unop(op, src);
881}
882
sewardje9d8a262009-07-01 08:06:34 +0000883static IRExpr* mkWidenFrom32 ( IRType ty, IRExpr* src, Bool sined )
ceriond953ebb2005-11-29 13:27:20 +0000884{
885 vassert(ty == Ity_I32 || ty == Ity_I64);
886 if (ty == Ity_I32)
887 return src;
888 return (sined) ? unop(Iop_32Sto64, src) : unop(Iop_32Uto64, src);
889}
cerion24d06f12005-11-09 21:34:20 +0000890
cerion4a49b032005-11-08 16:23:07 +0000891
sewardjb51f0f42005-07-18 11:38:02 +0000892static Int integerGuestRegOffset ( UInt archreg )
cerion45b70ff2005-01-31 17:03:25 +0000893{
sewardjb51f0f42005-07-18 11:38:02 +0000894 vassert(archreg < 32);
895
896 // jrs: probably not necessary; only matters if we reference sub-parts
cerion5b2325f2005-12-23 00:55:09 +0000897 // of the ppc registers, but that isn't the case
sewardjb51f0f42005-07-18 11:38:02 +0000898 // later: this might affect Altivec though?
899 vassert(host_is_bigendian);
900
cerion5b2325f2005-12-23 00:55:09 +0000901 switch (archreg) {
902 case 0: return offsetofPPCGuestState(guest_GPR0);
903 case 1: return offsetofPPCGuestState(guest_GPR1);
904 case 2: return offsetofPPCGuestState(guest_GPR2);
905 case 3: return offsetofPPCGuestState(guest_GPR3);
906 case 4: return offsetofPPCGuestState(guest_GPR4);
907 case 5: return offsetofPPCGuestState(guest_GPR5);
908 case 6: return offsetofPPCGuestState(guest_GPR6);
909 case 7: return offsetofPPCGuestState(guest_GPR7);
910 case 8: return offsetofPPCGuestState(guest_GPR8);
911 case 9: return offsetofPPCGuestState(guest_GPR9);
912 case 10: return offsetofPPCGuestState(guest_GPR10);
913 case 11: return offsetofPPCGuestState(guest_GPR11);
914 case 12: return offsetofPPCGuestState(guest_GPR12);
915 case 13: return offsetofPPCGuestState(guest_GPR13);
916 case 14: return offsetofPPCGuestState(guest_GPR14);
917 case 15: return offsetofPPCGuestState(guest_GPR15);
918 case 16: return offsetofPPCGuestState(guest_GPR16);
919 case 17: return offsetofPPCGuestState(guest_GPR17);
920 case 18: return offsetofPPCGuestState(guest_GPR18);
921 case 19: return offsetofPPCGuestState(guest_GPR19);
922 case 20: return offsetofPPCGuestState(guest_GPR20);
923 case 21: return offsetofPPCGuestState(guest_GPR21);
924 case 22: return offsetofPPCGuestState(guest_GPR22);
925 case 23: return offsetofPPCGuestState(guest_GPR23);
926 case 24: return offsetofPPCGuestState(guest_GPR24);
927 case 25: return offsetofPPCGuestState(guest_GPR25);
928 case 26: return offsetofPPCGuestState(guest_GPR26);
929 case 27: return offsetofPPCGuestState(guest_GPR27);
930 case 28: return offsetofPPCGuestState(guest_GPR28);
931 case 29: return offsetofPPCGuestState(guest_GPR29);
932 case 30: return offsetofPPCGuestState(guest_GPR30);
933 case 31: return offsetofPPCGuestState(guest_GPR31);
934 default: break;
sewardjb51f0f42005-07-18 11:38:02 +0000935 }
cerion5b2325f2005-12-23 00:55:09 +0000936 vpanic("integerGuestRegOffset(ppc,be)"); /*notreached*/
sewardjb51f0f42005-07-18 11:38:02 +0000937}
938
939static IRExpr* getIReg ( UInt archreg )
940{
ceriond953ebb2005-11-29 13:27:20 +0000941 IRType ty = mode64 ? Ity_I64 : Ity_I32;
sewardjb51f0f42005-07-18 11:38:02 +0000942 vassert(archreg < 32);
ceriond953ebb2005-11-29 13:27:20 +0000943 return IRExpr_Get( integerGuestRegOffset(archreg), ty );
sewardjb51f0f42005-07-18 11:38:02 +0000944}
945
946/* Ditto, but write to a reg instead. */
947static void putIReg ( UInt archreg, IRExpr* e )
948{
ceriond953ebb2005-11-29 13:27:20 +0000949 IRType ty = mode64 ? Ity_I64 : Ity_I32;
sewardjb51f0f42005-07-18 11:38:02 +0000950 vassert(archreg < 32);
sewardjdd40fdf2006-12-24 02:20:24 +0000951 vassert(typeOfIRExpr(irsb->tyenv, e) == ty );
sewardjb51f0f42005-07-18 11:38:02 +0000952 stmt( IRStmt_Put(integerGuestRegOffset(archreg), e) );
953}
954
955
956static Int floatGuestRegOffset ( UInt archreg )
957{
958 vassert(archreg < 32);
959
cerion5b2325f2005-12-23 00:55:09 +0000960 switch (archreg) {
961 case 0: return offsetofPPCGuestState(guest_FPR0);
962 case 1: return offsetofPPCGuestState(guest_FPR1);
963 case 2: return offsetofPPCGuestState(guest_FPR2);
964 case 3: return offsetofPPCGuestState(guest_FPR3);
965 case 4: return offsetofPPCGuestState(guest_FPR4);
966 case 5: return offsetofPPCGuestState(guest_FPR5);
967 case 6: return offsetofPPCGuestState(guest_FPR6);
968 case 7: return offsetofPPCGuestState(guest_FPR7);
969 case 8: return offsetofPPCGuestState(guest_FPR8);
970 case 9: return offsetofPPCGuestState(guest_FPR9);
971 case 10: return offsetofPPCGuestState(guest_FPR10);
972 case 11: return offsetofPPCGuestState(guest_FPR11);
973 case 12: return offsetofPPCGuestState(guest_FPR12);
974 case 13: return offsetofPPCGuestState(guest_FPR13);
975 case 14: return offsetofPPCGuestState(guest_FPR14);
976 case 15: return offsetofPPCGuestState(guest_FPR15);
977 case 16: return offsetofPPCGuestState(guest_FPR16);
978 case 17: return offsetofPPCGuestState(guest_FPR17);
979 case 18: return offsetofPPCGuestState(guest_FPR18);
980 case 19: return offsetofPPCGuestState(guest_FPR19);
981 case 20: return offsetofPPCGuestState(guest_FPR20);
982 case 21: return offsetofPPCGuestState(guest_FPR21);
983 case 22: return offsetofPPCGuestState(guest_FPR22);
984 case 23: return offsetofPPCGuestState(guest_FPR23);
985 case 24: return offsetofPPCGuestState(guest_FPR24);
986 case 25: return offsetofPPCGuestState(guest_FPR25);
987 case 26: return offsetofPPCGuestState(guest_FPR26);
988 case 27: return offsetofPPCGuestState(guest_FPR27);
989 case 28: return offsetofPPCGuestState(guest_FPR28);
990 case 29: return offsetofPPCGuestState(guest_FPR29);
991 case 30: return offsetofPPCGuestState(guest_FPR30);
992 case 31: return offsetofPPCGuestState(guest_FPR31);
993 default: break;
sewardjb51f0f42005-07-18 11:38:02 +0000994 }
cerion5b2325f2005-12-23 00:55:09 +0000995 vpanic("floatGuestRegOffset(ppc)"); /*notreached*/
sewardjb51f0f42005-07-18 11:38:02 +0000996}
997
998static IRExpr* getFReg ( UInt archreg )
999{
1000 vassert(archreg < 32);
1001 return IRExpr_Get( floatGuestRegOffset(archreg), Ity_F64 );
1002}
1003
1004/* Ditto, but write to a reg instead. */
1005static void putFReg ( UInt archreg, IRExpr* e )
1006{
1007 vassert(archreg < 32);
sewardjdd40fdf2006-12-24 02:20:24 +00001008 vassert(typeOfIRExpr(irsb->tyenv, e) == Ity_F64);
sewardjb51f0f42005-07-18 11:38:02 +00001009 stmt( IRStmt_Put(floatGuestRegOffset(archreg), e) );
1010}
1011
1012
1013static Int vectorGuestRegOffset ( UInt archreg )
1014{
1015 vassert(archreg < 32);
1016
cerion5b2325f2005-12-23 00:55:09 +00001017 switch (archreg) {
1018 case 0: return offsetofPPCGuestState(guest_VR0);
1019 case 1: return offsetofPPCGuestState(guest_VR1);
1020 case 2: return offsetofPPCGuestState(guest_VR2);
1021 case 3: return offsetofPPCGuestState(guest_VR3);
1022 case 4: return offsetofPPCGuestState(guest_VR4);
1023 case 5: return offsetofPPCGuestState(guest_VR5);
1024 case 6: return offsetofPPCGuestState(guest_VR6);
1025 case 7: return offsetofPPCGuestState(guest_VR7);
1026 case 8: return offsetofPPCGuestState(guest_VR8);
1027 case 9: return offsetofPPCGuestState(guest_VR9);
1028 case 10: return offsetofPPCGuestState(guest_VR10);
1029 case 11: return offsetofPPCGuestState(guest_VR11);
1030 case 12: return offsetofPPCGuestState(guest_VR12);
1031 case 13: return offsetofPPCGuestState(guest_VR13);
1032 case 14: return offsetofPPCGuestState(guest_VR14);
1033 case 15: return offsetofPPCGuestState(guest_VR15);
1034 case 16: return offsetofPPCGuestState(guest_VR16);
1035 case 17: return offsetofPPCGuestState(guest_VR17);
1036 case 18: return offsetofPPCGuestState(guest_VR18);
1037 case 19: return offsetofPPCGuestState(guest_VR19);
1038 case 20: return offsetofPPCGuestState(guest_VR20);
1039 case 21: return offsetofPPCGuestState(guest_VR21);
1040 case 22: return offsetofPPCGuestState(guest_VR22);
1041 case 23: return offsetofPPCGuestState(guest_VR23);
1042 case 24: return offsetofPPCGuestState(guest_VR24);
1043 case 25: return offsetofPPCGuestState(guest_VR25);
1044 case 26: return offsetofPPCGuestState(guest_VR26);
1045 case 27: return offsetofPPCGuestState(guest_VR27);
1046 case 28: return offsetofPPCGuestState(guest_VR28);
1047 case 29: return offsetofPPCGuestState(guest_VR29);
1048 case 30: return offsetofPPCGuestState(guest_VR30);
1049 case 31: return offsetofPPCGuestState(guest_VR31);
1050 default: break;
sewardjb51f0f42005-07-18 11:38:02 +00001051 }
cerion5b2325f2005-12-23 00:55:09 +00001052 vpanic("vextorGuestRegOffset(ppc)"); /*notreached*/
sewardjb51f0f42005-07-18 11:38:02 +00001053}
1054
1055static IRExpr* getVReg ( UInt archreg )
1056{
1057 vassert(archreg < 32);
1058 return IRExpr_Get( vectorGuestRegOffset(archreg), Ity_V128 );
1059}
1060
1061/* Ditto, but write to a reg instead. */
1062static void putVReg ( UInt archreg, IRExpr* e )
1063{
1064 vassert(archreg < 32);
sewardjdd40fdf2006-12-24 02:20:24 +00001065 vassert(typeOfIRExpr(irsb->tyenv, e) == Ity_V128);
sewardjb51f0f42005-07-18 11:38:02 +00001066 stmt( IRStmt_Put(vectorGuestRegOffset(archreg), e) );
1067}
1068
1069static Int guestCR321offset ( UInt cr )
1070{
cerion5b2325f2005-12-23 00:55:09 +00001071 switch (cr) {
1072 case 0: return offsetofPPCGuestState(guest_CR0_321 );
1073 case 1: return offsetofPPCGuestState(guest_CR1_321 );
1074 case 2: return offsetofPPCGuestState(guest_CR2_321 );
1075 case 3: return offsetofPPCGuestState(guest_CR3_321 );
1076 case 4: return offsetofPPCGuestState(guest_CR4_321 );
1077 case 5: return offsetofPPCGuestState(guest_CR5_321 );
1078 case 6: return offsetofPPCGuestState(guest_CR6_321 );
1079 case 7: return offsetofPPCGuestState(guest_CR7_321 );
1080 default: vpanic("guestCR321offset(ppc)");
sewardjb51f0f42005-07-18 11:38:02 +00001081 }
1082}
1083
1084static Int guestCR0offset ( UInt cr )
1085{
cerion5b2325f2005-12-23 00:55:09 +00001086 switch (cr) {
1087 case 0: return offsetofPPCGuestState(guest_CR0_0 );
1088 case 1: return offsetofPPCGuestState(guest_CR1_0 );
1089 case 2: return offsetofPPCGuestState(guest_CR2_0 );
1090 case 3: return offsetofPPCGuestState(guest_CR3_0 );
1091 case 4: return offsetofPPCGuestState(guest_CR4_0 );
1092 case 5: return offsetofPPCGuestState(guest_CR5_0 );
1093 case 6: return offsetofPPCGuestState(guest_CR6_0 );
1094 case 7: return offsetofPPCGuestState(guest_CR7_0 );
1095 default: vpanic("guestCR3offset(ppc)");
sewardjb51f0f42005-07-18 11:38:02 +00001096 }
sewardjb51f0f42005-07-18 11:38:02 +00001097}
1098
cerion07b07a92005-12-22 14:32:35 +00001099// ROTL(src32/64, rot_amt5/6)
ceriond953ebb2005-11-29 13:27:20 +00001100static IRExpr* /* :: Ity_I32/64 */ ROTL ( IRExpr* src,
1101 IRExpr* rot_amt )
sewardjb51f0f42005-07-18 11:38:02 +00001102{
ceriond953ebb2005-11-29 13:27:20 +00001103 IRExpr *mask, *rot;
sewardjdd40fdf2006-12-24 02:20:24 +00001104 vassert(typeOfIRExpr(irsb->tyenv,rot_amt) == Ity_I8);
sewardjb51f0f42005-07-18 11:38:02 +00001105
sewardjdd40fdf2006-12-24 02:20:24 +00001106 if (typeOfIRExpr(irsb->tyenv,src) == Ity_I64) {
ceriond953ebb2005-11-29 13:27:20 +00001107 // rot = (src << rot_amt) | (src >> (64-rot_amt))
1108 mask = binop(Iop_And8, rot_amt, mkU8(63));
1109 rot = binop(Iop_Or64,
1110 binop(Iop_Shl64, src, mask),
1111 binop(Iop_Shr64, src, binop(Iop_Sub8, mkU8(64), mask)));
1112 } else {
ceriond953ebb2005-11-29 13:27:20 +00001113 // rot = (src << rot_amt) | (src >> (32-rot_amt))
cerionf0de28c2005-12-13 20:21:11 +00001114 mask = binop(Iop_And8, rot_amt, mkU8(31));
ceriond953ebb2005-11-29 13:27:20 +00001115 rot = binop(Iop_Or32,
1116 binop(Iop_Shl32, src, mask),
1117 binop(Iop_Shr32, src, binop(Iop_Sub8, mkU8(32), mask)));
cerion2831b002005-11-30 19:55:22 +00001118 }
sewardjc9659532005-07-21 21:33:57 +00001119 /* Note: the MuxOX is not merely an optimisation; it's needed
cerionf0de28c2005-12-13 20:21:11 +00001120 because otherwise the Shr is a shift by the word size when
ceriond953ebb2005-11-29 13:27:20 +00001121 mask denotes zero. For rotates by immediates, a lot of
sewardjc9659532005-07-21 21:33:57 +00001122 this junk gets folded out. */
ceriond953ebb2005-11-29 13:27:20 +00001123 return IRExpr_Mux0X( mask, /* zero rotate */ src,
1124 /* non-zero rotate */ rot );
cerion45b70ff2005-01-31 17:03:25 +00001125}
cerion896a1372005-01-25 12:24:25 +00001126
ceriond953ebb2005-11-29 13:27:20 +00001127/* Standard effective address calc: (rA + rB) */
1128static IRExpr* ea_rA_idxd ( UInt rA, UInt rB )
1129{
1130 IRType ty = mode64 ? Ity_I64 : Ity_I32;
1131 vassert(rA < 32);
1132 vassert(rB < 32);
cerion2831b002005-11-30 19:55:22 +00001133 return binop(mkSzOp(ty, Iop_Add8), getIReg(rA), getIReg(rB));
sewardj87e651f2005-09-09 08:31:18 +00001134}
1135
ceriond953ebb2005-11-29 13:27:20 +00001136/* Standard effective address calc: (rA + simm) */
1137static IRExpr* ea_rA_simm ( UInt rA, UInt simm16 )
sewardj87e651f2005-09-09 08:31:18 +00001138{
ceriond953ebb2005-11-29 13:27:20 +00001139 IRType ty = mode64 ? Ity_I64 : Ity_I32;
1140 vassert(rA < 32);
cerion2831b002005-11-30 19:55:22 +00001141 return binop(mkSzOp(ty, Iop_Add8), getIReg(rA),
1142 mkSzExtendS16(ty, simm16));
ceriond953ebb2005-11-29 13:27:20 +00001143}
1144
1145/* Standard effective address calc: (rA|0) */
1146static IRExpr* ea_rAor0 ( UInt rA )
1147{
1148 IRType ty = mode64 ? Ity_I64 : Ity_I32;
1149 vassert(rA < 32);
sewardj87e651f2005-09-09 08:31:18 +00001150 if (rA == 0) {
cerion2831b002005-11-30 19:55:22 +00001151 return mkSzImm(ty, 0);
sewardj87e651f2005-09-09 08:31:18 +00001152 } else {
1153 return getIReg(rA);
1154 }
1155}
1156
ceriond953ebb2005-11-29 13:27:20 +00001157/* Standard effective address calc: (rA|0) + rB */
1158static IRExpr* ea_rAor0_idxd ( UInt rA, UInt rB )
1159{
1160 vassert(rA < 32);
1161 vassert(rB < 32);
1162 return (rA == 0) ? getIReg(rB) : ea_rA_idxd( rA, rB );
1163}
1164
1165/* Standard effective address calc: (rA|0) + simm16 */
1166static IRExpr* ea_rAor0_simm ( UInt rA, UInt simm16 )
1167{
1168 IRType ty = mode64 ? Ity_I64 : Ity_I32;
1169 vassert(rA < 32);
1170 if (rA == 0) {
cerion2831b002005-11-30 19:55:22 +00001171 return mkSzExtendS16(ty, simm16);
ceriond953ebb2005-11-29 13:27:20 +00001172 } else {
1173 return ea_rA_simm( rA, simm16 );
1174 }
1175}
1176
1177
1178/* Align effective address */
1179static IRExpr* addr_align( IRExpr* addr, UChar align )
1180{
1181 IRType ty = mode64 ? Ity_I64 : Ity_I32;
1182 Long mask;
1183 switch (align) {
cerion2831b002005-11-30 19:55:22 +00001184 case 1: return addr; // byte aligned
ceriond953ebb2005-11-29 13:27:20 +00001185 case 2: mask = ((Long)-1) << 1; break; // half-word aligned
1186 case 4: mask = ((Long)-1) << 2; break; // word aligned
1187 case 16: mask = ((Long)-1) << 4; break; // quad-word aligned
1188 default:
1189 vex_printf("addr_align: align = %u\n", align);
cerion5b2325f2005-12-23 00:55:09 +00001190 vpanic("addr_align(ppc)");
ceriond953ebb2005-11-29 13:27:20 +00001191 }
1192
sewardjdd40fdf2006-12-24 02:20:24 +00001193 vassert(typeOfIRExpr(irsb->tyenv,addr) == ty);
cerionfb197c42005-12-24 12:32:10 +00001194 return binop( mkSzOp(ty, Iop_And8), addr, mkSzImm(ty, mask) );
ceriond953ebb2005-11-29 13:27:20 +00001195}
1196
cerion896a1372005-01-25 12:24:25 +00001197
sewardje9d8a262009-07-01 08:06:34 +00001198/* Exit the trace if ADDR (intended to be a guest memory address) is
1199 not ALIGN-aligned, generating a request for a SIGBUS followed by a
1200 restart of the current insn. */
1201static void gen_SIGBUS_if_misaligned ( IRTemp addr, UChar align )
1202{
1203 vassert(align == 4 || align == 8);
1204 if (mode64) {
1205 vassert(typeOfIRTemp(irsb->tyenv, addr) == Ity_I64);
1206 stmt(
1207 IRStmt_Exit(
1208 binop(Iop_CmpNE64,
1209 binop(Iop_And64, mkexpr(addr), mkU64(align-1)),
1210 mkU64(0)),
1211 Ijk_SigBUS,
1212 IRConst_U64( guest_CIA_curr_instr )
1213 )
1214 );
1215 } else {
1216 vassert(typeOfIRTemp(irsb->tyenv, addr) == Ity_I32);
1217 stmt(
1218 IRStmt_Exit(
1219 binop(Iop_CmpNE32,
1220 binop(Iop_And32, mkexpr(addr), mkU32(align-1)),
1221 mkU32(0)),
1222 Ijk_SigBUS,
1223 IRConst_U32( guest_CIA_curr_instr )
1224 )
1225 );
1226 }
1227}
1228
1229
sewardjaca070a2006-10-17 00:28:22 +00001230/* Generate AbiHints which mark points at which the ELF or PowerOpen
1231 ABIs say that the stack red zone (viz, -N(r1) .. -1(r1), for some
1232 N) becomes undefined. That is at function calls and returns. ELF
sewardj478646f2008-05-01 20:13:04 +00001233 ppc32 doesn't have this "feature" (how fortunate for it). nia is
1234 the address of the next instruction to be executed.
sewardjcf8986c2006-01-18 04:14:52 +00001235*/
sewardj478646f2008-05-01 20:13:04 +00001236static void make_redzone_AbiHint ( VexAbiInfo* vbi,
1237 IRTemp nia, HChar* who )
sewardjcf8986c2006-01-18 04:14:52 +00001238{
sewardjdd40fdf2006-12-24 02:20:24 +00001239 Int szB = vbi->guest_stack_redzone_size;
sewardjcf8986c2006-01-18 04:14:52 +00001240 if (0) vex_printf("AbiHint: %s\n", who);
sewardjaca070a2006-10-17 00:28:22 +00001241 vassert(szB >= 0);
1242 if (szB > 0) {
sewardj478646f2008-05-01 20:13:04 +00001243 if (mode64) {
1244 vassert(typeOfIRTemp(irsb->tyenv, nia) == Ity_I64);
sewardjaca070a2006-10-17 00:28:22 +00001245 stmt( IRStmt_AbiHint(
1246 binop(Iop_Sub64, getIReg(1), mkU64(szB)),
sewardj478646f2008-05-01 20:13:04 +00001247 szB,
1248 mkexpr(nia)
sewardjaca070a2006-10-17 00:28:22 +00001249 ));
sewardj478646f2008-05-01 20:13:04 +00001250 } else {
1251 vassert(typeOfIRTemp(irsb->tyenv, nia) == Ity_I32);
sewardjaca070a2006-10-17 00:28:22 +00001252 stmt( IRStmt_AbiHint(
1253 binop(Iop_Sub32, getIReg(1), mkU32(szB)),
sewardj478646f2008-05-01 20:13:04 +00001254 szB,
1255 mkexpr(nia)
sewardjaca070a2006-10-17 00:28:22 +00001256 ));
sewardj478646f2008-05-01 20:13:04 +00001257 }
sewardjaca070a2006-10-17 00:28:22 +00001258 }
sewardjcf8986c2006-01-18 04:14:52 +00001259}
1260
1261
cerion896a1372005-01-25 12:24:25 +00001262/*------------------------------------------------------------*/
sewardjb51f0f42005-07-18 11:38:02 +00001263/*--- Helpers for condition codes. ---*/
cerion896a1372005-01-25 12:24:25 +00001264/*------------------------------------------------------------*/
1265
sewardjb51f0f42005-07-18 11:38:02 +00001266/* Condition register layout.
cerion896a1372005-01-25 12:24:25 +00001267
sewardjb51f0f42005-07-18 11:38:02 +00001268 In the hardware, CR is laid out like this. The leftmost end is the
1269 most significant bit in the register; however the IBM documentation
1270 numbers the bits backwards for some reason.
1271
1272 CR0 CR1 .......... CR6 CR7
1273 0 .. 3 ....................... 28 .. 31 (IBM bit numbering)
1274 31 28 3 0 (normal bit numbering)
1275
cerionedf7fc52005-11-18 20:57:41 +00001276 Each CR field is 4 bits: [<,>,==,SO]
sewardjb51f0f42005-07-18 11:38:02 +00001277
cerionedf7fc52005-11-18 20:57:41 +00001278 Hence in IBM's notation, BI=0 is CR7[SO], BI=1 is CR7[==], etc.
sewardjb51f0f42005-07-18 11:38:02 +00001279
1280 Indexing from BI to guest state:
1281
1282 let n = BI / 4
1283 off = BI % 4
1284 this references CR n:
1285
cerionedf7fc52005-11-18 20:57:41 +00001286 off==0 -> guest_CRn_321 >> 3
1287 off==1 -> guest_CRn_321 >> 2
1288 off==2 -> guest_CRn_321 >> 1
sewardjb51f0f42005-07-18 11:38:02 +00001289 off==3 -> guest_CRn_SO
sewardjb51f0f42005-07-18 11:38:02 +00001290
1291 Bear in mind the only significant bit in guest_CRn_SO is bit 0
cerionedf7fc52005-11-18 20:57:41 +00001292 (normal notation) and in guest_CRn_321 the significant bits are
sewardjb51f0f42005-07-18 11:38:02 +00001293 3, 2 and 1 (normal notation).
1294*/
cerionedf7fc52005-11-18 20:57:41 +00001295
1296static void putCR321 ( UInt cr, IRExpr* e )
1297{
1298 vassert(cr < 8);
sewardjdd40fdf2006-12-24 02:20:24 +00001299 vassert(typeOfIRExpr(irsb->tyenv, e) == Ity_I8);
cerionedf7fc52005-11-18 20:57:41 +00001300 stmt( IRStmt_Put(guestCR321offset(cr), e) );
1301}
1302
1303static void putCR0 ( UInt cr, IRExpr* e )
1304{
1305 vassert(cr < 8);
sewardjdd40fdf2006-12-24 02:20:24 +00001306 vassert(typeOfIRExpr(irsb->tyenv, e) == Ity_I8);
cerionedf7fc52005-11-18 20:57:41 +00001307 stmt( IRStmt_Put(guestCR0offset(cr), e) );
1308}
1309
1310static IRExpr* /* :: Ity_I8 */ getCR0 ( UInt cr )
1311{
1312 vassert(cr < 8);
1313 return IRExpr_Get(guestCR0offset(cr), Ity_I8);
1314}
1315
1316static IRExpr* /* :: Ity_I8 */ getCR321 ( UInt cr )
1317{
1318 vassert(cr < 8);
1319 return IRExpr_Get(guestCR321offset(cr), Ity_I8);
1320}
1321
sewardjb51f0f42005-07-18 11:38:02 +00001322/* Fetch the specified CR bit (as per IBM/hardware notation) and
1323 return it at the bottom of an I32; the top 31 bits are guaranteed
1324 to be zero. */
1325static IRExpr* /* :: Ity_I32 */ getCRbit ( UInt bi )
cerion896a1372005-01-25 12:24:25 +00001326{
sewardjb51f0f42005-07-18 11:38:02 +00001327 UInt n = bi / 4;
1328 UInt off = bi % 4;
1329 vassert(bi < 32);
1330 if (off == 3) {
1331 /* Fetch the SO bit for this CR field */
1332 /* Note: And32 is redundant paranoia iff guest state only has 0
1333 or 1 in that slot. */
1334 return binop(Iop_And32, unop(Iop_8Uto32, getCR0(n)), mkU32(1));
1335 } else {
1336 /* Fetch the <, > or == bit for this CR field */
1337 return binop( Iop_And32,
1338 binop( Iop_Shr32,
1339 unop(Iop_8Uto32, getCR321(n)),
sewardjc7cd2142005-09-09 22:31:49 +00001340 mkU8(toUChar(3-off)) ),
sewardjb51f0f42005-07-18 11:38:02 +00001341 mkU32(1) );
1342 }
cerion91ad5362005-01-27 23:02:41 +00001343}
1344
sewardjb51f0f42005-07-18 11:38:02 +00001345/* Dually, write the least significant bit of BIT to the specified CR
1346 bit. Indexing as per getCRbit. */
1347static void putCRbit ( UInt bi, IRExpr* bit )
1348{
sewardj197bd172005-10-12 11:34:33 +00001349 UInt n, off;
sewardjb51f0f42005-07-18 11:38:02 +00001350 IRExpr* safe;
sewardjdd40fdf2006-12-24 02:20:24 +00001351 vassert(typeOfIRExpr(irsb->tyenv,bit) == Ity_I32);
sewardjb51f0f42005-07-18 11:38:02 +00001352 safe = binop(Iop_And32, bit, mkU32(1));
sewardj197bd172005-10-12 11:34:33 +00001353 n = bi / 4;
1354 off = bi % 4;
sewardjb51f0f42005-07-18 11:38:02 +00001355 vassert(bi < 32);
1356 if (off == 3) {
1357 /* This is the SO bit for this CR field */
1358 putCR0(n, unop(Iop_32to8, safe));
1359 } else {
1360 off = 3 - off;
1361 vassert(off == 1 || off == 2 || off == 3);
1362 putCR321(
1363 n,
1364 unop( Iop_32to8,
1365 binop( Iop_Or32,
1366 /* old value with field masked out */
1367 binop(Iop_And32, unop(Iop_8Uto32, getCR321(n)),
1368 mkU32(~(1 << off))),
1369 /* new value in the right place */
sewardjc7cd2142005-09-09 22:31:49 +00001370 binop(Iop_Shl32, safe, mkU8(toUChar(off)))
sewardjb51f0f42005-07-18 11:38:02 +00001371 )
1372 )
1373 );
1374 }
1375}
1376
sewardjb51f0f42005-07-18 11:38:02 +00001377/* Fetch the specified CR bit (as per IBM/hardware notation) and
1378 return it somewhere in an I32; it does not matter where, but
1379 whichever bit it is, all other bits are guaranteed to be zero. In
1380 other words, the I32-typed expression will be zero if the bit is
1381 zero and nonzero if the bit is 1. Write into *where the index
1382 of where the bit will be. */
1383
cerion5b2325f2005-12-23 00:55:09 +00001384static
1385IRExpr* /* :: Ity_I32 */ getCRbit_anywhere ( UInt bi, Int* where )
sewardjb51f0f42005-07-18 11:38:02 +00001386{
1387 UInt n = bi / 4;
1388 UInt off = bi % 4;
1389 vassert(bi < 32);
1390 if (off == 3) {
1391 /* Fetch the SO bit for this CR field */
1392 /* Note: And32 is redundant paranoia iff guest state only has 0
1393 or 1 in that slot. */
1394 *where = 0;
1395 return binop(Iop_And32, unop(Iop_8Uto32, getCR0(n)), mkU32(1));
1396 } else {
1397 /* Fetch the <, > or == bit for this CR field */
1398 *where = 3-off;
1399 return binop( Iop_And32,
1400 unop(Iop_8Uto32, getCR321(n)),
1401 mkU32(1 << (3-off)) );
1402 }
1403}
1404
sewardjb51f0f42005-07-18 11:38:02 +00001405/* Set the CR0 flags following an arithmetic operation.
1406 (Condition Register CR0 Field Definition, PPC32 p60)
cerion896a1372005-01-25 12:24:25 +00001407*/
cerionedf7fc52005-11-18 20:57:41 +00001408static IRExpr* getXER_SO ( void );
sewardj20ef5472005-07-21 14:48:31 +00001409static void set_CR0 ( IRExpr* result )
cerion896a1372005-01-25 12:24:25 +00001410{
sewardjdd40fdf2006-12-24 02:20:24 +00001411 vassert(typeOfIRExpr(irsb->tyenv,result) == Ity_I32 ||
1412 typeOfIRExpr(irsb->tyenv,result) == Ity_I64);
ceriond953ebb2005-11-29 13:27:20 +00001413 if (mode64) {
ceriond953ebb2005-11-29 13:27:20 +00001414 putCR321( 0, unop(Iop_64to8,
cerion2831b002005-11-30 19:55:22 +00001415 binop(Iop_CmpORD64S, result, mkU64(0))) );
ceriond953ebb2005-11-29 13:27:20 +00001416 } else {
1417 putCR321( 0, unop(Iop_32to8,
1418 binop(Iop_CmpORD32S, result, mkU32(0))) );
1419 }
sewardjb51f0f42005-07-18 11:38:02 +00001420 putCR0( 0, getXER_SO() );
cerion896a1372005-01-25 12:24:25 +00001421}
cerion896a1372005-01-25 12:24:25 +00001422
sewardj20ef5472005-07-21 14:48:31 +00001423
cerionedf7fc52005-11-18 20:57:41 +00001424/* Set the CR6 flags following an AltiVec compare operation. */
1425static void set_AV_CR6 ( IRExpr* result, Bool test_all_ones )
1426{
1427 /* CR6[0:3] = {all_ones, 0, all_zeros, 0}
1428 all_ones = (v[0] && v[1] && v[2] && v[3])
1429 all_zeros = ~(v[0] || v[1] || v[2] || v[3])
1430 */
1431 IRTemp v0 = newTemp(Ity_V128);
1432 IRTemp v1 = newTemp(Ity_V128);
1433 IRTemp v2 = newTemp(Ity_V128);
1434 IRTemp v3 = newTemp(Ity_V128);
1435 IRTemp rOnes = newTemp(Ity_I8);
1436 IRTemp rZeros = newTemp(Ity_I8);
1437
sewardjdd40fdf2006-12-24 02:20:24 +00001438 vassert(typeOfIRExpr(irsb->tyenv,result) == Ity_V128);
cerionedf7fc52005-11-18 20:57:41 +00001439
1440 assign( v0, result );
1441 assign( v1, binop(Iop_ShrV128, result, mkU8(32)) );
1442 assign( v2, binop(Iop_ShrV128, result, mkU8(64)) );
1443 assign( v3, binop(Iop_ShrV128, result, mkU8(96)) );
1444
1445 assign( rZeros, unop(Iop_1Uto8,
1446 binop(Iop_CmpEQ32, mkU32(0xFFFFFFFF),
1447 unop(Iop_Not32,
1448 unop(Iop_V128to32,
1449 binop(Iop_OrV128,
1450 binop(Iop_OrV128, mkexpr(v0), mkexpr(v1)),
1451 binop(Iop_OrV128, mkexpr(v2), mkexpr(v3))))
1452 ))) );
1453
1454 if (test_all_ones) {
1455 assign( rOnes, unop(Iop_1Uto8,
1456 binop(Iop_CmpEQ32, mkU32(0xFFFFFFFF),
1457 unop(Iop_V128to32,
1458 binop(Iop_AndV128,
1459 binop(Iop_AndV128, mkexpr(v0), mkexpr(v1)),
cerion5b2325f2005-12-23 00:55:09 +00001460 binop(Iop_AndV128, mkexpr(v2), mkexpr(v3)))
1461 ))) );
cerionedf7fc52005-11-18 20:57:41 +00001462 putCR321( 6, binop(Iop_Or8,
1463 binop(Iop_Shl8, mkexpr(rOnes), mkU8(3)),
1464 binop(Iop_Shl8, mkexpr(rZeros), mkU8(1))) );
1465 } else {
1466 putCR321( 6, binop(Iop_Shl8, mkexpr(rZeros), mkU8(1)) );
1467 }
1468 putCR0( 6, mkU8(0) );
1469}
1470
1471
1472
1473/*------------------------------------------------------------*/
1474/*--- Helpers for XER flags. ---*/
1475/*------------------------------------------------------------*/
1476
1477static void putXER_SO ( IRExpr* e )
1478{
sewardj63327402006-01-25 03:26:27 +00001479 IRExpr* so;
sewardjdd40fdf2006-12-24 02:20:24 +00001480 vassert(typeOfIRExpr(irsb->tyenv, e) == Ity_I8);
sewardj63327402006-01-25 03:26:27 +00001481 so = binop(Iop_And8, e, mkU8(1));
cerion5b2325f2005-12-23 00:55:09 +00001482 stmt( IRStmt_Put( OFFB_XER_SO, so ) );
cerionedf7fc52005-11-18 20:57:41 +00001483}
1484
1485static void putXER_OV ( IRExpr* e )
1486{
sewardj63327402006-01-25 03:26:27 +00001487 IRExpr* ov;
sewardjdd40fdf2006-12-24 02:20:24 +00001488 vassert(typeOfIRExpr(irsb->tyenv, e) == Ity_I8);
sewardj63327402006-01-25 03:26:27 +00001489 ov = binop(Iop_And8, e, mkU8(1));
cerion5b2325f2005-12-23 00:55:09 +00001490 stmt( IRStmt_Put( OFFB_XER_OV, ov ) );
cerionedf7fc52005-11-18 20:57:41 +00001491}
1492
1493static void putXER_CA ( IRExpr* e )
1494{
sewardj63327402006-01-25 03:26:27 +00001495 IRExpr* ca;
sewardjdd40fdf2006-12-24 02:20:24 +00001496 vassert(typeOfIRExpr(irsb->tyenv, e) == Ity_I8);
sewardj63327402006-01-25 03:26:27 +00001497 ca = binop(Iop_And8, e, mkU8(1));
cerion5b2325f2005-12-23 00:55:09 +00001498 stmt( IRStmt_Put( OFFB_XER_CA, ca ) );
cerionedf7fc52005-11-18 20:57:41 +00001499}
1500
1501static void putXER_BC ( IRExpr* e )
1502{
sewardj63327402006-01-25 03:26:27 +00001503 IRExpr* bc;
sewardjdd40fdf2006-12-24 02:20:24 +00001504 vassert(typeOfIRExpr(irsb->tyenv, e) == Ity_I8);
sewardj63327402006-01-25 03:26:27 +00001505 bc = binop(Iop_And8, e, mkU8(0x7F));
cerion5b2325f2005-12-23 00:55:09 +00001506 stmt( IRStmt_Put( OFFB_XER_BC, bc ) );
cerionedf7fc52005-11-18 20:57:41 +00001507}
1508
1509static IRExpr* /* :: Ity_I8 */ getXER_SO ( void )
1510{
cerion5b2325f2005-12-23 00:55:09 +00001511 return IRExpr_Get( OFFB_XER_SO, Ity_I8 );
cerionedf7fc52005-11-18 20:57:41 +00001512}
1513
1514static IRExpr* /* :: Ity_I32 */ getXER_SO32 ( void )
1515{
1516 return binop( Iop_And32, unop(Iop_8Uto32, getXER_SO()), mkU32(1) );
1517}
1518
1519static IRExpr* /* :: Ity_I8 */ getXER_OV ( void )
1520{
cerion5b2325f2005-12-23 00:55:09 +00001521 return IRExpr_Get( OFFB_XER_OV, Ity_I8 );
cerionedf7fc52005-11-18 20:57:41 +00001522}
1523
1524static IRExpr* /* :: Ity_I32 */ getXER_OV32 ( void )
1525{
1526 return binop( Iop_And32, unop(Iop_8Uto32, getXER_OV()), mkU32(1) );
1527}
1528
1529static IRExpr* /* :: Ity_I32 */ getXER_CA32 ( void )
1530{
cerion5b2325f2005-12-23 00:55:09 +00001531 IRExpr* ca = IRExpr_Get( OFFB_XER_CA, Ity_I8 );
ceriond953ebb2005-11-29 13:27:20 +00001532 return binop( Iop_And32, unop(Iop_8Uto32, ca ), mkU32(1) );
cerionedf7fc52005-11-18 20:57:41 +00001533}
1534
1535static IRExpr* /* :: Ity_I8 */ getXER_BC ( void )
1536{
cerion5b2325f2005-12-23 00:55:09 +00001537 return IRExpr_Get( OFFB_XER_BC, Ity_I8 );
cerionedf7fc52005-11-18 20:57:41 +00001538}
1539
1540static IRExpr* /* :: Ity_I32 */ getXER_BC32 ( void )
1541{
cerion5b2325f2005-12-23 00:55:09 +00001542 IRExpr* bc = IRExpr_Get( OFFB_XER_BC, Ity_I8 );
ceriond953ebb2005-11-29 13:27:20 +00001543 return binop( Iop_And32, unop(Iop_8Uto32, bc), mkU32(0x7F) );
cerionedf7fc52005-11-18 20:57:41 +00001544}
1545
1546
sewardj20ef5472005-07-21 14:48:31 +00001547/* RES is the result of doing OP on ARGL and ARGR. Set %XER.OV and
1548 %XER.SO accordingly. */
1549
ceriond953ebb2005-11-29 13:27:20 +00001550static void set_XER_OV_32( UInt op, IRExpr* res,
1551 IRExpr* argL, IRExpr* argR )
sewardj20ef5472005-07-21 14:48:31 +00001552{
1553 IRTemp t64;
1554 IRExpr* xer_ov;
cerion5b2325f2005-12-23 00:55:09 +00001555 vassert(op < PPCG_FLAG_OP_NUMBER);
sewardjdd40fdf2006-12-24 02:20:24 +00001556 vassert(typeOfIRExpr(irsb->tyenv,res) == Ity_I32);
1557 vassert(typeOfIRExpr(irsb->tyenv,argL) == Ity_I32);
1558 vassert(typeOfIRExpr(irsb->tyenv,argR) == Ity_I32);
sewardj20ef5472005-07-21 14:48:31 +00001559
1560# define INT32_MIN 0x80000000
1561
1562# define XOR2(_aa,_bb) \
1563 binop(Iop_Xor32,(_aa),(_bb))
1564
1565# define XOR3(_cc,_dd,_ee) \
1566 binop(Iop_Xor32,binop(Iop_Xor32,(_cc),(_dd)),(_ee))
1567
1568# define AND3(_ff,_gg,_hh) \
1569 binop(Iop_And32,binop(Iop_And32,(_ff),(_gg)),(_hh))
1570
1571#define NOT(_jj) \
1572 unop(Iop_Not32, (_jj))
1573
1574 switch (op) {
cerion5b2325f2005-12-23 00:55:09 +00001575 case /* 0 */ PPCG_FLAG_OP_ADD:
1576 case /* 1 */ PPCG_FLAG_OP_ADDE:
ceriond953ebb2005-11-29 13:27:20 +00001577 /* (argL^argR^-1) & (argL^res) & (1<<31) ?1:0 */
1578 // i.e. ((both_same_sign) & (sign_changed) & (sign_mask))
1579 xer_ov
1580 = AND3( XOR3(argL,argR,mkU32(-1)),
1581 XOR2(argL,res),
1582 mkU32(INT32_MIN) );
1583 /* xer_ov can only be 0 or 1<<31 */
1584 xer_ov
1585 = binop(Iop_Shr32, xer_ov, mkU8(31) );
1586 break;
1587
cerion5b2325f2005-12-23 00:55:09 +00001588 case /* 2 */ PPCG_FLAG_OP_DIVW:
ceriond953ebb2005-11-29 13:27:20 +00001589 /* (argL == INT32_MIN && argR == -1) || argR == 0 */
1590 xer_ov
1591 = mkOR1(
1592 mkAND1(
1593 binop(Iop_CmpEQ32, argL, mkU32(INT32_MIN)),
1594 binop(Iop_CmpEQ32, argR, mkU32(-1))
1595 ),
1596 binop(Iop_CmpEQ32, argR, mkU32(0) )
1597 );
1598 xer_ov
1599 = unop(Iop_1Uto32, xer_ov);
1600 break;
1601
cerion5b2325f2005-12-23 00:55:09 +00001602 case /* 3 */ PPCG_FLAG_OP_DIVWU:
ceriond953ebb2005-11-29 13:27:20 +00001603 /* argR == 0 */
1604 xer_ov
1605 = unop(Iop_1Uto32, binop(Iop_CmpEQ32, argR, mkU32(0)));
1606 break;
1607
cerion5b2325f2005-12-23 00:55:09 +00001608 case /* 4 */ PPCG_FLAG_OP_MULLW:
ceriond953ebb2005-11-29 13:27:20 +00001609 /* OV true if result can't be represented in 32 bits
1610 i.e sHi != sign extension of sLo */
1611 t64 = newTemp(Ity_I64);
1612 assign( t64, binop(Iop_MullS32, argL, argR) );
1613 xer_ov
1614 = binop( Iop_CmpNE32,
1615 unop(Iop_64HIto32, mkexpr(t64)),
1616 binop( Iop_Sar32,
1617 unop(Iop_64to32, mkexpr(t64)),
1618 mkU8(31))
1619 );
1620 xer_ov
1621 = unop(Iop_1Uto32, xer_ov);
1622 break;
1623
cerion5b2325f2005-12-23 00:55:09 +00001624 case /* 5 */ PPCG_FLAG_OP_NEG:
ceriond953ebb2005-11-29 13:27:20 +00001625 /* argL == INT32_MIN */
1626 xer_ov
1627 = unop( Iop_1Uto32,
1628 binop(Iop_CmpEQ32, argL, mkU32(INT32_MIN)) );
1629 break;
1630
cerion5b2325f2005-12-23 00:55:09 +00001631 case /* 6 */ PPCG_FLAG_OP_SUBF:
1632 case /* 7 */ PPCG_FLAG_OP_SUBFC:
1633 case /* 8 */ PPCG_FLAG_OP_SUBFE:
ceriond953ebb2005-11-29 13:27:20 +00001634 /* ((~argL)^argR^-1) & ((~argL)^res) & (1<<31) ?1:0; */
1635 xer_ov
1636 = AND3( XOR3(NOT(argL),argR,mkU32(-1)),
1637 XOR2(NOT(argL),res),
1638 mkU32(INT32_MIN) );
1639 /* xer_ov can only be 0 or 1<<31 */
1640 xer_ov
1641 = binop(Iop_Shr32, xer_ov, mkU8(31) );
1642 break;
1643
1644 default:
1645 vex_printf("set_XER_OV: op = %u\n", op);
cerion5b2325f2005-12-23 00:55:09 +00001646 vpanic("set_XER_OV(ppc)");
sewardj20ef5472005-07-21 14:48:31 +00001647 }
ceriond953ebb2005-11-29 13:27:20 +00001648
sewardj20ef5472005-07-21 14:48:31 +00001649 /* xer_ov MUST denote either 0 or 1, no other value allowed */
ceriond953ebb2005-11-29 13:27:20 +00001650 putXER_OV( unop(Iop_32to8, xer_ov) );
sewardj20ef5472005-07-21 14:48:31 +00001651
1652 /* Update the summary overflow */
cerionedf7fc52005-11-18 20:57:41 +00001653 putXER_SO( binop(Iop_Or8, getXER_SO(), getXER_OV()) );
sewardj20ef5472005-07-21 14:48:31 +00001654
1655# undef INT32_MIN
1656# undef AND3
1657# undef XOR3
1658# undef XOR2
1659# undef NOT
1660}
1661
ceriond953ebb2005-11-29 13:27:20 +00001662static void set_XER_OV_64( UInt op, IRExpr* res,
1663 IRExpr* argL, IRExpr* argR )
1664{
1665 IRExpr* xer_ov;
cerion5b2325f2005-12-23 00:55:09 +00001666 vassert(op < PPCG_FLAG_OP_NUMBER);
sewardjdd40fdf2006-12-24 02:20:24 +00001667 vassert(typeOfIRExpr(irsb->tyenv,res) == Ity_I64);
1668 vassert(typeOfIRExpr(irsb->tyenv,argL) == Ity_I64);
1669 vassert(typeOfIRExpr(irsb->tyenv,argR) == Ity_I64);
ceriond953ebb2005-11-29 13:27:20 +00001670
cerion2831b002005-11-30 19:55:22 +00001671# define INT64_MIN 0x8000000000000000ULL
ceriond953ebb2005-11-29 13:27:20 +00001672
1673# define XOR2(_aa,_bb) \
1674 binop(Iop_Xor64,(_aa),(_bb))
1675
1676# define XOR3(_cc,_dd,_ee) \
1677 binop(Iop_Xor64,binop(Iop_Xor64,(_cc),(_dd)),(_ee))
1678
1679# define AND3(_ff,_gg,_hh) \
1680 binop(Iop_And64,binop(Iop_And64,(_ff),(_gg)),(_hh))
1681
1682#define NOT(_jj) \
1683 unop(Iop_Not64, (_jj))
1684
1685 switch (op) {
cerion5b2325f2005-12-23 00:55:09 +00001686 case /* 0 */ PPCG_FLAG_OP_ADD:
1687 case /* 1 */ PPCG_FLAG_OP_ADDE:
ceriond953ebb2005-11-29 13:27:20 +00001688 /* (argL^argR^-1) & (argL^res) & (1<<63) ? 1:0 */
1689 // i.e. ((both_same_sign) & (sign_changed) & (sign_mask))
1690 xer_ov
1691 = AND3( XOR3(argL,argR,mkU64(-1)),
1692 XOR2(argL,res),
1693 mkU64(INT64_MIN) );
1694 /* xer_ov can only be 0 or 1<<63 */
1695 xer_ov
1696 = unop(Iop_64to1, binop(Iop_Shr64, xer_ov, mkU8(63)));
1697 break;
1698
cerion5b2325f2005-12-23 00:55:09 +00001699 case /* 2 */ PPCG_FLAG_OP_DIVW:
ceriond953ebb2005-11-29 13:27:20 +00001700 /* (argL == INT64_MIN && argR == -1) || argR == 0 */
1701 xer_ov
1702 = mkOR1(
1703 mkAND1(
1704 binop(Iop_CmpEQ64, argL, mkU64(INT64_MIN)),
1705 binop(Iop_CmpEQ64, argR, mkU64(-1))
1706 ),
1707 binop(Iop_CmpEQ64, argR, mkU64(0) )
1708 );
1709 break;
1710
cerion5b2325f2005-12-23 00:55:09 +00001711 case /* 3 */ PPCG_FLAG_OP_DIVWU:
ceriond953ebb2005-11-29 13:27:20 +00001712 /* argR == 0 */
1713 xer_ov
1714 = binop(Iop_CmpEQ64, argR, mkU64(0));
1715 break;
1716
cerion5b2325f2005-12-23 00:55:09 +00001717 case /* 4 */ PPCG_FLAG_OP_MULLW: {
ceriond953ebb2005-11-29 13:27:20 +00001718 /* OV true if result can't be represented in 64 bits
1719 i.e sHi != sign extension of sLo */
ceriond953ebb2005-11-29 13:27:20 +00001720 xer_ov
cerionbb01b7c2005-12-16 13:40:18 +00001721 = binop( Iop_CmpNE32,
1722 unop(Iop_64HIto32, res),
1723 binop( Iop_Sar32,
1724 unop(Iop_64to32, res),
1725 mkU8(31))
1726 );
ceriond953ebb2005-11-29 13:27:20 +00001727 break;
1728 }
1729
cerion5b2325f2005-12-23 00:55:09 +00001730 case /* 5 */ PPCG_FLAG_OP_NEG:
ceriond953ebb2005-11-29 13:27:20 +00001731 /* argL == INT64_MIN */
1732 xer_ov
1733 = binop(Iop_CmpEQ64, argL, mkU64(INT64_MIN));
1734 break;
1735
cerion5b2325f2005-12-23 00:55:09 +00001736 case /* 6 */ PPCG_FLAG_OP_SUBF:
1737 case /* 7 */ PPCG_FLAG_OP_SUBFC:
1738 case /* 8 */ PPCG_FLAG_OP_SUBFE:
ceriond953ebb2005-11-29 13:27:20 +00001739 /* ((~argL)^argR^-1) & ((~argL)^res) & (1<<63) ?1:0; */
1740 xer_ov
1741 = AND3( XOR3(NOT(argL),argR,mkU64(-1)),
1742 XOR2(NOT(argL),res),
1743 mkU64(INT64_MIN) );
1744 /* xer_ov can only be 0 or 1<<63 */
1745 xer_ov
1746 = unop(Iop_64to1, binop(Iop_Shr64, xer_ov, mkU8(63)));
1747 break;
1748
1749 default:
1750 vex_printf("set_XER_OV: op = %u\n", op);
cerion5b2325f2005-12-23 00:55:09 +00001751 vpanic("set_XER_OV(ppc64)");
ceriond953ebb2005-11-29 13:27:20 +00001752 }
1753
1754 /* xer_ov MUST denote either 0 or 1, no other value allowed */
1755 putXER_OV( unop(Iop_1Uto8, xer_ov) );
1756
1757 /* Update the summary overflow */
1758 putXER_SO( binop(Iop_Or8, getXER_SO(), getXER_OV()) );
1759
1760# undef INT64_MIN
1761# undef AND3
1762# undef XOR3
1763# undef XOR2
1764# undef NOT
1765}
1766
1767static void set_XER_OV ( IRType ty, UInt op, IRExpr* res,
1768 IRExpr* argL, IRExpr* argR )
1769{
1770 if (ty == Ity_I32)
1771 set_XER_OV_32( op, res, argL, argR );
1772 else
1773 set_XER_OV_64( op, res, argL, argR );
1774}
1775
1776
1777
sewardjb51f0f42005-07-18 11:38:02 +00001778/* RES is the result of doing OP on ARGL and ARGR with the old %XER.CA
1779 value being OLDCA. Set %XER.CA accordingly. */
cerione9d361a2005-03-04 17:35:29 +00001780
cerion2831b002005-11-30 19:55:22 +00001781static void set_XER_CA_32 ( UInt op, IRExpr* res,
1782 IRExpr* argL, IRExpr* argR, IRExpr* oldca )
cerion38674602005-02-08 02:19:25 +00001783{
sewardj9a036bf2005-03-14 18:19:08 +00001784 IRExpr* xer_ca;
cerion5b2325f2005-12-23 00:55:09 +00001785 vassert(op < PPCG_FLAG_OP_NUMBER);
sewardjdd40fdf2006-12-24 02:20:24 +00001786 vassert(typeOfIRExpr(irsb->tyenv,res) == Ity_I32);
1787 vassert(typeOfIRExpr(irsb->tyenv,argL) == Ity_I32);
1788 vassert(typeOfIRExpr(irsb->tyenv,argR) == Ity_I32);
1789 vassert(typeOfIRExpr(irsb->tyenv,oldca) == Ity_I32);
cerion70e24122005-03-16 00:27:37 +00001790
sewardj20ef5472005-07-21 14:48:31 +00001791 /* Incoming oldca is assumed to hold the values 0 or 1 only. This
1792 seems reasonable given that it's always generated by
cerionedf7fc52005-11-18 20:57:41 +00001793 getXER_CA32(), which masks it accordingly. In any case it being
cerion75949202005-12-24 13:14:11 +00001794 0 or 1 is an invariant of the ppc guest state representation;
sewardj20ef5472005-07-21 14:48:31 +00001795 if it has any other value, that invariant has been violated. */
cerione9d361a2005-03-04 17:35:29 +00001796
sewardj20ef5472005-07-21 14:48:31 +00001797 switch (op) {
cerion5b2325f2005-12-23 00:55:09 +00001798 case /* 0 */ PPCG_FLAG_OP_ADD:
ceriond953ebb2005-11-29 13:27:20 +00001799 /* res <u argL */
1800 xer_ca
1801 = unop(Iop_1Uto32, binop(Iop_CmpLT32U, res, argL));
1802 break;
1803
cerion5b2325f2005-12-23 00:55:09 +00001804 case /* 1 */ PPCG_FLAG_OP_ADDE:
ceriond953ebb2005-11-29 13:27:20 +00001805 /* res <u argL || (old_ca==1 && res==argL) */
1806 xer_ca
1807 = mkOR1(
1808 binop(Iop_CmpLT32U, res, argL),
1809 mkAND1(
1810 binop(Iop_CmpEQ32, oldca, mkU32(1)),
1811 binop(Iop_CmpEQ32, res, argL)
1812 )
1813 );
1814 xer_ca
1815 = unop(Iop_1Uto32, xer_ca);
1816 break;
1817
cerion5b2325f2005-12-23 00:55:09 +00001818 case /* 8 */ PPCG_FLAG_OP_SUBFE:
ceriond953ebb2005-11-29 13:27:20 +00001819 /* res <u argR || (old_ca==1 && res==argR) */
1820 xer_ca
1821 = mkOR1(
1822 binop(Iop_CmpLT32U, res, argR),
1823 mkAND1(
1824 binop(Iop_CmpEQ32, oldca, mkU32(1)),
1825 binop(Iop_CmpEQ32, res, argR)
1826 )
1827 );
1828 xer_ca
1829 = unop(Iop_1Uto32, xer_ca);
1830 break;
1831
cerion5b2325f2005-12-23 00:55:09 +00001832 case /* 7 */ PPCG_FLAG_OP_SUBFC:
1833 case /* 9 */ PPCG_FLAG_OP_SUBFI:
ceriond953ebb2005-11-29 13:27:20 +00001834 /* res <=u argR */
1835 xer_ca
1836 = unop(Iop_1Uto32, binop(Iop_CmpLE32U, res, argR));
1837 break;
1838
cerion5b2325f2005-12-23 00:55:09 +00001839 case /* 10 */ PPCG_FLAG_OP_SRAW:
ceriond953ebb2005-11-29 13:27:20 +00001840 /* The shift amount is guaranteed to be in 0 .. 63 inclusive.
1841 If it is <= 31, behave like SRAWI; else XER.CA is the sign
1842 bit of argL. */
1843 /* This term valid for shift amount < 32 only */
1844 xer_ca
1845 = binop(
1846 Iop_And32,
1847 binop(Iop_Sar32, argL, mkU8(31)),
1848 binop( Iop_And32,
1849 argL,
1850 binop( Iop_Sub32,
cerion5b2325f2005-12-23 00:55:09 +00001851 binop(Iop_Shl32, mkU32(1),
1852 unop(Iop_32to8,argR)),
ceriond953ebb2005-11-29 13:27:20 +00001853 mkU32(1) )
1854 )
sewardj20ef5472005-07-21 14:48:31 +00001855 );
ceriond953ebb2005-11-29 13:27:20 +00001856 xer_ca
1857 = IRExpr_Mux0X(
1858 /* shift amt > 31 ? */
1859 unop(Iop_1Uto8, binop(Iop_CmpLT32U, mkU32(31), argR)),
1860 /* no -- be like srawi */
1861 unop(Iop_1Uto32, binop(Iop_CmpNE32, xer_ca, mkU32(0))),
1862 /* yes -- get sign bit of argL */
1863 binop(Iop_Shr32, argL, mkU8(31))
1864 );
1865 break;
sewardj20ef5472005-07-21 14:48:31 +00001866
cerion5b2325f2005-12-23 00:55:09 +00001867 case /* 11 */ PPCG_FLAG_OP_SRAWI:
ceriond953ebb2005-11-29 13:27:20 +00001868 /* xer_ca is 1 iff src was negative and bits_shifted_out !=
1869 0. Since the shift amount is known to be in the range
1870 0 .. 31 inclusive the following seems viable:
1871 xer.ca == 1 iff the following is nonzero:
1872 (argL >>s 31) -- either all 0s or all 1s
1873 & (argL & (1<<argR)-1) -- the stuff shifted out */
1874 xer_ca
1875 = binop(
1876 Iop_And32,
1877 binop(Iop_Sar32, argL, mkU8(31)),
1878 binop( Iop_And32,
1879 argL,
1880 binop( Iop_Sub32,
cerion5b2325f2005-12-23 00:55:09 +00001881 binop(Iop_Shl32, mkU32(1),
1882 unop(Iop_32to8,argR)),
ceriond953ebb2005-11-29 13:27:20 +00001883 mkU32(1) )
1884 )
sewardj20ef5472005-07-21 14:48:31 +00001885 );
ceriond953ebb2005-11-29 13:27:20 +00001886 xer_ca
1887 = unop(Iop_1Uto32, binop(Iop_CmpNE32, xer_ca, mkU32(0)));
1888 break;
1889
1890 default:
1891 vex_printf("set_XER_CA: op = %u\n", op);
cerion5b2325f2005-12-23 00:55:09 +00001892 vpanic("set_XER_CA(ppc)");
sewardj20ef5472005-07-21 14:48:31 +00001893 }
1894
1895 /* xer_ca MUST denote either 0 or 1, no other value allowed */
cerionedf7fc52005-11-18 20:57:41 +00001896 putXER_CA( unop(Iop_32to8, xer_ca) );
sewardjb51f0f42005-07-18 11:38:02 +00001897}
1898
cerion2831b002005-11-30 19:55:22 +00001899static void set_XER_CA_64 ( UInt op, IRExpr* res,
1900 IRExpr* argL, IRExpr* argR, IRExpr* oldca )
sewardje14bb9f2005-07-22 09:39:02 +00001901{
ceriond953ebb2005-11-29 13:27:20 +00001902 IRExpr* xer_ca;
cerion5b2325f2005-12-23 00:55:09 +00001903 vassert(op < PPCG_FLAG_OP_NUMBER);
sewardjdd40fdf2006-12-24 02:20:24 +00001904 vassert(typeOfIRExpr(irsb->tyenv,res) == Ity_I64);
1905 vassert(typeOfIRExpr(irsb->tyenv,argL) == Ity_I64);
1906 vassert(typeOfIRExpr(irsb->tyenv,argR) == Ity_I64);
1907 vassert(typeOfIRExpr(irsb->tyenv,oldca) == Ity_I64);
sewardje14bb9f2005-07-22 09:39:02 +00001908
ceriond953ebb2005-11-29 13:27:20 +00001909 /* Incoming oldca is assumed to hold the values 0 or 1 only. This
1910 seems reasonable given that it's always generated by
1911 getXER_CA32(), which masks it accordingly. In any case it being
cerion75949202005-12-24 13:14:11 +00001912 0 or 1 is an invariant of the ppc guest state representation;
ceriond953ebb2005-11-29 13:27:20 +00001913 if it has any other value, that invariant has been violated. */
1914
1915 switch (op) {
cerion5b2325f2005-12-23 00:55:09 +00001916 case /* 0 */ PPCG_FLAG_OP_ADD:
ceriond953ebb2005-11-29 13:27:20 +00001917 /* res <u argL */
1918 xer_ca
cerionf0de28c2005-12-13 20:21:11 +00001919 = unop(Iop_1Uto32, binop(Iop_CmpLT64U, res, argL));
ceriond953ebb2005-11-29 13:27:20 +00001920 break;
sewardje14bb9f2005-07-22 09:39:02 +00001921
cerion5b2325f2005-12-23 00:55:09 +00001922 case /* 1 */ PPCG_FLAG_OP_ADDE:
ceriond953ebb2005-11-29 13:27:20 +00001923 /* res <u argL || (old_ca==1 && res==argL) */
1924 xer_ca
1925 = mkOR1(
1926 binop(Iop_CmpLT64U, res, argL),
1927 mkAND1(
cerionf0de28c2005-12-13 20:21:11 +00001928 binop(Iop_CmpEQ64, oldca, mkU64(1)),
ceriond953ebb2005-11-29 13:27:20 +00001929 binop(Iop_CmpEQ64, res, argL)
1930 )
1931 );
cerionf0de28c2005-12-13 20:21:11 +00001932 xer_ca
1933 = unop(Iop_1Uto32, xer_ca);
cerionedf7fc52005-11-18 20:57:41 +00001934 break;
ceriond953ebb2005-11-29 13:27:20 +00001935
cerion5b2325f2005-12-23 00:55:09 +00001936 case /* 8 */ PPCG_FLAG_OP_SUBFE:
ceriond953ebb2005-11-29 13:27:20 +00001937 /* res <u argR || (old_ca==1 && res==argR) */
1938 xer_ca
1939 = mkOR1(
1940 binop(Iop_CmpLT64U, res, argR),
1941 mkAND1(
1942 binop(Iop_CmpEQ64, oldca, mkU64(1)),
1943 binop(Iop_CmpEQ64, res, argR)
1944 )
1945 );
cerionf0de28c2005-12-13 20:21:11 +00001946 xer_ca
1947 = unop(Iop_1Uto32, xer_ca);
ceriond953ebb2005-11-29 13:27:20 +00001948 break;
1949
cerion5b2325f2005-12-23 00:55:09 +00001950 case /* 7 */ PPCG_FLAG_OP_SUBFC:
1951 case /* 9 */ PPCG_FLAG_OP_SUBFI:
ceriond953ebb2005-11-29 13:27:20 +00001952 /* res <=u argR */
1953 xer_ca
cerionf0de28c2005-12-13 20:21:11 +00001954 = unop(Iop_1Uto32, binop(Iop_CmpLE64U, res, argR));
ceriond953ebb2005-11-29 13:27:20 +00001955 break;
1956
1957
cerion5b2325f2005-12-23 00:55:09 +00001958 case /* 10 */ PPCG_FLAG_OP_SRAW:
cerionf0de28c2005-12-13 20:21:11 +00001959 /* The shift amount is guaranteed to be in 0 .. 31 inclusive.
ceriond953ebb2005-11-29 13:27:20 +00001960 If it is <= 31, behave like SRAWI; else XER.CA is the sign
1961 bit of argL. */
cerionf0de28c2005-12-13 20:21:11 +00001962 /* This term valid for shift amount < 31 only */
1963
ceriond953ebb2005-11-29 13:27:20 +00001964 xer_ca
1965 = binop(
cerionf0de28c2005-12-13 20:21:11 +00001966 Iop_And64,
1967 binop(Iop_Sar64, argL, mkU8(31)),
1968 binop( Iop_And64,
ceriond953ebb2005-11-29 13:27:20 +00001969 argL,
cerionf0de28c2005-12-13 20:21:11 +00001970 binop( Iop_Sub64,
cerion5b2325f2005-12-23 00:55:09 +00001971 binop(Iop_Shl64, mkU64(1),
1972 unop(Iop_64to8,argR)),
cerionf0de28c2005-12-13 20:21:11 +00001973 mkU64(1) )
ceriond953ebb2005-11-29 13:27:20 +00001974 )
1975 );
1976 xer_ca
1977 = IRExpr_Mux0X(
1978 /* shift amt > 31 ? */
cerionf0de28c2005-12-13 20:21:11 +00001979 unop(Iop_1Uto8, binop(Iop_CmpLT64U, mkU64(31), argR)),
ceriond953ebb2005-11-29 13:27:20 +00001980 /* no -- be like srawi */
cerionf0de28c2005-12-13 20:21:11 +00001981 unop(Iop_1Uto32, binop(Iop_CmpNE64, xer_ca, mkU64(0))),
ceriond953ebb2005-11-29 13:27:20 +00001982 /* yes -- get sign bit of argL */
cerionf0de28c2005-12-13 20:21:11 +00001983 unop(Iop_64to32, binop(Iop_Shr64, argL, mkU8(63)))
ceriond953ebb2005-11-29 13:27:20 +00001984 );
1985 break;
1986
cerion5b2325f2005-12-23 00:55:09 +00001987 case /* 11 */ PPCG_FLAG_OP_SRAWI:
cerionf0de28c2005-12-13 20:21:11 +00001988 /* xer_ca is 1 iff src was negative and bits_shifted_out != 0.
1989 Since the shift amount is known to be in the range 0 .. 31
1990 inclusive the following seems viable:
ceriond953ebb2005-11-29 13:27:20 +00001991 xer.ca == 1 iff the following is nonzero:
1992 (argL >>s 31) -- either all 0s or all 1s
1993 & (argL & (1<<argR)-1) -- the stuff shifted out */
cerionf0de28c2005-12-13 20:21:11 +00001994
ceriond953ebb2005-11-29 13:27:20 +00001995 xer_ca
1996 = binop(
cerionf0de28c2005-12-13 20:21:11 +00001997 Iop_And64,
1998 binop(Iop_Sar64, argL, mkU8(31)),
1999 binop( Iop_And64,
ceriond953ebb2005-11-29 13:27:20 +00002000 argL,
cerionf0de28c2005-12-13 20:21:11 +00002001 binop( Iop_Sub64,
cerion5b2325f2005-12-23 00:55:09 +00002002 binop(Iop_Shl64, mkU64(1),
2003 unop(Iop_64to8,argR)),
cerionf0de28c2005-12-13 20:21:11 +00002004 mkU64(1) )
ceriond953ebb2005-11-29 13:27:20 +00002005 )
2006 );
2007 xer_ca
cerionf0de28c2005-12-13 20:21:11 +00002008 = unop(Iop_1Uto32, binop(Iop_CmpNE64, xer_ca, mkU64(0)));
ceriond953ebb2005-11-29 13:27:20 +00002009 break;
ceriond953ebb2005-11-29 13:27:20 +00002010
cerionf0de28c2005-12-13 20:21:11 +00002011
cerion5b2325f2005-12-23 00:55:09 +00002012 case /* 12 */ PPCG_FLAG_OP_SRAD:
cerionf0de28c2005-12-13 20:21:11 +00002013 /* The shift amount is guaranteed to be in 0 .. 63 inclusive.
2014 If it is <= 63, behave like SRADI; else XER.CA is the sign
2015 bit of argL. */
2016 /* This term valid for shift amount < 63 only */
2017
2018 xer_ca
2019 = binop(
2020 Iop_And64,
2021 binop(Iop_Sar64, argL, mkU8(63)),
2022 binop( Iop_And64,
2023 argL,
2024 binop( Iop_Sub64,
cerion5b2325f2005-12-23 00:55:09 +00002025 binop(Iop_Shl64, mkU64(1),
2026 unop(Iop_64to8,argR)),
cerionf0de28c2005-12-13 20:21:11 +00002027 mkU64(1) )
2028 )
2029 );
2030 xer_ca
2031 = IRExpr_Mux0X(
2032 /* shift amt > 63 ? */
2033 unop(Iop_1Uto8, binop(Iop_CmpLT64U, mkU64(63), argR)),
cerion07b07a92005-12-22 14:32:35 +00002034 /* no -- be like sradi */
2035 unop(Iop_1Uto32, binop(Iop_CmpNE64, xer_ca, mkU64(0))),
cerionf0de28c2005-12-13 20:21:11 +00002036 /* yes -- get sign bit of argL */
2037 unop(Iop_64to32, binop(Iop_Shr64, argL, mkU8(63)))
2038 );
2039 break;
2040
2041
cerion5b2325f2005-12-23 00:55:09 +00002042 case /* 13 */ PPCG_FLAG_OP_SRADI:
cerionf0de28c2005-12-13 20:21:11 +00002043 /* xer_ca is 1 iff src was negative and bits_shifted_out != 0.
2044 Since the shift amount is known to be in the range 0 .. 63
2045 inclusive, the following seems viable:
2046 xer.ca == 1 iff the following is nonzero:
2047 (argL >>s 63) -- either all 0s or all 1s
2048 & (argL & (1<<argR)-1) -- the stuff shifted out */
2049
2050 xer_ca
2051 = binop(
2052 Iop_And64,
2053 binop(Iop_Sar64, argL, mkU8(63)),
2054 binop( Iop_And64,
2055 argL,
2056 binop( Iop_Sub64,
cerion5b2325f2005-12-23 00:55:09 +00002057 binop(Iop_Shl64, mkU64(1),
2058 unop(Iop_64to8,argR)),
cerionf0de28c2005-12-13 20:21:11 +00002059 mkU64(1) )
2060 )
2061 );
2062 xer_ca
2063 = unop(Iop_1Uto32, binop(Iop_CmpNE64, xer_ca, mkU64(0)));
2064 break;
2065
ceriond953ebb2005-11-29 13:27:20 +00002066 default:
2067 vex_printf("set_XER_CA: op = %u\n", op);
cerionf0de28c2005-12-13 20:21:11 +00002068 vpanic("set_XER_CA(ppc64)");
sewardje14bb9f2005-07-22 09:39:02 +00002069 }
2070
ceriond953ebb2005-11-29 13:27:20 +00002071 /* xer_ca MUST denote either 0 or 1, no other value allowed */
cerionf0de28c2005-12-13 20:21:11 +00002072 putXER_CA( unop(Iop_32to8, xer_ca) );
sewardje14bb9f2005-07-22 09:39:02 +00002073}
2074
cerion2831b002005-11-30 19:55:22 +00002075static void set_XER_CA ( IRType ty, UInt op, IRExpr* res,
2076 IRExpr* argL, IRExpr* argR, IRExpr* oldca )
cerionedf7fc52005-11-18 20:57:41 +00002077{
cerion2831b002005-11-30 19:55:22 +00002078 if (ty == Ity_I32)
ceriond953ebb2005-11-29 13:27:20 +00002079 set_XER_CA_32( op, res, argL, argR, oldca );
2080 else
2081 set_XER_CA_64( op, res, argL, argR, oldca );
cerionedf7fc52005-11-18 20:57:41 +00002082}
2083
ceriond953ebb2005-11-29 13:27:20 +00002084
2085
2086/*------------------------------------------------------------*/
2087/*--- Read/write to guest-state --- */
2088/*------------------------------------------------------------*/
2089
cerionf0de28c2005-12-13 20:21:11 +00002090static IRExpr* /* :: Ity_I32/64 */ getGST ( PPC_GST reg )
cerionedf7fc52005-11-18 20:57:41 +00002091{
ceriond953ebb2005-11-29 13:27:20 +00002092 IRType ty = mode64 ? Ity_I64 : Ity_I32;
cerionedf7fc52005-11-18 20:57:41 +00002093 switch (reg) {
sewardjaca070a2006-10-17 00:28:22 +00002094 case PPC_GST_SPRG3_RO:
2095 return IRExpr_Get( OFFB_SPRG3_RO, ty );
2096
2097 case PPC_GST_CIA:
2098 return IRExpr_Get( OFFB_CIA, ty );
2099
ceriond953ebb2005-11-29 13:27:20 +00002100 case PPC_GST_LR:
cerion5b2325f2005-12-23 00:55:09 +00002101 return IRExpr_Get( OFFB_LR, ty );
ceriond953ebb2005-11-29 13:27:20 +00002102
2103 case PPC_GST_CTR:
cerion5b2325f2005-12-23 00:55:09 +00002104 return IRExpr_Get( OFFB_CTR, ty );
ceriond953ebb2005-11-29 13:27:20 +00002105
2106 case PPC_GST_VRSAVE:
cerion5b2325f2005-12-23 00:55:09 +00002107 return IRExpr_Get( OFFB_VRSAVE, Ity_I32 );
ceriond953ebb2005-11-29 13:27:20 +00002108
2109 case PPC_GST_VSCR:
cerion5b2325f2005-12-23 00:55:09 +00002110 return binop(Iop_And32, IRExpr_Get( OFFB_VSCR,Ity_I32 ),
2111 mkU32(MASK_VSCR_VALID));
ceriond953ebb2005-11-29 13:27:20 +00002112
2113 case PPC_GST_CR: {
cerionedf7fc52005-11-18 20:57:41 +00002114 /* Synthesise the entire CR into a single word. Expensive. */
2115# define FIELD(_n) \
2116 binop(Iop_Shl32, \
2117 unop(Iop_8Uto32, \
2118 binop(Iop_Or8, \
2119 binop(Iop_And8, getCR321(_n), mkU8(7<<1)), \
2120 binop(Iop_And8, getCR0(_n), mkU8(1)) \
2121 ) \
2122 ), \
2123 mkU8(4 * (7-(_n))) \
2124 )
2125 return binop(Iop_Or32,
2126 binop(Iop_Or32,
2127 binop(Iop_Or32, FIELD(0), FIELD(1)),
2128 binop(Iop_Or32, FIELD(2), FIELD(3))
2129 ),
2130 binop(Iop_Or32,
2131 binop(Iop_Or32, FIELD(4), FIELD(5)),
2132 binop(Iop_Or32, FIELD(6), FIELD(7))
2133 )
2134 );
2135# undef FIELD
2136 }
ceriond953ebb2005-11-29 13:27:20 +00002137
2138 case PPC_GST_XER:
cerionedf7fc52005-11-18 20:57:41 +00002139 return binop(Iop_Or32,
2140 binop(Iop_Or32,
2141 binop( Iop_Shl32, getXER_SO32(), mkU8(31)),
2142 binop( Iop_Shl32, getXER_OV32(), mkU8(30))),
2143 binop(Iop_Or32,
2144 binop( Iop_Shl32, getXER_CA32(), mkU8(29)),
2145 getXER_BC32()));
ceriond953ebb2005-11-29 13:27:20 +00002146
cerionedf7fc52005-11-18 20:57:41 +00002147 default:
cerion5b2325f2005-12-23 00:55:09 +00002148 vex_printf("getGST(ppc): reg = %u", reg);
2149 vpanic("getGST(ppc)");
ceriond953ebb2005-11-29 13:27:20 +00002150 }
2151}
2152
2153/* Get a masked word from the given reg */
2154static IRExpr* /* ::Ity_I32 */ getGST_masked ( PPC_GST reg, UInt mask )
2155{
2156 IRTemp val = newTemp(Ity_I32);
2157 vassert( reg < PPC_GST_MAX );
2158
2159 switch (reg) {
2160
2161 case PPC_GST_FPSCR: {
cerion5b2325f2005-12-23 00:55:09 +00002162 /* Vex-generated code expects the FPSCR to be set as follows:
ceriond953ebb2005-11-29 13:27:20 +00002163 all exceptions masked, round-to-nearest.
2164 This corresponds to a FPSCR value of 0x0. */
2165
2166 /* We're only keeping track of the rounding mode,
2167 so if the mask isn't asking for this, just return 0x0 */
2168 if (mask & 0x3) {
cerion5b2325f2005-12-23 00:55:09 +00002169 assign( val, IRExpr_Get( OFFB_FPROUND, Ity_I32 ) );
ceriond953ebb2005-11-29 13:27:20 +00002170 } else {
2171 assign( val, mkU32(0x0) );
2172 }
2173 break;
2174 }
2175
2176 default:
cerion5b2325f2005-12-23 00:55:09 +00002177 vex_printf("getGST_masked(ppc): reg = %u", reg);
2178 vpanic("getGST_masked(ppc)");
ceriond953ebb2005-11-29 13:27:20 +00002179 }
2180
2181 if (mask != 0xFFFFFFFF) {
2182 return binop(Iop_And32, mkexpr(val), mkU32(mask));
2183 } else {
2184 return mkexpr(val);
2185 }
2186}
2187
2188/* Fetch the specified REG[FLD] nibble (as per IBM/hardware notation)
2189 and return it at the bottom of an I32; the top 27 bits are
2190 guaranteed to be zero. */
2191static IRExpr* /* ::Ity_I32 */ getGST_field ( PPC_GST reg, UInt fld )
2192{
2193 UInt shft, mask;
2194
2195 vassert( fld < 8 );
2196 vassert( reg < PPC_GST_MAX );
2197
2198 shft = 4*(7-fld);
2199 mask = 0xF<<shft;
2200
2201 switch (reg) {
2202 case PPC_GST_XER:
2203 vassert(fld ==7);
2204 return binop(Iop_Or32,
2205 binop(Iop_Or32,
2206 binop(Iop_Shl32, getXER_SO32(), mkU8(3)),
2207 binop(Iop_Shl32, getXER_OV32(), mkU8(2))),
2208 binop( Iop_Shl32, getXER_CA32(), mkU8(1)));
2209 break;
2210
2211 default:
2212 if (shft == 0)
2213 return getGST_masked( reg, mask );
2214 else
2215 return binop(Iop_Shr32,
2216 getGST_masked( reg, mask ),
2217 mkU8(toUChar( shft )));
2218 }
2219}
2220
2221static void putGST ( PPC_GST reg, IRExpr* src )
2222{
cerion5b2325f2005-12-23 00:55:09 +00002223 IRType ty = mode64 ? Ity_I64 : Ity_I32;
sewardjdd40fdf2006-12-24 02:20:24 +00002224 IRType ty_src = typeOfIRExpr(irsb->tyenv,src );
ceriond953ebb2005-11-29 13:27:20 +00002225 vassert( reg < PPC_GST_MAX );
2226 switch (reg) {
sewardje86310f2009-03-19 22:21:40 +00002227 case PPC_GST_IP_AT_SYSCALL:
sewardjaca070a2006-10-17 00:28:22 +00002228 vassert( ty_src == ty );
sewardje86310f2009-03-19 22:21:40 +00002229 stmt( IRStmt_Put( OFFB_IP_AT_SYSCALL, src ) );
sewardjaca070a2006-10-17 00:28:22 +00002230 break;
ceriond953ebb2005-11-29 13:27:20 +00002231 case PPC_GST_CIA:
cerion5b2325f2005-12-23 00:55:09 +00002232 vassert( ty_src == ty );
2233 stmt( IRStmt_Put( OFFB_CIA, src ) );
ceriond953ebb2005-11-29 13:27:20 +00002234 break;
2235 case PPC_GST_LR:
cerion5b2325f2005-12-23 00:55:09 +00002236 vassert( ty_src == ty );
2237 stmt( IRStmt_Put( OFFB_LR, src ) );
ceriond953ebb2005-11-29 13:27:20 +00002238 break;
2239 case PPC_GST_CTR:
cerion5b2325f2005-12-23 00:55:09 +00002240 vassert( ty_src == ty );
2241 stmt( IRStmt_Put( OFFB_CTR, src ) );
ceriond953ebb2005-11-29 13:27:20 +00002242 break;
2243 case PPC_GST_VRSAVE:
cerion5b2325f2005-12-23 00:55:09 +00002244 vassert( ty_src == Ity_I32 );
2245 stmt( IRStmt_Put( OFFB_VRSAVE,src));
ceriond953ebb2005-11-29 13:27:20 +00002246 break;
2247 case PPC_GST_VSCR:
cerion5b2325f2005-12-23 00:55:09 +00002248 vassert( ty_src == Ity_I32 );
2249 stmt( IRStmt_Put( OFFB_VSCR,
ceriond953ebb2005-11-29 13:27:20 +00002250 binop(Iop_And32, src,
2251 mkU32(MASK_VSCR_VALID)) ) );
2252 break;
2253 case PPC_GST_XER:
cerion5b2325f2005-12-23 00:55:09 +00002254 vassert( ty_src == Ity_I32 );
ceriond953ebb2005-11-29 13:27:20 +00002255 putXER_SO( unop(Iop_32to8, binop(Iop_Shr32, src, mkU8(31))) );
2256 putXER_OV( unop(Iop_32to8, binop(Iop_Shr32, src, mkU8(30))) );
2257 putXER_CA( unop(Iop_32to8, binop(Iop_Shr32, src, mkU8(29))) );
2258 putXER_BC( unop(Iop_32to8, src) );
2259 break;
2260
2261 case PPC_GST_EMWARN:
cerion5b2325f2005-12-23 00:55:09 +00002262 vassert( ty_src == Ity_I32 );
2263 stmt( IRStmt_Put( OFFB_EMWARN,src) );
ceriond953ebb2005-11-29 13:27:20 +00002264 break;
2265
2266 case PPC_GST_TISTART:
cerion5b2325f2005-12-23 00:55:09 +00002267 vassert( ty_src == ty );
2268 stmt( IRStmt_Put( OFFB_TISTART, src) );
ceriond953ebb2005-11-29 13:27:20 +00002269 break;
2270
2271 case PPC_GST_TILEN:
cerion5b2325f2005-12-23 00:55:09 +00002272 vassert( ty_src == ty );
2273 stmt( IRStmt_Put( OFFB_TILEN, src) );
ceriond953ebb2005-11-29 13:27:20 +00002274 break;
2275
ceriond953ebb2005-11-29 13:27:20 +00002276 default:
cerion5b2325f2005-12-23 00:55:09 +00002277 vex_printf("putGST(ppc): reg = %u", reg);
2278 vpanic("putGST(ppc)");
cerionedf7fc52005-11-18 20:57:41 +00002279 }
2280}
sewardje14bb9f2005-07-22 09:39:02 +00002281
2282/* Write masked src to the given reg */
ceriond953ebb2005-11-29 13:27:20 +00002283static void putGST_masked ( PPC_GST reg, IRExpr* src, UInt mask )
sewardje14bb9f2005-07-22 09:39:02 +00002284{
ceriond953ebb2005-11-29 13:27:20 +00002285 IRType ty = mode64 ? Ity_I64 : Ity_I32;
2286 vassert( reg < PPC_GST_MAX );
sewardjdd40fdf2006-12-24 02:20:24 +00002287 vassert( typeOfIRExpr(irsb->tyenv,src ) == Ity_I32 );
sewardje14bb9f2005-07-22 09:39:02 +00002288
2289 switch (reg) {
ceriond953ebb2005-11-29 13:27:20 +00002290 case PPC_GST_FPSCR: {
sewardje14bb9f2005-07-22 09:39:02 +00002291 /* Allow writes to Rounding Mode */
2292 if (mask & 0x3) {
sewardjb183b852006-02-03 16:08:03 +00002293 /* construct new fpround from new and old values as per mask:
2294 new fpround = (src & (3 & mask)) | (fpround & (3 & ~mask)) */
2295 stmt(
2296 IRStmt_Put(
2297 OFFB_FPROUND,
2298 binop(
2299 Iop_Or32,
2300 binop(Iop_And32, src, mkU32(3 & mask)),
2301 binop(
2302 Iop_And32,
2303 IRExpr_Get(OFFB_FPROUND,Ity_I32),
2304 mkU32(3 & ~mask)
2305 )
2306 )
2307 )
2308 );
sewardje14bb9f2005-07-22 09:39:02 +00002309 }
2310
cerionedf7fc52005-11-18 20:57:41 +00002311 /* Give EmWarn for attempted writes to:
sewardje14bb9f2005-07-22 09:39:02 +00002312 - Exception Controls
2313 - Non-IEEE Mode
2314 */
2315 if (mask & 0xFC) { // Exception Control, Non-IEE mode
sewardj5ff11dd2006-01-20 14:19:25 +00002316 VexEmWarn ew = EmWarn_PPCexns;
sewardje14bb9f2005-07-22 09:39:02 +00002317
2318 /* If any of the src::exception_control bits are actually set,
2319 side-exit to the next insn, reporting the warning,
2320 so that Valgrind's dispatcher sees the warning. */
ceriond953ebb2005-11-29 13:27:20 +00002321 putGST( PPC_GST_EMWARN, mkU32(ew) );
sewardje14bb9f2005-07-22 09:39:02 +00002322 stmt(
2323 IRStmt_Exit(
2324 binop(Iop_CmpNE32, mkU32(ew), mkU32(EmWarn_NONE)),
2325 Ijk_EmWarn,
cerion2831b002005-11-30 19:55:22 +00002326 mkSzConst( ty, nextInsnAddr()) ));
sewardje14bb9f2005-07-22 09:39:02 +00002327 }
2328
cerionedf7fc52005-11-18 20:57:41 +00002329 /* Ignore all other writes */
sewardje14bb9f2005-07-22 09:39:02 +00002330 break;
cerionedf7fc52005-11-18 20:57:41 +00002331 }
sewardje14bb9f2005-07-22 09:39:02 +00002332
2333 default:
cerion5b2325f2005-12-23 00:55:09 +00002334 vex_printf("putGST_masked(ppc): reg = %u", reg);
2335 vpanic("putGST_masked(ppc)");
sewardje14bb9f2005-07-22 09:39:02 +00002336 }
2337}
2338
cerionedf7fc52005-11-18 20:57:41 +00002339/* Write the least significant nibble of src to the specified
2340 REG[FLD] (as per IBM/hardware notation). */
ceriond953ebb2005-11-29 13:27:20 +00002341static void putGST_field ( PPC_GST reg, IRExpr* src, UInt fld )
cerionedf7fc52005-11-18 20:57:41 +00002342{
sewardj41a7b702005-11-18 22:18:23 +00002343 UInt shft, mask;
2344
sewardjdd40fdf2006-12-24 02:20:24 +00002345 vassert( typeOfIRExpr(irsb->tyenv,src ) == Ity_I32 );
cerionedf7fc52005-11-18 20:57:41 +00002346 vassert( fld < 8 );
ceriond953ebb2005-11-29 13:27:20 +00002347 vassert( reg < PPC_GST_MAX );
cerionedf7fc52005-11-18 20:57:41 +00002348
sewardj41a7b702005-11-18 22:18:23 +00002349 shft = 4*(7-fld);
2350 mask = 0xF<<shft;
cerionedf7fc52005-11-18 20:57:41 +00002351
2352 switch (reg) {
ceriond953ebb2005-11-29 13:27:20 +00002353 case PPC_GST_CR:
cerionedf7fc52005-11-18 20:57:41 +00002354 putCR0 (fld, binop(Iop_And8, mkU8(1 ), unop(Iop_32to8, src)));
2355 putCR321(fld, binop(Iop_And8, mkU8(7<<1), unop(Iop_32to8, src)));
2356 break;
2357
2358 default:
2359 if (shft == 0) {
ceriond953ebb2005-11-29 13:27:20 +00002360 putGST_masked( reg, src, mask );
cerionedf7fc52005-11-18 20:57:41 +00002361 } else {
ceriond953ebb2005-11-29 13:27:20 +00002362 putGST_masked( reg,
cerionedf7fc52005-11-18 20:57:41 +00002363 binop(Iop_Shl32, src, mkU8(toUChar(shft))),
2364 mask );
2365 }
2366 }
2367}
cerion62bec572005-02-01 21:29:39 +00002368
cerion76222262005-02-05 13:45:57 +00002369
2370
cerione9d361a2005-03-04 17:35:29 +00002371/*------------------------------------------------------------*/
cerion3d870a32005-03-18 12:23:33 +00002372/*--- Integer Instruction Translation --- */
cerione9d361a2005-03-04 17:35:29 +00002373/*------------------------------------------------------------*/
cerion896a1372005-01-25 12:24:25 +00002374
cerion91ad5362005-01-27 23:02:41 +00002375/*
2376 Integer Arithmetic Instructions
2377*/
cerion645c9302005-01-31 10:09:59 +00002378static Bool dis_int_arith ( UInt theInstr )
cerion91ad5362005-01-27 23:02:41 +00002379{
cerion76de5cf2005-11-18 18:25:12 +00002380 /* D-Form, XO-Form */
2381 UChar opc1 = ifieldOPC(theInstr);
2382 UChar rD_addr = ifieldRegDS(theInstr);
2383 UChar rA_addr = ifieldRegA(theInstr);
ceriond953ebb2005-11-29 13:27:20 +00002384 UInt uimm16 = ifieldUIMM16(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00002385 UChar rB_addr = ifieldRegB(theInstr);
2386 UChar flag_OE = ifieldBIT10(theInstr);
2387 UInt opc2 = ifieldOPClo9(theInstr);
2388 UChar flag_rC = ifieldBIT0(theInstr);
2389
ceriond953ebb2005-11-29 13:27:20 +00002390 Long simm16 = extend_s_16to64(uimm16);
2391 IRType ty = mode64 ? Ity_I64 : Ity_I32;
2392 IRTemp rA = newTemp(ty);
2393 IRTemp rB = newTemp(ty);
2394 IRTemp rD = newTemp(ty);
cerion70e24122005-03-16 00:27:37 +00002395
cerionb85e8bb2005-02-16 08:54:33 +00002396 Bool do_rc = False;
cerion91ad5362005-01-27 23:02:41 +00002397
cerion76de5cf2005-11-18 18:25:12 +00002398 assign( rA, getIReg(rA_addr) );
2399 assign( rB, getIReg(rB_addr) ); // XO-Form: rD, rA, rB
sewardjb51f0f42005-07-18 11:38:02 +00002400
cerionb85e8bb2005-02-16 08:54:33 +00002401 switch (opc1) {
cerionb85e8bb2005-02-16 08:54:33 +00002402 /* D-Form */
cerione9d361a2005-03-04 17:35:29 +00002403 case 0x0C: // addic (Add Immediate Carrying, PPC32 p351
ceriond953ebb2005-11-29 13:27:20 +00002404 DIP("addic r%u,r%u,%d\n", rD_addr, rA_addr, (Int)simm16);
cerion2831b002005-11-30 19:55:22 +00002405 assign( rD, binop( mkSzOp(ty, Iop_Add8), mkexpr(rA),
2406 mkSzExtendS16(ty, uimm16) ) );
cerion5b2325f2005-12-23 00:55:09 +00002407 set_XER_CA( ty, PPCG_FLAG_OP_ADD,
cerion2831b002005-11-30 19:55:22 +00002408 mkexpr(rD), mkexpr(rA), mkSzExtendS16(ty, uimm16),
2409 mkSzImm(ty, 0)/*old xer.ca, which is ignored*/ );
cerion4561acb2005-02-21 14:07:48 +00002410 break;
sewardjb51f0f42005-07-18 11:38:02 +00002411
cerione9d361a2005-03-04 17:35:29 +00002412 case 0x0D: // addic. (Add Immediate Carrying and Record, PPC32 p352)
ceriond953ebb2005-11-29 13:27:20 +00002413 DIP("addic. r%u,r%u,%d\n", rD_addr, rA_addr, (Int)simm16);
cerion2831b002005-11-30 19:55:22 +00002414 assign( rD, binop( mkSzOp(ty, Iop_Add8), mkexpr(rA),
2415 mkSzExtendS16(ty, uimm16) ) );
cerion5b2325f2005-12-23 00:55:09 +00002416 set_XER_CA( ty, PPCG_FLAG_OP_ADD,
cerion2831b002005-11-30 19:55:22 +00002417 mkexpr(rD), mkexpr(rA), mkSzExtendS16(ty, uimm16),
2418 mkSzImm(ty, 0)/*old xer.ca, which is ignored*/ );
cerion70e24122005-03-16 00:27:37 +00002419 do_rc = True; // Always record to CR
cerion76de5cf2005-11-18 18:25:12 +00002420 flag_rC = 1;
cerion4561acb2005-02-21 14:07:48 +00002421 break;
2422
cerione9d361a2005-03-04 17:35:29 +00002423 case 0x0E: // addi (Add Immediate, PPC32 p350)
cerionb85e8bb2005-02-16 08:54:33 +00002424 // li rD,val == addi rD,0,val
2425 // la disp(rA) == addi rD,rA,disp
cerion76de5cf2005-11-18 18:25:12 +00002426 if ( rA_addr == 0 ) {
ceriond953ebb2005-11-29 13:27:20 +00002427 DIP("li r%u,%d\n", rD_addr, (Int)simm16);
cerion2831b002005-11-30 19:55:22 +00002428 assign( rD, mkSzExtendS16(ty, uimm16) );
cerionb85e8bb2005-02-16 08:54:33 +00002429 } else {
ceriond953ebb2005-11-29 13:27:20 +00002430 DIP("addi r%u,r%u,%d\n", rD_addr, rA_addr, (Int)simm16);
cerion2831b002005-11-30 19:55:22 +00002431 assign( rD, binop( mkSzOp(ty, Iop_Add8), mkexpr(rA),
2432 mkSzExtendS16(ty, uimm16) ) );
cerionb85e8bb2005-02-16 08:54:33 +00002433 }
2434 break;
cerion91ad5362005-01-27 23:02:41 +00002435
cerione9d361a2005-03-04 17:35:29 +00002436 case 0x0F: // addis (Add Immediate Shifted, PPC32 p353)
cerionb85e8bb2005-02-16 08:54:33 +00002437 // lis rD,val == addis rD,0,val
cerion76de5cf2005-11-18 18:25:12 +00002438 if ( rA_addr == 0 ) {
ceriond953ebb2005-11-29 13:27:20 +00002439 DIP("lis r%u,%d\n", rD_addr, (Int)simm16);
cerion2831b002005-11-30 19:55:22 +00002440 assign( rD, mkSzExtendS32(ty, uimm16 << 16) );
cerionb85e8bb2005-02-16 08:54:33 +00002441 } else {
ceriond953ebb2005-11-29 13:27:20 +00002442 DIP("addis r%u,r%u,0x%x\n", rD_addr, rA_addr, (Int)simm16);
cerion2831b002005-11-30 19:55:22 +00002443 assign( rD, binop( mkSzOp(ty, Iop_Add8), mkexpr(rA),
2444 mkSzExtendS32(ty, uimm16 << 16) ) );
cerionb85e8bb2005-02-16 08:54:33 +00002445 }
2446 break;
cerion91ad5362005-01-27 23:02:41 +00002447
cerione9d361a2005-03-04 17:35:29 +00002448 case 0x07: // mulli (Multiply Low Immediate, PPC32 p490)
ceriond953ebb2005-11-29 13:27:20 +00002449 DIP("mulli r%u,r%u,%d\n", rD_addr, rA_addr, (Int)simm16);
2450 if (mode64)
2451 assign( rD, unop(Iop_128to64,
2452 binop(Iop_MullS64, mkexpr(rA),
cerion2831b002005-11-30 19:55:22 +00002453 mkSzExtendS16(ty, uimm16))) );
ceriond953ebb2005-11-29 13:27:20 +00002454 else
2455 assign( rD, unop(Iop_64to32,
2456 binop(Iop_MullS32, mkexpr(rA),
cerion2831b002005-11-30 19:55:22 +00002457 mkSzExtendS16(ty, uimm16))) );
cerionb85e8bb2005-02-16 08:54:33 +00002458 break;
cerion38674602005-02-08 02:19:25 +00002459
cerione9d361a2005-03-04 17:35:29 +00002460 case 0x08: // subfic (Subtract from Immediate Carrying, PPC32 p540)
ceriond953ebb2005-11-29 13:27:20 +00002461 DIP("subfic r%u,r%u,%d\n", rD_addr, rA_addr, (Int)simm16);
cerion76de5cf2005-11-18 18:25:12 +00002462 // rD = simm16 - rA
cerion2831b002005-11-30 19:55:22 +00002463 assign( rD, binop( mkSzOp(ty, Iop_Sub8),
2464 mkSzExtendS16(ty, uimm16),
ceriond953ebb2005-11-29 13:27:20 +00002465 mkexpr(rA)) );
cerion5b2325f2005-12-23 00:55:09 +00002466 set_XER_CA( ty, PPCG_FLAG_OP_SUBFI,
cerion2831b002005-11-30 19:55:22 +00002467 mkexpr(rD), mkexpr(rA), mkSzExtendS16(ty, uimm16),
2468 mkSzImm(ty, 0)/*old xer.ca, which is ignored*/ );
cerionb85e8bb2005-02-16 08:54:33 +00002469 break;
cerion38674602005-02-08 02:19:25 +00002470
cerionb85e8bb2005-02-16 08:54:33 +00002471 /* XO-Form */
2472 case 0x1F:
cerionb85e8bb2005-02-16 08:54:33 +00002473 do_rc = True; // All below record to CR
2474
2475 switch (opc2) {
cerione9d361a2005-03-04 17:35:29 +00002476 case 0x10A: // add (Add, PPC32 p347)
ceriond953ebb2005-11-29 13:27:20 +00002477 DIP("add%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002478 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002479 rD_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00002480 assign( rD, binop( mkSzOp(ty, Iop_Add8),
ceriond953ebb2005-11-29 13:27:20 +00002481 mkexpr(rA), mkexpr(rB) ) );
cerion70e24122005-03-16 00:27:37 +00002482 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002483 set_XER_OV( ty, PPCG_FLAG_OP_ADD,
cerion76de5cf2005-11-18 18:25:12 +00002484 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00002485 }
cerionb85e8bb2005-02-16 08:54:33 +00002486 break;
cerion91ad5362005-01-27 23:02:41 +00002487
cerione9d361a2005-03-04 17:35:29 +00002488 case 0x00A: // addc (Add Carrying, PPC32 p348)
ceriond953ebb2005-11-29 13:27:20 +00002489 DIP("addc%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002490 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002491 rD_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00002492 assign( rD, binop( mkSzOp(ty, Iop_Add8),
ceriond953ebb2005-11-29 13:27:20 +00002493 mkexpr(rA), mkexpr(rB)) );
cerion5b2325f2005-12-23 00:55:09 +00002494 set_XER_CA( ty, PPCG_FLAG_OP_ADD,
cerion76de5cf2005-11-18 18:25:12 +00002495 mkexpr(rD), mkexpr(rA), mkexpr(rB),
cerion2831b002005-11-30 19:55:22 +00002496 mkSzImm(ty, 0)/*old xer.ca, which is ignored*/ );
cerion70e24122005-03-16 00:27:37 +00002497 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002498 set_XER_OV( ty, PPCG_FLAG_OP_ADD,
cerion76de5cf2005-11-18 18:25:12 +00002499 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00002500 }
cerionb85e8bb2005-02-16 08:54:33 +00002501 break;
2502
sewardjb51f0f42005-07-18 11:38:02 +00002503 case 0x08A: { // adde (Add Extended, PPC32 p349)
cerion2831b002005-11-30 19:55:22 +00002504 IRTemp old_xer_ca = newTemp(ty);
ceriond953ebb2005-11-29 13:27:20 +00002505 DIP("adde%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002506 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002507 rD_addr, rA_addr, rB_addr);
cerionb85e8bb2005-02-16 08:54:33 +00002508 // rD = rA + rB + XER[CA]
sewardje9d8a262009-07-01 08:06:34 +00002509 assign( old_xer_ca, mkWidenFrom32(ty, getXER_CA32(), False) );
cerion2831b002005-11-30 19:55:22 +00002510 assign( rD, binop( mkSzOp(ty, Iop_Add8), mkexpr(rA),
2511 binop( mkSzOp(ty, Iop_Add8),
2512 mkexpr(rB), mkexpr(old_xer_ca))) );
cerion5b2325f2005-12-23 00:55:09 +00002513 set_XER_CA( ty, PPCG_FLAG_OP_ADDE,
cerion76de5cf2005-11-18 18:25:12 +00002514 mkexpr(rD), mkexpr(rA), mkexpr(rB),
cerion2831b002005-11-30 19:55:22 +00002515 mkexpr(old_xer_ca) );
cerion70e24122005-03-16 00:27:37 +00002516 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002517 set_XER_OV( ty, PPCG_FLAG_OP_ADDE,
cerion76de5cf2005-11-18 18:25:12 +00002518 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00002519 }
cerionb85e8bb2005-02-16 08:54:33 +00002520 break;
sewardjb51f0f42005-07-18 11:38:02 +00002521 }
2522
cerion5b2325f2005-12-23 00:55:09 +00002523 case 0x0EA: { // addme (Add to Minus One Extended, PPC32 p354)
cerion2831b002005-11-30 19:55:22 +00002524 IRTemp old_xer_ca = newTemp(ty);
2525 IRExpr *min_one;
cerion76de5cf2005-11-18 18:25:12 +00002526 if (rB_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002527 vex_printf("dis_int_arith(ppc)(addme,rB_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002528 return False;
2529 }
ceriond953ebb2005-11-29 13:27:20 +00002530 DIP("addme%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002531 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002532 rD_addr, rA_addr, rB_addr);
cerion70e24122005-03-16 00:27:37 +00002533 // rD = rA + (-1) + XER[CA]
2534 // => Just another form of adde
sewardje9d8a262009-07-01 08:06:34 +00002535 assign( old_xer_ca, mkWidenFrom32(ty, getXER_CA32(), False) );
cerion2831b002005-11-30 19:55:22 +00002536 min_one = mkSzImm(ty, (Long)-1);
2537 assign( rD, binop( mkSzOp(ty, Iop_Add8), mkexpr(rA),
2538 binop( mkSzOp(ty, Iop_Add8),
2539 min_one, mkexpr(old_xer_ca)) ));
cerion5b2325f2005-12-23 00:55:09 +00002540 set_XER_CA( ty, PPCG_FLAG_OP_ADDE,
ceriond953ebb2005-11-29 13:27:20 +00002541 mkexpr(rD), mkexpr(rA), min_one,
cerion2831b002005-11-30 19:55:22 +00002542 mkexpr(old_xer_ca) );
cerion70e24122005-03-16 00:27:37 +00002543 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002544 set_XER_OV( ty, PPCG_FLAG_OP_ADDE,
ceriond953ebb2005-11-29 13:27:20 +00002545 mkexpr(rD), mkexpr(rA), min_one );
cerion70e24122005-03-16 00:27:37 +00002546 }
cerionb85e8bb2005-02-16 08:54:33 +00002547 break;
sewardjb51f0f42005-07-18 11:38:02 +00002548 }
2549
2550 case 0x0CA: { // addze (Add to Zero Extended, PPC32 p355)
cerion2831b002005-11-30 19:55:22 +00002551 IRTemp old_xer_ca = newTemp(ty);
cerion76de5cf2005-11-18 18:25:12 +00002552 if (rB_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002553 vex_printf("dis_int_arith(ppc)(addze,rB_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002554 return False;
2555 }
ceriond953ebb2005-11-29 13:27:20 +00002556 DIP("addze%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002557 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002558 rD_addr, rA_addr, rB_addr);
cerion70e24122005-03-16 00:27:37 +00002559 // rD = rA + (0) + XER[CA]
2560 // => Just another form of adde
sewardje9d8a262009-07-01 08:06:34 +00002561 assign( old_xer_ca, mkWidenFrom32(ty, getXER_CA32(), False) );
cerion2831b002005-11-30 19:55:22 +00002562 assign( rD, binop( mkSzOp(ty, Iop_Add8),
2563 mkexpr(rA), mkexpr(old_xer_ca)) );
cerion5b2325f2005-12-23 00:55:09 +00002564 set_XER_CA( ty, PPCG_FLAG_OP_ADDE,
cerion2831b002005-11-30 19:55:22 +00002565 mkexpr(rD), mkexpr(rA), mkSzImm(ty, 0),
2566 mkexpr(old_xer_ca) );
cerion70e24122005-03-16 00:27:37 +00002567 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002568 set_XER_OV( ty, PPCG_FLAG_OP_ADDE,
cerion2831b002005-11-30 19:55:22 +00002569 mkexpr(rD), mkexpr(rA), mkSzImm(ty, 0) );
cerion70e24122005-03-16 00:27:37 +00002570 }
cerionb85e8bb2005-02-16 08:54:33 +00002571 break;
sewardjb51f0f42005-07-18 11:38:02 +00002572 }
cerion91ad5362005-01-27 23:02:41 +00002573
cerione9d361a2005-03-04 17:35:29 +00002574 case 0x1EB: // divw (Divide Word, PPC32 p388)
ceriond953ebb2005-11-29 13:27:20 +00002575 DIP("divw%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002576 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002577 rD_addr, rA_addr, rB_addr);
ceriond953ebb2005-11-29 13:27:20 +00002578 if (mode64) {
cerion2831b002005-11-30 19:55:22 +00002579 /* Note:
2580 XER settings are mode independent, and reflect the
2581 overflow of the low-order 32bit result
2582 CR0[LT|GT|EQ] are undefined if flag_rC && mode64
2583 */
cerionbb01b7c2005-12-16 13:40:18 +00002584 /* rD[hi32] are undefined: setting them to sign of lo32
2585 - makes set_CR0 happy */
2586 IRExpr* dividend = mk64lo32Sto64( mkexpr(rA) );
2587 IRExpr* divisor = mk64lo32Sto64( mkexpr(rB) );
cerion5b2325f2005-12-23 00:55:09 +00002588 assign( rD, mk64lo32Uto64( binop(Iop_DivS64, dividend,
2589 divisor) ) );
ceriond953ebb2005-11-29 13:27:20 +00002590 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002591 set_XER_OV( ty, PPCG_FLAG_OP_DIVW,
cerionf0de28c2005-12-13 20:21:11 +00002592 mkexpr(rD), dividend, divisor );
ceriond953ebb2005-11-29 13:27:20 +00002593 }
2594 } else {
2595 assign( rD, binop(Iop_DivS32, mkexpr(rA), mkexpr(rB)) );
2596 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002597 set_XER_OV( ty, PPCG_FLAG_OP_DIVW,
ceriond953ebb2005-11-29 13:27:20 +00002598 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
2599 }
cerion70e24122005-03-16 00:27:37 +00002600 }
cerionb85e8bb2005-02-16 08:54:33 +00002601 /* Note:
2602 if (0x8000_0000 / -1) or (x / 0)
cerion76de5cf2005-11-18 18:25:12 +00002603 => rD=undef, if(flag_rC) CR7=undef, if(flag_OE) XER_OV=1
cerionb85e8bb2005-02-16 08:54:33 +00002604 => But _no_ exception raised. */
2605 break;
cerion91ad5362005-01-27 23:02:41 +00002606
cerione9d361a2005-03-04 17:35:29 +00002607 case 0x1CB: // divwu (Divide Word Unsigned, PPC32 p389)
ceriond953ebb2005-11-29 13:27:20 +00002608 DIP("divwu%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002609 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002610 rD_addr, rA_addr, rB_addr);
ceriond953ebb2005-11-29 13:27:20 +00002611 if (mode64) {
cerion2831b002005-11-30 19:55:22 +00002612 /* Note:
2613 XER settings are mode independent, and reflect the
2614 overflow of the low-order 32bit result
2615 CR0[LT|GT|EQ] are undefined if flag_rC && mode64
2616 */
cerionbb01b7c2005-12-16 13:40:18 +00002617 IRExpr* dividend = mk64lo32Uto64( mkexpr(rA) );
2618 IRExpr* divisor = mk64lo32Uto64( mkexpr(rB) );
cerion5b2325f2005-12-23 00:55:09 +00002619 assign( rD, mk64lo32Uto64( binop(Iop_DivU64, dividend,
2620 divisor) ) );
ceriond953ebb2005-11-29 13:27:20 +00002621 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002622 set_XER_OV( ty, PPCG_FLAG_OP_DIVWU,
cerionf0de28c2005-12-13 20:21:11 +00002623 mkexpr(rD), dividend, divisor );
ceriond953ebb2005-11-29 13:27:20 +00002624 }
cerion2831b002005-11-30 19:55:22 +00002625 } else {
ceriond953ebb2005-11-29 13:27:20 +00002626 assign( rD, binop(Iop_DivU32, mkexpr(rA), mkexpr(rB)) );
2627 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002628 set_XER_OV( ty, PPCG_FLAG_OP_DIVWU,
ceriond953ebb2005-11-29 13:27:20 +00002629 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
2630 }
cerion70e24122005-03-16 00:27:37 +00002631 }
cerionb85e8bb2005-02-16 08:54:33 +00002632 /* Note: ditto comment divw, for (x / 0) */
2633 break;
cerion91ad5362005-01-27 23:02:41 +00002634
cerione9d361a2005-03-04 17:35:29 +00002635 case 0x04B: // mulhw (Multiply High Word, PPC32 p488)
cerionb85e8bb2005-02-16 08:54:33 +00002636 if (flag_OE != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002637 vex_printf("dis_int_arith(ppc)(mulhw,flag_OE)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002638 return False;
2639 }
cerion5b2325f2005-12-23 00:55:09 +00002640 DIP("mulhw%s r%u,r%u,r%u\n", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002641 rD_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00002642 if (mode64) {
cerionbb01b7c2005-12-16 13:40:18 +00002643 /* rD[hi32] are undefined: setting them to sign of lo32
2644 - makes set_CR0 happy */
2645 assign( rD, binop(Iop_Sar64,
2646 binop(Iop_Mul64,
2647 mk64lo32Sto64( mkexpr(rA) ),
2648 mk64lo32Sto64( mkexpr(rB) )),
2649 mkU8(32)) );
cerion2831b002005-11-30 19:55:22 +00002650 } else {
ceriond953ebb2005-11-29 13:27:20 +00002651 assign( rD, unop(Iop_64HIto32,
2652 binop(Iop_MullS32,
2653 mkexpr(rA), mkexpr(rB))) );
cerion2831b002005-11-30 19:55:22 +00002654 }
cerionb85e8bb2005-02-16 08:54:33 +00002655 break;
cerionc19d5e12005-02-01 15:56:25 +00002656
cerion5b2325f2005-12-23 00:55:09 +00002657 case 0x00B: // mulhwu (Multiply High Word Unsigned, PPC32 p489)
cerionb85e8bb2005-02-16 08:54:33 +00002658 if (flag_OE != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002659 vex_printf("dis_int_arith(ppc)(mulhwu,flag_OE)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002660 return False;
2661 }
cerion5b2325f2005-12-23 00:55:09 +00002662 DIP("mulhwu%s r%u,r%u,r%u\n", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002663 rD_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00002664 if (mode64) {
cerionbb01b7c2005-12-16 13:40:18 +00002665 /* rD[hi32] are undefined: setting them to sign of lo32
2666 - makes set_CR0 happy */
2667 assign( rD, binop(Iop_Sar64,
2668 binop(Iop_Mul64,
2669 mk64lo32Uto64( mkexpr(rA) ),
2670 mk64lo32Uto64( mkexpr(rB) ) ),
2671 mkU8(32)) );
cerion2831b002005-11-30 19:55:22 +00002672 } else {
ceriond953ebb2005-11-29 13:27:20 +00002673 assign( rD, unop(Iop_64HIto32,
2674 binop(Iop_MullU32,
2675 mkexpr(rA), mkexpr(rB))) );
cerion2831b002005-11-30 19:55:22 +00002676 }
cerionb85e8bb2005-02-16 08:54:33 +00002677 break;
2678
cerione9d361a2005-03-04 17:35:29 +00002679 case 0x0EB: // mullw (Multiply Low Word, PPC32 p491)
ceriond953ebb2005-11-29 13:27:20 +00002680 DIP("mullw%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002681 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002682 rD_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00002683 if (mode64) {
cerionbb01b7c2005-12-16 13:40:18 +00002684 /* rD[hi32] are undefined: setting them to sign of lo32
2685 - set_XER_OV() and set_CR0() depend on this */
2686 IRExpr *a = unop(Iop_64to32, mkexpr(rA) );
2687 IRExpr *b = unop(Iop_64to32, mkexpr(rB) );
2688 assign( rD, binop(Iop_MullS32, a, b) );
2689 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002690 set_XER_OV( ty, PPCG_FLAG_OP_MULLW,
cerionbb01b7c2005-12-16 13:40:18 +00002691 mkexpr(rD),
2692 unop(Iop_32Uto64, a), unop(Iop_32Uto64, b) );
2693 }
cerion2831b002005-11-30 19:55:22 +00002694 } else {
ceriond953ebb2005-11-29 13:27:20 +00002695 assign( rD, unop(Iop_64to32,
2696 binop(Iop_MullU32,
2697 mkexpr(rA), mkexpr(rB))) );
cerionbb01b7c2005-12-16 13:40:18 +00002698 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002699 set_XER_OV( ty, PPCG_FLAG_OP_MULLW,
cerionbb01b7c2005-12-16 13:40:18 +00002700 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
2701 }
cerion70e24122005-03-16 00:27:37 +00002702 }
cerionb85e8bb2005-02-16 08:54:33 +00002703 break;
cerionc19d5e12005-02-01 15:56:25 +00002704
cerione9d361a2005-03-04 17:35:29 +00002705 case 0x068: // neg (Negate, PPC32 p493)
cerion76de5cf2005-11-18 18:25:12 +00002706 if (rB_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002707 vex_printf("dis_int_arith(ppc)(neg,rB_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002708 return False;
2709 }
ceriond953ebb2005-11-29 13:27:20 +00002710 DIP("neg%s%s r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002711 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002712 rD_addr, rA_addr);
ceriond953ebb2005-11-29 13:27:20 +00002713 // rD = (~rA) + 1
cerion2831b002005-11-30 19:55:22 +00002714 assign( rD, binop( mkSzOp(ty, Iop_Add8),
2715 unop( mkSzOp(ty, Iop_Not8), mkexpr(rA) ),
2716 mkSzImm(ty, 1)) );
cerion70e24122005-03-16 00:27:37 +00002717 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002718 set_XER_OV( ty, PPCG_FLAG_OP_NEG,
cerion76de5cf2005-11-18 18:25:12 +00002719 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00002720 }
cerionb85e8bb2005-02-16 08:54:33 +00002721 break;
cerion91ad5362005-01-27 23:02:41 +00002722
cerione9d361a2005-03-04 17:35:29 +00002723 case 0x028: // subf (Subtract From, PPC32 p537)
ceriond953ebb2005-11-29 13:27:20 +00002724 DIP("subf%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002725 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002726 rD_addr, rA_addr, rB_addr);
cerion01908472005-02-25 16:43:08 +00002727 // rD = rB - rA
cerion2831b002005-11-30 19:55:22 +00002728 assign( rD, binop( mkSzOp(ty, Iop_Sub8),
ceriond953ebb2005-11-29 13:27:20 +00002729 mkexpr(rB), mkexpr(rA)) );
cerion70e24122005-03-16 00:27:37 +00002730 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002731 set_XER_OV( ty, PPCG_FLAG_OP_SUBF,
cerion76de5cf2005-11-18 18:25:12 +00002732 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00002733 }
cerionb85e8bb2005-02-16 08:54:33 +00002734 break;
cerion38674602005-02-08 02:19:25 +00002735
cerione9d361a2005-03-04 17:35:29 +00002736 case 0x008: // subfc (Subtract from Carrying, PPC32 p538)
ceriond953ebb2005-11-29 13:27:20 +00002737 DIP("subfc%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002738 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002739 rD_addr, rA_addr, rB_addr);
cerion01908472005-02-25 16:43:08 +00002740 // rD = rB - rA
cerion2831b002005-11-30 19:55:22 +00002741 assign( rD, binop( mkSzOp(ty, Iop_Sub8),
ceriond953ebb2005-11-29 13:27:20 +00002742 mkexpr(rB), mkexpr(rA)) );
cerion5b2325f2005-12-23 00:55:09 +00002743 set_XER_CA( ty, PPCG_FLAG_OP_SUBFC,
cerion76de5cf2005-11-18 18:25:12 +00002744 mkexpr(rD), mkexpr(rA), mkexpr(rB),
cerion2831b002005-11-30 19:55:22 +00002745 mkSzImm(ty, 0)/*old xer.ca, which is ignored*/ );
cerion70e24122005-03-16 00:27:37 +00002746 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002747 set_XER_OV( ty, PPCG_FLAG_OP_SUBFC,
cerion76de5cf2005-11-18 18:25:12 +00002748 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00002749 }
cerionb85e8bb2005-02-16 08:54:33 +00002750 break;
2751
sewardjb51f0f42005-07-18 11:38:02 +00002752 case 0x088: {// subfe (Subtract from Extended, PPC32 p539)
cerion2831b002005-11-30 19:55:22 +00002753 IRTemp old_xer_ca = newTemp(ty);
ceriond953ebb2005-11-29 13:27:20 +00002754 DIP("subfe%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002755 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002756 rD_addr, rA_addr, rB_addr);
cerionb85e8bb2005-02-16 08:54:33 +00002757 // rD = (log not)rA + rB + XER[CA]
sewardje9d8a262009-07-01 08:06:34 +00002758 assign( old_xer_ca, mkWidenFrom32(ty, getXER_CA32(), False) );
cerion2831b002005-11-30 19:55:22 +00002759 assign( rD, binop( mkSzOp(ty, Iop_Add8),
2760 unop( mkSzOp(ty, Iop_Not8), mkexpr(rA)),
2761 binop( mkSzOp(ty, Iop_Add8),
2762 mkexpr(rB), mkexpr(old_xer_ca))) );
cerion5b2325f2005-12-23 00:55:09 +00002763 set_XER_CA( ty, PPCG_FLAG_OP_SUBFE,
cerion76de5cf2005-11-18 18:25:12 +00002764 mkexpr(rD), mkexpr(rA), mkexpr(rB),
cerion2831b002005-11-30 19:55:22 +00002765 mkexpr(old_xer_ca) );
cerion70e24122005-03-16 00:27:37 +00002766 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002767 set_XER_OV( ty, PPCG_FLAG_OP_SUBFE,
cerion76de5cf2005-11-18 18:25:12 +00002768 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00002769 }
cerionb85e8bb2005-02-16 08:54:33 +00002770 break;
sewardjb51f0f42005-07-18 11:38:02 +00002771 }
2772
cerion5b2325f2005-12-23 00:55:09 +00002773 case 0x0E8: { // subfme (Subtract from -1 Extended, PPC32 p541)
cerion2831b002005-11-30 19:55:22 +00002774 IRTemp old_xer_ca = newTemp(ty);
2775 IRExpr *min_one;
cerion76de5cf2005-11-18 18:25:12 +00002776 if (rB_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002777 vex_printf("dis_int_arith(ppc)(subfme,rB_addr)\n");
sewardj20ef5472005-07-21 14:48:31 +00002778 return False;
2779 }
ceriond953ebb2005-11-29 13:27:20 +00002780 DIP("subfme%s%s r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002781 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002782 rD_addr, rA_addr);
sewardj20ef5472005-07-21 14:48:31 +00002783 // rD = (log not)rA + (-1) + XER[CA]
2784 // => Just another form of subfe
sewardje9d8a262009-07-01 08:06:34 +00002785 assign( old_xer_ca, mkWidenFrom32(ty, getXER_CA32(), False) );
cerion2831b002005-11-30 19:55:22 +00002786 min_one = mkSzImm(ty, (Long)-1);
2787 assign( rD, binop( mkSzOp(ty, Iop_Add8),
2788 unop( mkSzOp(ty, Iop_Not8), mkexpr(rA)),
2789 binop( mkSzOp(ty, Iop_Add8),
2790 min_one, mkexpr(old_xer_ca))) );
cerion5b2325f2005-12-23 00:55:09 +00002791 set_XER_CA( ty, PPCG_FLAG_OP_SUBFE,
ceriond953ebb2005-11-29 13:27:20 +00002792 mkexpr(rD), mkexpr(rA), min_one,
cerion2831b002005-11-30 19:55:22 +00002793 mkexpr(old_xer_ca) );
sewardj20ef5472005-07-21 14:48:31 +00002794 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002795 set_XER_OV( ty, PPCG_FLAG_OP_SUBFE,
ceriond953ebb2005-11-29 13:27:20 +00002796 mkexpr(rD), mkexpr(rA), min_one );
sewardj20ef5472005-07-21 14:48:31 +00002797 }
2798 break;
2799 }
2800
cerion5b2325f2005-12-23 00:55:09 +00002801 case 0x0C8: { // subfze (Subtract from Zero Extended, PPC32 p542)
cerion2831b002005-11-30 19:55:22 +00002802 IRTemp old_xer_ca = newTemp(ty);
cerion76de5cf2005-11-18 18:25:12 +00002803 if (rB_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002804 vex_printf("dis_int_arith(ppc)(subfze,rB_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002805 return False;
2806 }
ceriond953ebb2005-11-29 13:27:20 +00002807 DIP("subfze%s%s r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002808 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002809 rD_addr, rA_addr);
cerion70e24122005-03-16 00:27:37 +00002810 // rD = (log not)rA + (0) + XER[CA]
2811 // => Just another form of subfe
sewardje9d8a262009-07-01 08:06:34 +00002812 assign( old_xer_ca, mkWidenFrom32(ty, getXER_CA32(), False) );
cerion2831b002005-11-30 19:55:22 +00002813 assign( rD, binop( mkSzOp(ty, Iop_Add8),
2814 unop( mkSzOp(ty, Iop_Not8),
2815 mkexpr(rA)), mkexpr(old_xer_ca)) );
cerion5b2325f2005-12-23 00:55:09 +00002816 set_XER_CA( ty, PPCG_FLAG_OP_SUBFE,
cerion2831b002005-11-30 19:55:22 +00002817 mkexpr(rD), mkexpr(rA), mkSzImm(ty, 0),
2818 mkexpr(old_xer_ca) );
cerion70e24122005-03-16 00:27:37 +00002819 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002820 set_XER_OV( ty, PPCG_FLAG_OP_SUBFE,
cerion2831b002005-11-30 19:55:22 +00002821 mkexpr(rD), mkexpr(rA), mkSzImm(ty, 0) );
cerion70e24122005-03-16 00:27:37 +00002822 }
cerionb85e8bb2005-02-16 08:54:33 +00002823 break;
sewardjb51f0f42005-07-18 11:38:02 +00002824 }
cerionae694622005-01-28 17:52:47 +00002825
cerionf0de28c2005-12-13 20:21:11 +00002826
2827 /* 64bit Arithmetic */
cerion5b2325f2005-12-23 00:55:09 +00002828 case 0x49: // mulhd (Multiply High DWord, PPC64 p539)
cerionf0de28c2005-12-13 20:21:11 +00002829 if (flag_OE != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002830 vex_printf("dis_int_arith(ppc)(mulhd,flagOE)\n");
cerionf0de28c2005-12-13 20:21:11 +00002831 return False;
2832 }
cerion5b2325f2005-12-23 00:55:09 +00002833 DIP("mulhd%s r%u,r%u,r%u\n", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00002834 rD_addr, rA_addr, rB_addr);
cerionf0de28c2005-12-13 20:21:11 +00002835 assign( rD, unop(Iop_128HIto64,
cerion07b07a92005-12-22 14:32:35 +00002836 binop(Iop_MullS64,
cerionf0de28c2005-12-13 20:21:11 +00002837 mkexpr(rA), mkexpr(rB))) );
cerion07b07a92005-12-22 14:32:35 +00002838
2839 break;
cerionf0de28c2005-12-13 20:21:11 +00002840
cerion5b2325f2005-12-23 00:55:09 +00002841 case 0x9: // mulhdu (Multiply High DWord Unsigned, PPC64 p540)
cerionf0de28c2005-12-13 20:21:11 +00002842 if (flag_OE != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002843 vex_printf("dis_int_arith(ppc)(mulhdu,flagOE)\n");
cerionf0de28c2005-12-13 20:21:11 +00002844 return False;
2845 }
cerion5b2325f2005-12-23 00:55:09 +00002846 DIP("mulhdu%s r%u,r%u,r%u\n", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00002847 rD_addr, rA_addr, rB_addr);
cerion07b07a92005-12-22 14:32:35 +00002848 assign( rD, unop(Iop_128HIto64,
2849 binop(Iop_MullU64,
2850 mkexpr(rA), mkexpr(rB))) );
2851 break;
cerionf0de28c2005-12-13 20:21:11 +00002852
cerion5b2325f2005-12-23 00:55:09 +00002853 case 0xE9: // mulld (Multiply Low DWord, PPC64 p543)
cerionf0de28c2005-12-13 20:21:11 +00002854 DIP("mulld%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002855 flag_OE ? "o" : "", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00002856 rD_addr, rA_addr, rB_addr);
2857 assign( rD, binop(Iop_Mul64, mkexpr(rA), mkexpr(rB)) );
2858 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002859 set_XER_OV( ty, PPCG_FLAG_OP_MULLW,
cerionf0de28c2005-12-13 20:21:11 +00002860 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
2861 }
2862 break;
2863
cerion5b2325f2005-12-23 00:55:09 +00002864 case 0x1E9: // divd (Divide DWord, PPC64 p419)
cerionf0de28c2005-12-13 20:21:11 +00002865 DIP("divd%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002866 flag_OE ? "o" : "", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00002867 rD_addr, rA_addr, rB_addr);
cerion07b07a92005-12-22 14:32:35 +00002868 assign( rD, binop(Iop_DivS64, mkexpr(rA), mkexpr(rB)) );
2869 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002870 set_XER_OV( ty, PPCG_FLAG_OP_DIVW,
cerion07b07a92005-12-22 14:32:35 +00002871 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
2872 }
2873 break;
cerionf0de28c2005-12-13 20:21:11 +00002874 /* Note:
cerion07b07a92005-12-22 14:32:35 +00002875 if (0x8000_0000_0000_0000 / -1) or (x / 0)
cerionf0de28c2005-12-13 20:21:11 +00002876 => rD=undef, if(flag_rC) CR7=undef, if(flag_OE) XER_OV=1
2877 => But _no_ exception raised. */
2878
cerion5b2325f2005-12-23 00:55:09 +00002879 case 0x1C9: // divdu (Divide DWord Unsigned, PPC64 p420)
cerionf0de28c2005-12-13 20:21:11 +00002880 DIP("divdu%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002881 flag_OE ? "o" : "", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00002882 rD_addr, rA_addr, rB_addr);
cerion07b07a92005-12-22 14:32:35 +00002883 assign( rD, binop(Iop_DivU64, mkexpr(rA), mkexpr(rB)) );
2884 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002885 set_XER_OV( ty, PPCG_FLAG_OP_DIVWU,
cerion07b07a92005-12-22 14:32:35 +00002886 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
2887 }
2888 break;
cerionf0de28c2005-12-13 20:21:11 +00002889 /* Note: ditto comment divd, for (x / 0) */
2890
cerionb85e8bb2005-02-16 08:54:33 +00002891 default:
cerion5b2325f2005-12-23 00:55:09 +00002892 vex_printf("dis_int_arith(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002893 return False;
2894 }
2895 break;
cerionf0de28c2005-12-13 20:21:11 +00002896
cerionb85e8bb2005-02-16 08:54:33 +00002897 default:
cerion5b2325f2005-12-23 00:55:09 +00002898 vex_printf("dis_int_arith(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002899 return False;
2900 }
cerion91ad5362005-01-27 23:02:41 +00002901
cerion76de5cf2005-11-18 18:25:12 +00002902 putIReg( rD_addr, mkexpr(rD) );
2903
2904 if (do_rc && flag_rC) {
2905 set_CR0( mkexpr(rD) );
cerionb85e8bb2005-02-16 08:54:33 +00002906 }
2907 return True;
cerion91ad5362005-01-27 23:02:41 +00002908}
2909
2910
2911
cerion3d870a32005-03-18 12:23:33 +00002912/*
2913 Integer Compare Instructions
2914*/
cerion7aa4bbc2005-01-29 09:32:07 +00002915static Bool dis_int_cmp ( UInt theInstr )
2916{
cerion76de5cf2005-11-18 18:25:12 +00002917 /* D-Form, X-Form */
2918 UChar opc1 = ifieldOPC(theInstr);
2919 UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) );
2920 UChar b22 = toUChar( IFIELD( theInstr, 22, 1 ) );
2921 UChar flag_L = toUChar( IFIELD( theInstr, 21, 1 ) );
2922 UChar rA_addr = ifieldRegA(theInstr);
ceriond953ebb2005-11-29 13:27:20 +00002923 UInt uimm16 = ifieldUIMM16(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00002924 UChar rB_addr = ifieldRegB(theInstr);
2925 UInt opc2 = ifieldOPClo10(theInstr);
2926 UChar b0 = ifieldBIT0(theInstr);
cerion7aa4bbc2005-01-29 09:32:07 +00002927
cerionbb01b7c2005-12-16 13:40:18 +00002928 IRType ty = mode64 ? Ity_I64 : Ity_I32;
2929 IRExpr *a = getIReg(rA_addr);
2930 IRExpr *b;
cerion76de5cf2005-11-18 18:25:12 +00002931
ceriond953ebb2005-11-29 13:27:20 +00002932 if (!mode64 && flag_L==1) { // L==1 invalid for 32 bit.
cerion5b2325f2005-12-23 00:55:09 +00002933 vex_printf("dis_int_cmp(ppc)(flag_L)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002934 return False;
2935 }
2936
cerion76de5cf2005-11-18 18:25:12 +00002937 if (b22 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002938 vex_printf("dis_int_cmp(ppc)(b22)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002939 return False;
2940 }
2941
2942 switch (opc1) {
ceriond953ebb2005-11-29 13:27:20 +00002943 case 0x0B: // cmpi (Compare Immediate, PPC32 p368)
2944 DIP("cmpi cr%u,%u,r%u,%d\n", crfD, flag_L, rA_addr,
2945 (Int)extend_s_16to32(uimm16));
cerion2831b002005-11-30 19:55:22 +00002946 b = mkSzExtendS16( ty, uimm16 );
cerionbb01b7c2005-12-16 13:40:18 +00002947 if (flag_L == 1) {
cerion2831b002005-11-30 19:55:22 +00002948 putCR321(crfD, unop(Iop_64to8, binop(Iop_CmpORD64S, a, b)));
ceriond953ebb2005-11-29 13:27:20 +00002949 } else {
sewardje9d8a262009-07-01 08:06:34 +00002950 a = mkNarrowTo32( ty, a );
2951 b = mkNarrowTo32( ty, b );
ceriond953ebb2005-11-29 13:27:20 +00002952 putCR321(crfD, unop(Iop_32to8, binop(Iop_CmpORD32S, a, b)));
2953 }
2954 putCR0( crfD, getXER_SO() );
2955 break;
2956
2957 case 0x0A: // cmpli (Compare Logical Immediate, PPC32 p370)
2958 DIP("cmpli cr%u,%u,r%u,0x%x\n", crfD, flag_L, rA_addr, uimm16);
cerion2831b002005-11-30 19:55:22 +00002959 b = mkSzImm( ty, uimm16 );
cerionbb01b7c2005-12-16 13:40:18 +00002960 if (flag_L == 1) {
cerion2831b002005-11-30 19:55:22 +00002961 putCR321(crfD, unop(Iop_64to8, binop(Iop_CmpORD64U, a, b)));
ceriond953ebb2005-11-29 13:27:20 +00002962 } else {
sewardje9d8a262009-07-01 08:06:34 +00002963 a = mkNarrowTo32( ty, a );
2964 b = mkNarrowTo32( ty, b );
ceriond953ebb2005-11-29 13:27:20 +00002965 putCR321(crfD, unop(Iop_32to8, binop(Iop_CmpORD32U, a, b)));
2966 }
2967 putCR0( crfD, getXER_SO() );
2968 break;
cerionb85e8bb2005-02-16 08:54:33 +00002969
2970 /* X Form */
2971 case 0x1F:
2972 if (b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002973 vex_printf("dis_int_cmp(ppc)(0x1F,b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002974 return False;
2975 }
cerionbb01b7c2005-12-16 13:40:18 +00002976 b = getIReg(rB_addr);
cerion7aa4bbc2005-01-29 09:32:07 +00002977
cerionb85e8bb2005-02-16 08:54:33 +00002978 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00002979 case 0x000: // cmp (Compare, PPC32 p367)
2980 DIP("cmp cr%u,%u,r%u,r%u\n", crfD, flag_L, rA_addr, rB_addr);
sewardja9cb67b2006-08-19 18:31:53 +00002981 /* Comparing a reg with itself produces a result which
2982 doesn't depend on the contents of the reg. Therefore
2983 remove the false dependency, which has been known to cause
2984 memcheck to produce false errors. */
sewardj9195aa12006-08-19 22:18:53 +00002985 if (rA_addr == rB_addr)
sewardjdd40fdf2006-12-24 02:20:24 +00002986 a = b = typeOfIRExpr(irsb->tyenv,a) == Ity_I64
sewardj9195aa12006-08-19 22:18:53 +00002987 ? mkU64(0) : mkU32(0);
cerionbb01b7c2005-12-16 13:40:18 +00002988 if (flag_L == 1) {
2989 putCR321(crfD, unop(Iop_64to8, binop(Iop_CmpORD64S, a, b)));
ceriond953ebb2005-11-29 13:27:20 +00002990 } else {
sewardje9d8a262009-07-01 08:06:34 +00002991 a = mkNarrowTo32( ty, a );
2992 b = mkNarrowTo32( ty, b );
cerionbb01b7c2005-12-16 13:40:18 +00002993 putCR321(crfD, unop(Iop_32to8,binop(Iop_CmpORD32S, a, b)));
ceriond953ebb2005-11-29 13:27:20 +00002994 }
2995 putCR0( crfD, getXER_SO() );
2996 break;
cerionb85e8bb2005-02-16 08:54:33 +00002997
ceriond953ebb2005-11-29 13:27:20 +00002998 case 0x020: // cmpl (Compare Logical, PPC32 p369)
2999 DIP("cmpl cr%u,%u,r%u,r%u\n", crfD, flag_L, rA_addr, rB_addr);
sewardja9cb67b2006-08-19 18:31:53 +00003000 /* Comparing a reg with itself produces a result which
3001 doesn't depend on the contents of the reg. Therefore
3002 remove the false dependency, which has been known to cause
3003 memcheck to produce false errors. */
sewardj9195aa12006-08-19 22:18:53 +00003004 if (rA_addr == rB_addr)
sewardjdd40fdf2006-12-24 02:20:24 +00003005 a = b = typeOfIRExpr(irsb->tyenv,a) == Ity_I64
sewardj9195aa12006-08-19 22:18:53 +00003006 ? mkU64(0) : mkU32(0);
cerionbb01b7c2005-12-16 13:40:18 +00003007 if (flag_L == 1) {
3008 putCR321(crfD, unop(Iop_64to8, binop(Iop_CmpORD64U, a, b)));
ceriond953ebb2005-11-29 13:27:20 +00003009 } else {
sewardje9d8a262009-07-01 08:06:34 +00003010 a = mkNarrowTo32( ty, a );
3011 b = mkNarrowTo32( ty, b );
cerionbb01b7c2005-12-16 13:40:18 +00003012 putCR321(crfD, unop(Iop_32to8, binop(Iop_CmpORD32U, a, b)));
ceriond953ebb2005-11-29 13:27:20 +00003013 }
3014 putCR0( crfD, getXER_SO() );
3015 break;
cerion7aa4bbc2005-01-29 09:32:07 +00003016
ceriond953ebb2005-11-29 13:27:20 +00003017 default:
cerion5b2325f2005-12-23 00:55:09 +00003018 vex_printf("dis_int_cmp(ppc)(opc2)\n");
ceriond953ebb2005-11-29 13:27:20 +00003019 return False;
cerionb85e8bb2005-02-16 08:54:33 +00003020 }
3021 break;
ceriond953ebb2005-11-29 13:27:20 +00003022
cerionb85e8bb2005-02-16 08:54:33 +00003023 default:
cerion5b2325f2005-12-23 00:55:09 +00003024 vex_printf("dis_int_cmp(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003025 return False;
3026 }
3027
cerionb85e8bb2005-02-16 08:54:33 +00003028 return True;
cerion7aa4bbc2005-01-29 09:32:07 +00003029}
3030
3031
cerion3d870a32005-03-18 12:23:33 +00003032/*
3033 Integer Logical Instructions
3034*/
cerion7aa4bbc2005-01-29 09:32:07 +00003035static Bool dis_int_logic ( UInt theInstr )
3036{
cerion76de5cf2005-11-18 18:25:12 +00003037 /* D-Form, X-Form */
3038 UChar opc1 = ifieldOPC(theInstr);
3039 UChar rS_addr = ifieldRegDS(theInstr);
3040 UChar rA_addr = ifieldRegA(theInstr);
ceriond953ebb2005-11-29 13:27:20 +00003041 UInt uimm16 = ifieldUIMM16(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00003042 UChar rB_addr = ifieldRegB(theInstr);
3043 UInt opc2 = ifieldOPClo10(theInstr);
3044 UChar flag_rC = ifieldBIT0(theInstr);
cerionb85e8bb2005-02-16 08:54:33 +00003045
ceriond953ebb2005-11-29 13:27:20 +00003046 IRType ty = mode64 ? Ity_I64 : Ity_I32;
3047 IRTemp rS = newTemp(ty);
3048 IRTemp rA = newTemp(ty);
3049 IRTemp rB = newTemp(ty);
cerione9d361a2005-03-04 17:35:29 +00003050 IRExpr* irx;
ceriond953ebb2005-11-29 13:27:20 +00003051 Bool do_rc = False;
3052
cerion76de5cf2005-11-18 18:25:12 +00003053 assign( rS, getIReg(rS_addr) );
3054 assign( rB, getIReg(rB_addr) );
cerionb85e8bb2005-02-16 08:54:33 +00003055
3056 switch (opc1) {
cerione9d361a2005-03-04 17:35:29 +00003057 case 0x1C: // andi. (AND Immediate, PPC32 p358)
ceriond953ebb2005-11-29 13:27:20 +00003058 DIP("andi. r%u,r%u,0x%x\n", rA_addr, rS_addr, uimm16);
cerion2831b002005-11-30 19:55:22 +00003059 assign( rA, binop( mkSzOp(ty, Iop_And8), mkexpr(rS),
3060 mkSzImm(ty, uimm16)) );
cerion70e24122005-03-16 00:27:37 +00003061 do_rc = True; // Always record to CR
cerion76de5cf2005-11-18 18:25:12 +00003062 flag_rC = 1;
cerionb85e8bb2005-02-16 08:54:33 +00003063 break;
3064
cerione9d361a2005-03-04 17:35:29 +00003065 case 0x1D: // andis. (AND Immediate Shifted, PPC32 p359)
ceriond953ebb2005-11-29 13:27:20 +00003066 DIP("andis r%u,r%u,0x%x\n", rA_addr, rS_addr, uimm16);
cerion2831b002005-11-30 19:55:22 +00003067 assign( rA, binop( mkSzOp(ty, Iop_And8), mkexpr(rS),
3068 mkSzImm(ty, uimm16 << 16)) );
cerion70e24122005-03-16 00:27:37 +00003069 do_rc = True; // Always record to CR
cerion76de5cf2005-11-18 18:25:12 +00003070 flag_rC = 1;
cerionb85e8bb2005-02-16 08:54:33 +00003071 break;
cerion7aa4bbc2005-01-29 09:32:07 +00003072
cerione9d361a2005-03-04 17:35:29 +00003073 case 0x18: // ori (OR Immediate, PPC32 p497)
ceriond953ebb2005-11-29 13:27:20 +00003074 DIP("ori r%u,r%u,0x%x\n", rA_addr, rS_addr, uimm16);
cerion2831b002005-11-30 19:55:22 +00003075 assign( rA, binop( mkSzOp(ty, Iop_Or8), mkexpr(rS),
3076 mkSzImm(ty, uimm16)) );
cerionb85e8bb2005-02-16 08:54:33 +00003077 break;
cerion7aa4bbc2005-01-29 09:32:07 +00003078
cerione9d361a2005-03-04 17:35:29 +00003079 case 0x19: // oris (OR Immediate Shifted, PPC32 p498)
ceriond953ebb2005-11-29 13:27:20 +00003080 DIP("oris r%u,r%u,0x%x\n", rA_addr, rS_addr, uimm16);
cerion2831b002005-11-30 19:55:22 +00003081 assign( rA, binop( mkSzOp(ty, Iop_Or8), mkexpr(rS),
3082 mkSzImm(ty, uimm16 << 16)) );
cerionb85e8bb2005-02-16 08:54:33 +00003083 break;
cerionaabdfbf2005-01-29 12:56:15 +00003084
cerione9d361a2005-03-04 17:35:29 +00003085 case 0x1A: // xori (XOR Immediate, PPC32 p550)
ceriond953ebb2005-11-29 13:27:20 +00003086 DIP("xori r%u,r%u,0x%x\n", rA_addr, rS_addr, uimm16);
cerion2831b002005-11-30 19:55:22 +00003087 assign( rA, binop( mkSzOp(ty, Iop_Xor8), mkexpr(rS),
3088 mkSzImm(ty, uimm16)) );
cerionb85e8bb2005-02-16 08:54:33 +00003089 break;
cerion38674602005-02-08 02:19:25 +00003090
cerione9d361a2005-03-04 17:35:29 +00003091 case 0x1B: // xoris (XOR Immediate Shifted, PPC32 p551)
ceriond953ebb2005-11-29 13:27:20 +00003092 DIP("xoris r%u,r%u,0x%x\n", rA_addr, rS_addr, uimm16);
cerion2831b002005-11-30 19:55:22 +00003093 assign( rA, binop( mkSzOp(ty, Iop_Xor8), mkexpr(rS),
3094 mkSzImm(ty, uimm16 << 16)) );
cerionb85e8bb2005-02-16 08:54:33 +00003095 break;
cerionaabdfbf2005-01-29 12:56:15 +00003096
cerionb85e8bb2005-02-16 08:54:33 +00003097 /* X Form */
3098 case 0x1F:
cerion70e24122005-03-16 00:27:37 +00003099 do_rc = True; // All below record to CR
3100
cerionb85e8bb2005-02-16 08:54:33 +00003101 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00003102 case 0x01C: // and (AND, PPC32 p356)
3103 DIP("and%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003104 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003105 assign(rA, binop( mkSzOp(ty, Iop_And8),
ceriond953ebb2005-11-29 13:27:20 +00003106 mkexpr(rS), mkexpr(rB)));
3107 break;
3108
3109 case 0x03C: // andc (AND with Complement, PPC32 p357)
3110 DIP("andc%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003111 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003112 assign(rA, binop( mkSzOp(ty, Iop_And8), mkexpr(rS),
3113 unop( mkSzOp(ty, Iop_Not8),
ceriond953ebb2005-11-29 13:27:20 +00003114 mkexpr(rB))));
3115 break;
3116
3117 case 0x01A: { // cntlzw (Count Leading Zeros Word, PPC32 p371)
3118 IRExpr* lo32;
3119 if (rB_addr!=0) {
cerion5b2325f2005-12-23 00:55:09 +00003120 vex_printf("dis_int_logic(ppc)(cntlzw,rB_addr)\n");
ceriond953ebb2005-11-29 13:27:20 +00003121 return False;
3122 }
3123 DIP("cntlzw%s r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003124 flag_rC ? ".":"", rA_addr, rS_addr);
ceriond953ebb2005-11-29 13:27:20 +00003125
3126 // mode64: count in low word only
3127 lo32 = mode64 ? unop(Iop_64to32, mkexpr(rS)) : mkexpr(rS);
3128
3129 // Iop_Clz32 undefined for arg==0, so deal with that case:
3130 irx = binop(Iop_CmpNE32, lo32, mkU32(0));
sewardje9d8a262009-07-01 08:06:34 +00003131 assign(rA, mkWidenFrom32(ty,
cerion5b2325f2005-12-23 00:55:09 +00003132 IRExpr_Mux0X( unop(Iop_1Uto8, irx),
3133 mkU32(32),
3134 unop(Iop_Clz32, lo32)),
3135 False));
3136
ceriond953ebb2005-11-29 13:27:20 +00003137 // TODO: alternatively: assign(rA, verbose_Clz32(rS));
3138 break;
3139 }
3140
3141 case 0x11C: // eqv (Equivalent, PPC32 p396)
3142 DIP("eqv%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, unop( mkSzOp(ty, Iop_Not8),
3145 binop( mkSzOp(ty, Iop_Xor8),
ceriond953ebb2005-11-29 13:27:20 +00003146 mkexpr(rS), mkexpr(rB))) );
sewardj20ef5472005-07-21 14:48:31 +00003147 break;
cerion7aa4bbc2005-01-29 09:32:07 +00003148
cerione9d361a2005-03-04 17:35:29 +00003149 case 0x3BA: // extsb (Extend Sign Byte, PPC32 p397
cerion76de5cf2005-11-18 18:25:12 +00003150 if (rB_addr!=0) {
cerion5b2325f2005-12-23 00:55:09 +00003151 vex_printf("dis_int_logic(ppc)(extsb,rB_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003152 return False;
3153 }
ceriond953ebb2005-11-29 13:27:20 +00003154 DIP("extsb%s r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003155 flag_rC ? ".":"", rA_addr, rS_addr);
ceriond953ebb2005-11-29 13:27:20 +00003156 if (mode64)
3157 assign( rA, unop(Iop_8Sto64, unop(Iop_64to8, mkexpr(rS))) );
3158 else
3159 assign( rA, unop(Iop_8Sto32, unop(Iop_32to8, mkexpr(rS))) );
cerionb85e8bb2005-02-16 08:54:33 +00003160 break;
cerion7aa4bbc2005-01-29 09:32:07 +00003161
cerione9d361a2005-03-04 17:35:29 +00003162 case 0x39A: // extsh (Extend Sign Half Word, PPC32 p398)
cerion76de5cf2005-11-18 18:25:12 +00003163 if (rB_addr!=0) {
cerion5b2325f2005-12-23 00:55:09 +00003164 vex_printf("dis_int_logic(ppc)(extsh,rB_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003165 return False;
3166 }
ceriond953ebb2005-11-29 13:27:20 +00003167 DIP("extsh%s r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003168 flag_rC ? ".":"", rA_addr, rS_addr);
ceriond953ebb2005-11-29 13:27:20 +00003169 if (mode64)
cerion5b2325f2005-12-23 00:55:09 +00003170 assign( rA, unop(Iop_16Sto64,
3171 unop(Iop_64to16, mkexpr(rS))) );
ceriond953ebb2005-11-29 13:27:20 +00003172 else
cerion5b2325f2005-12-23 00:55:09 +00003173 assign( rA, unop(Iop_16Sto32,
3174 unop(Iop_32to16, mkexpr(rS))) );
cerionb85e8bb2005-02-16 08:54:33 +00003175 break;
cerion7aa4bbc2005-01-29 09:32:07 +00003176
cerione9d361a2005-03-04 17:35:29 +00003177 case 0x1DC: // nand (NAND, PPC32 p492)
ceriond953ebb2005-11-29 13:27:20 +00003178 DIP("nand%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003179 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003180 assign( rA, unop( mkSzOp(ty, Iop_Not8),
3181 binop( mkSzOp(ty, Iop_And8),
ceriond953ebb2005-11-29 13:27:20 +00003182 mkexpr(rS), mkexpr(rB))) );
cerionb85e8bb2005-02-16 08:54:33 +00003183 break;
3184
cerione9d361a2005-03-04 17:35:29 +00003185 case 0x07C: // nor (NOR, PPC32 p494)
ceriond953ebb2005-11-29 13:27:20 +00003186 DIP("nor%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003187 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003188 assign( rA, unop( mkSzOp(ty, Iop_Not8),
3189 binop( mkSzOp(ty, Iop_Or8),
ceriond953ebb2005-11-29 13:27:20 +00003190 mkexpr(rS), mkexpr(rB))) );
cerionb85e8bb2005-02-16 08:54:33 +00003191 break;
cerion7aa4bbc2005-01-29 09:32:07 +00003192
cerione9d361a2005-03-04 17:35:29 +00003193 case 0x1BC: // or (OR, PPC32 p495)
cerion76de5cf2005-11-18 18:25:12 +00003194 if ((!flag_rC) && rS_addr == rB_addr) {
ceriond953ebb2005-11-29 13:27:20 +00003195 DIP("mr r%u,r%u\n", rA_addr, rS_addr);
cerion76de5cf2005-11-18 18:25:12 +00003196 assign( rA, mkexpr(rS) );
sewardjb51f0f42005-07-18 11:38:02 +00003197 } else {
ceriond953ebb2005-11-29 13:27:20 +00003198 DIP("or%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003199 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003200 assign( rA, binop( mkSzOp(ty, Iop_Or8),
ceriond953ebb2005-11-29 13:27:20 +00003201 mkexpr(rS), mkexpr(rB)) );
sewardjb51f0f42005-07-18 11:38:02 +00003202 }
cerionb85e8bb2005-02-16 08:54:33 +00003203 break;
cerion7aa4bbc2005-01-29 09:32:07 +00003204
cerione9d361a2005-03-04 17:35:29 +00003205 case 0x19C: // orc (OR with Complement, PPC32 p496)
ceriond953ebb2005-11-29 13:27:20 +00003206 DIP("orc%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003207 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003208 assign( rA, binop( mkSzOp(ty, Iop_Or8), mkexpr(rS),
3209 unop(mkSzOp(ty, Iop_Not8), mkexpr(rB))));
cerionb85e8bb2005-02-16 08:54:33 +00003210 break;
3211
cerione9d361a2005-03-04 17:35:29 +00003212 case 0x13C: // xor (XOR, PPC32 p549)
ceriond953ebb2005-11-29 13:27:20 +00003213 DIP("xor%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003214 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003215 assign( rA, binop( mkSzOp(ty, Iop_Xor8),
ceriond953ebb2005-11-29 13:27:20 +00003216 mkexpr(rS), mkexpr(rB)) );
cerionb85e8bb2005-02-16 08:54:33 +00003217 break;
cerion7aa4bbc2005-01-29 09:32:07 +00003218
cerionf0de28c2005-12-13 20:21:11 +00003219
3220 /* 64bit Integer Logical Instructions */
3221 case 0x3DA: // extsw (Extend Sign Word, PPC64 p430)
3222 if (rB_addr!=0) {
cerion5b2325f2005-12-23 00:55:09 +00003223 vex_printf("dis_int_logic(ppc)(extsw,rB_addr)\n");
cerionf0de28c2005-12-13 20:21:11 +00003224 return False;
3225 }
cerion5b2325f2005-12-23 00:55:09 +00003226 DIP("extsw%s r%u,r%u\n", flag_rC ? ".":"", rA_addr, rS_addr);
cerionf0de28c2005-12-13 20:21:11 +00003227 assign(rA, unop(Iop_32Sto64, unop(Iop_64to32, mkexpr(rS))));
3228 break;
3229
cerion5b2325f2005-12-23 00:55:09 +00003230 case 0x03A: // cntlzd (Count Leading Zeros DWord, PPC64 p401)
cerionf0de28c2005-12-13 20:21:11 +00003231 if (rB_addr!=0) {
cerion5b2325f2005-12-23 00:55:09 +00003232 vex_printf("dis_int_logic(ppc)(cntlzd,rB_addr)\n");
cerionf0de28c2005-12-13 20:21:11 +00003233 return False;
3234 }
cerion5b2325f2005-12-23 00:55:09 +00003235 DIP("cntlzd%s r%u,r%u\n",
3236 flag_rC ? ".":"", rA_addr, rS_addr);
cerion07b07a92005-12-22 14:32:35 +00003237 // Iop_Clz64 undefined for arg==0, so deal with that case:
3238 irx = binop(Iop_CmpNE64, mkexpr(rS), mkU64(0));
3239 assign(rA, IRExpr_Mux0X( unop(Iop_1Uto8, irx),
3240 mkU64(64),
3241 unop(Iop_Clz64, mkexpr(rS)) ));
cerion5b2325f2005-12-23 00:55:09 +00003242 // TODO: alternatively: assign(rA, verbose_Clz64(rS));
cerion07b07a92005-12-22 14:32:35 +00003243 break;
cerionf0de28c2005-12-13 20:21:11 +00003244
cerionb85e8bb2005-02-16 08:54:33 +00003245 default:
cerion5b2325f2005-12-23 00:55:09 +00003246 vex_printf("dis_int_logic(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003247 return False;
3248 }
cerionb85e8bb2005-02-16 08:54:33 +00003249 break;
3250
3251 default:
cerion5b2325f2005-12-23 00:55:09 +00003252 vex_printf("dis_int_logic(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003253 return False;
3254 }
cerion70e24122005-03-16 00:27:37 +00003255
cerion76de5cf2005-11-18 18:25:12 +00003256 putIReg( rA_addr, mkexpr(rA) );
3257
3258 if (do_rc && flag_rC) {
3259 set_CR0( mkexpr(rA) );
cerionb85e8bb2005-02-16 08:54:33 +00003260 }
3261 return True;
cerion645c9302005-01-31 10:09:59 +00003262}
3263
3264
3265
cerion3d870a32005-03-18 12:23:33 +00003266/*
3267 Integer Rotate Instructions
3268*/
cerion645c9302005-01-31 10:09:59 +00003269static Bool dis_int_rot ( UInt theInstr )
3270{
cerionf0de28c2005-12-13 20:21:11 +00003271 /* M-Form, MDS-Form */
ceriond953ebb2005-11-29 13:27:20 +00003272 UChar opc1 = ifieldOPC(theInstr);
3273 UChar rS_addr = ifieldRegDS(theInstr);
3274 UChar rA_addr = ifieldRegA(theInstr);
3275 UChar rB_addr = ifieldRegB(theInstr);
3276 UChar sh_imm = rB_addr;
3277 UChar MaskBeg = toUChar( IFIELD( theInstr, 6, 5 ) );
3278 UChar MaskEnd = toUChar( IFIELD( theInstr, 1, 5 ) );
cerionf0de28c2005-12-13 20:21:11 +00003279 UChar msk_imm = toUChar( IFIELD( theInstr, 5, 6 ) );
3280 UChar opc2 = toUChar( IFIELD( theInstr, 2, 3 ) );
3281 UChar b1 = ifieldBIT1(theInstr);
ceriond953ebb2005-11-29 13:27:20 +00003282 UChar flag_rC = ifieldBIT0(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00003283
ceriond953ebb2005-11-29 13:27:20 +00003284 IRType ty = mode64 ? Ity_I64 : Ity_I32;
3285 IRTemp rS = newTemp(ty);
3286 IRTemp rA = newTemp(ty);
3287 IRTemp rB = newTemp(ty);
cerionbb01b7c2005-12-16 13:40:18 +00003288 IRTemp rot = newTemp(ty);
ceriond953ebb2005-11-29 13:27:20 +00003289 IRExpr *r;
cerionf0de28c2005-12-13 20:21:11 +00003290 UInt mask32;
3291 ULong mask64;
ceriond953ebb2005-11-29 13:27:20 +00003292
cerion76de5cf2005-11-18 18:25:12 +00003293 assign( rS, getIReg(rS_addr) );
3294 assign( rB, getIReg(rB_addr) );
ceriond953ebb2005-11-29 13:27:20 +00003295
cerionb85e8bb2005-02-16 08:54:33 +00003296 switch (opc1) {
ceriond953ebb2005-11-29 13:27:20 +00003297 case 0x14: {
cerion5b2325f2005-12-23 00:55:09 +00003298 // rlwimi (Rotate Left Word Imm then Mask Insert, PPC32 p500)
3299 DIP("rlwimi%s r%u,r%u,%d,%d,%d\n", flag_rC ? ".":"",
ceriond953ebb2005-11-29 13:27:20 +00003300 rA_addr, rS_addr, sh_imm, MaskBeg, MaskEnd);
3301 if (mode64) {
3302 // tmp32 = (ROTL(rS_Lo32, Imm)
3303 // rA = ((tmp32 || tmp32) & mask64) | (rA & ~mask64)
cerionf0de28c2005-12-13 20:21:11 +00003304 mask64 = MASK64(31-MaskEnd, 31-MaskBeg);
ceriond953ebb2005-11-29 13:27:20 +00003305 r = ROTL( unop(Iop_64to32, mkexpr(rS) ), mkU8(sh_imm) );
3306 r = unop(Iop_32Uto64, r);
cerion5b2325f2005-12-23 00:55:09 +00003307 assign( rot, binop(Iop_Or64, r,
3308 binop(Iop_Shl64, r, mkU8(32))) );
ceriond953ebb2005-11-29 13:27:20 +00003309 assign( rA,
3310 binop(Iop_Or64,
cerionbb01b7c2005-12-16 13:40:18 +00003311 binop(Iop_And64, mkexpr(rot), mkU64(mask64)),
cerionf0de28c2005-12-13 20:21:11 +00003312 binop(Iop_And64, getIReg(rA_addr), mkU64(~mask64))) );
sewardj26b33202005-10-07 09:45:16 +00003313 }
3314 else {
ceriond953ebb2005-11-29 13:27:20 +00003315 // rA = (ROTL(rS, Imm) & mask) | (rA & ~mask);
cerionf0de28c2005-12-13 20:21:11 +00003316 mask32 = MASK32(31-MaskEnd, 31-MaskBeg);
3317 r = ROTL(mkexpr(rS), mkU8(sh_imm));
ceriond953ebb2005-11-29 13:27:20 +00003318 assign( rA,
3319 binop(Iop_Or32,
cerionf0de28c2005-12-13 20:21:11 +00003320 binop(Iop_And32, mkU32(mask32), r),
3321 binop(Iop_And32, getIReg(rA_addr), mkU32(~mask32))) );
sewardj26b33202005-10-07 09:45:16 +00003322 }
cerionb85e8bb2005-02-16 08:54:33 +00003323 break;
ceriond953ebb2005-11-29 13:27:20 +00003324 }
cerion45b70ff2005-01-31 17:03:25 +00003325
ceriond953ebb2005-11-29 13:27:20 +00003326 case 0x15: {
cerion5b2325f2005-12-23 00:55:09 +00003327 // rlwinm (Rotate Left Word Imm then AND with Mask, PPC32 p501)
ceriond953ebb2005-11-29 13:27:20 +00003328 vassert(MaskBeg < 32);
3329 vassert(MaskEnd < 32);
3330 vassert(sh_imm < 32);
3331
3332 if (mode64) {
sewardjef4433b2006-10-19 03:01:09 +00003333 IRTemp rTmp = newTemp(Ity_I64);
cerionf0de28c2005-12-13 20:21:11 +00003334 mask64 = MASK64(31-MaskEnd, 31-MaskBeg);
cerion5b2325f2005-12-23 00:55:09 +00003335 DIP("rlwinm%s r%u,r%u,%d,%d,%d\n", flag_rC ? ".":"",
ceriond953ebb2005-11-29 13:27:20 +00003336 rA_addr, rS_addr, sh_imm, MaskBeg, MaskEnd);
3337 // tmp32 = (ROTL(rS_Lo32, Imm)
3338 // rA = ((tmp32 || tmp32) & mask64)
3339 r = ROTL( unop(Iop_64to32, mkexpr(rS) ), mkU8(sh_imm) );
3340 r = unop(Iop_32Uto64, r);
sewardjef4433b2006-10-19 03:01:09 +00003341 assign( rTmp, r );
3342 r = NULL;
3343 assign( rot, binop(Iop_Or64, mkexpr(rTmp),
3344 binop(Iop_Shl64, mkexpr(rTmp), mkU8(32))) );
cerionbb01b7c2005-12-16 13:40:18 +00003345 assign( rA, binop(Iop_And64, mkexpr(rot), mkU64(mask64)) );
ceriond953ebb2005-11-29 13:27:20 +00003346 }
3347 else {
3348 if (MaskBeg == 0 && sh_imm+MaskEnd == 31) {
3349 /* Special-case the ,n,0,31-n form as that is just n-bit
cerion5b2325f2005-12-23 00:55:09 +00003350 shift left, PPC32 p501 */
3351 DIP("slwi%s r%u,r%u,%d\n", flag_rC ? ".":"",
ceriond953ebb2005-11-29 13:27:20 +00003352 rA_addr, rS_addr, sh_imm);
3353 assign( rA, binop(Iop_Shl32, mkexpr(rS), mkU8(sh_imm)) );
3354 }
cerion2831b002005-11-30 19:55:22 +00003355 else if (MaskEnd == 31 && sh_imm+MaskBeg == 32) {
3356 /* Special-case the ,32-n,n,31 form as that is just n-bit
cerion5b2325f2005-12-23 00:55:09 +00003357 unsigned shift right, PPC32 p501 */
3358 DIP("srwi%s r%u,r%u,%d\n", flag_rC ? ".":"",
sewardjee4a8592006-10-19 00:15:25 +00003359 rA_addr, rS_addr, MaskBeg);
cerion2831b002005-11-30 19:55:22 +00003360 assign( rA, binop(Iop_Shr32, mkexpr(rS), mkU8(MaskBeg)) );
3361 }
3362 else {
3363 /* General case. */
cerionf0de28c2005-12-13 20:21:11 +00003364 mask32 = MASK32(31-MaskEnd, 31-MaskBeg);
cerion5b2325f2005-12-23 00:55:09 +00003365 DIP("rlwinm%s r%u,r%u,%d,%d,%d\n", flag_rC ? ".":"",
cerion2831b002005-11-30 19:55:22 +00003366 rA_addr, rS_addr, sh_imm, MaskBeg, MaskEnd);
3367 // rA = ROTL(rS, Imm) & mask
cerion5b2325f2005-12-23 00:55:09 +00003368 assign( rA, binop(Iop_And32,
3369 ROTL(mkexpr(rS), mkU8(sh_imm)),
cerionf0de28c2005-12-13 20:21:11 +00003370 mkU32(mask32)) );
cerion2831b002005-11-30 19:55:22 +00003371 }
ceriond953ebb2005-11-29 13:27:20 +00003372 }
sewardjc9659532005-07-21 21:33:57 +00003373 break;
ceriond953ebb2005-11-29 13:27:20 +00003374 }
3375
3376 case 0x17: {
3377 // rlwnm (Rotate Left Word then AND with Mask, PPC32 p503
cerion5b2325f2005-12-23 00:55:09 +00003378 DIP("rlwnm%s r%u,r%u,r%u,%d,%d\n", flag_rC ? ".":"",
ceriond953ebb2005-11-29 13:27:20 +00003379 rA_addr, rS_addr, rB_addr, MaskBeg, MaskEnd);
3380 if (mode64) {
cerionf0de28c2005-12-13 20:21:11 +00003381 mask64 = MASK64(31-MaskEnd, 31-MaskBeg);
cerionbb01b7c2005-12-16 13:40:18 +00003382 /* weird insn alert!
3383 tmp32 = (ROTL(rS_Lo32, rB[0-4])
3384 rA = ((tmp32 || tmp32) & mask64)
3385 */
ceriond953ebb2005-11-29 13:27:20 +00003386 // note, ROTL does the masking, so we don't do it here
3387 r = ROTL( unop(Iop_64to32, mkexpr(rS)),
cerionbb01b7c2005-12-16 13:40:18 +00003388 unop(Iop_64to8, mkexpr(rB)) );
ceriond953ebb2005-11-29 13:27:20 +00003389 r = unop(Iop_32Uto64, r);
cerionbb01b7c2005-12-16 13:40:18 +00003390 assign(rot, binop(Iop_Or64, r, binop(Iop_Shl64, r, mkU8(32))));
3391 assign( rA, binop(Iop_And64, mkexpr(rot), mkU64(mask64)) );
ceriond953ebb2005-11-29 13:27:20 +00003392 } else {
cerionf0de28c2005-12-13 20:21:11 +00003393 mask32 = MASK32(31-MaskEnd, 31-MaskBeg);
ceriond953ebb2005-11-29 13:27:20 +00003394 // rA = ROTL(rS, rB[0-4]) & mask
3395 // note, ROTL does the masking, so we don't do it here
3396 assign( rA, binop(Iop_And32,
cerion5b2325f2005-12-23 00:55:09 +00003397 ROTL(mkexpr(rS),
3398 unop(Iop_32to8, mkexpr(rB))),
cerionf0de28c2005-12-13 20:21:11 +00003399 mkU32(mask32)) );
ceriond953ebb2005-11-29 13:27:20 +00003400 }
3401 break;
3402 }
cerion45b70ff2005-01-31 17:03:25 +00003403
cerionf0de28c2005-12-13 20:21:11 +00003404 /* 64bit Integer Rotates */
3405 case 0x1E: {
3406 msk_imm = ((msk_imm & 1) << 5) | (msk_imm >> 1);
3407 sh_imm |= b1 << 5;
3408
3409 vassert( msk_imm < 64 );
3410 vassert( sh_imm < 64 );
3411
3412 switch (opc2) {
cerion07b07a92005-12-22 14:32:35 +00003413 case 0x4: {
3414 /* r = ROTL64( rS, rB_lo6) */
3415 r = ROTL( mkexpr(rS), unop(Iop_64to8, mkexpr(rB)) );
3416
cerion5b2325f2005-12-23 00:55:09 +00003417 if (b1 == 0) { // rldcl (Rotl DWord, Clear Left, PPC64 p555)
3418 DIP("rldcl%s r%u,r%u,r%u,%u\n", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00003419 rA_addr, rS_addr, rB_addr, msk_imm);
cerion07b07a92005-12-22 14:32:35 +00003420 // note, ROTL does the masking, so we don't do it here
cerionf0de28c2005-12-13 20:21:11 +00003421 mask64 = MASK64(0, 63-msk_imm);
3422 assign( rA, binop(Iop_And64, r, mkU64(mask64)) );
3423 break;
cerion5b2325f2005-12-23 00:55:09 +00003424 } else { // rldcr (Rotl DWord, Clear Right, PPC64 p556)
3425 DIP("rldcr%s r%u,r%u,r%u,%u\n", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00003426 rA_addr, rS_addr, rB_addr, msk_imm);
cerionf0de28c2005-12-13 20:21:11 +00003427 mask64 = MASK64(63-msk_imm, 63);
3428 assign( rA, binop(Iop_And64, r, mkU64(mask64)) );
3429 break;
3430 }
3431 break;
cerion07b07a92005-12-22 14:32:35 +00003432 }
cerion5b2325f2005-12-23 00:55:09 +00003433 case 0x2: // rldic (Rotl DWord Imm, Clear, PPC64 p557)
3434 DIP("rldic%s r%u,r%u,%u,%u\n", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00003435 rA_addr, rS_addr, sh_imm, msk_imm);
3436 r = ROTL(mkexpr(rS), mkU8(sh_imm));
3437 mask64 = MASK64(sh_imm, 63-msk_imm);
3438 assign( rA, binop(Iop_And64, r, mkU64(mask64)) );
3439 break;
3440 // later: deal with special case: (msk_imm==0) => SHL(sh_imm)
3441 /*
3442 Hmm... looks like this'll do the job more simply:
3443 r = SHL(rS, sh_imm)
3444 m = ~(1 << (63-msk_imm))
3445 assign(rA, r & m);
3446 */
3447
cerion5b2325f2005-12-23 00:55:09 +00003448 case 0x0: // rldicl (Rotl DWord Imm, Clear Left, PPC64 p558)
sewardjee4a8592006-10-19 00:15:25 +00003449 if (mode64
3450 && sh_imm + msk_imm == 64 && msk_imm >= 1 && msk_imm <= 63) {
3451 /* special-case the ,64-n,n form as that is just
3452 unsigned shift-right by n */
3453 DIP("srdi%s r%u,r%u,%u\n",
3454 flag_rC ? ".":"", rA_addr, rS_addr, msk_imm);
3455 assign( rA, binop(Iop_Shr64, mkexpr(rS), mkU8(msk_imm)) );
3456 } else {
3457 DIP("rldicl%s r%u,r%u,%u,%u\n", flag_rC ? ".":"",
3458 rA_addr, rS_addr, sh_imm, msk_imm);
3459 r = ROTL(mkexpr(rS), mkU8(sh_imm));
3460 mask64 = MASK64(0, 63-msk_imm);
3461 assign( rA, binop(Iop_And64, r, mkU64(mask64)) );
3462 }
cerionf0de28c2005-12-13 20:21:11 +00003463 break;
cerionf0de28c2005-12-13 20:21:11 +00003464
cerion5b2325f2005-12-23 00:55:09 +00003465 case 0x1: // rldicr (Rotl DWord Imm, Clear Right, PPC64 p559)
sewardjee4a8592006-10-19 00:15:25 +00003466 if (mode64
3467 && sh_imm + msk_imm == 63 && sh_imm >= 1 && sh_imm <= 63) {
3468 /* special-case the ,n,63-n form as that is just
3469 shift-left by n */
3470 DIP("sldi%s r%u,r%u,%u\n",
3471 flag_rC ? ".":"", rA_addr, rS_addr, sh_imm);
3472 assign( rA, binop(Iop_Shl64, mkexpr(rS), mkU8(sh_imm)) );
3473 } else {
3474 DIP("rldicr%s r%u,r%u,%u,%u\n", flag_rC ? ".":"",
3475 rA_addr, rS_addr, sh_imm, msk_imm);
3476 r = ROTL(mkexpr(rS), mkU8(sh_imm));
3477 mask64 = MASK64(63-msk_imm, 63);
3478 assign( rA, binop(Iop_And64, r, mkU64(mask64)) );
3479 }
cerionf0de28c2005-12-13 20:21:11 +00003480 break;
cerionf0de28c2005-12-13 20:21:11 +00003481
cerion5b2325f2005-12-23 00:55:09 +00003482 case 0x3: { // rldimi (Rotl DWord Imm, Mask Insert, PPC64 p560)
cerion07b07a92005-12-22 14:32:35 +00003483 IRTemp rA_orig = newTemp(ty);
cerion5b2325f2005-12-23 00:55:09 +00003484 DIP("rldimi%s r%u,r%u,%u,%u\n", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00003485 rA_addr, rS_addr, sh_imm, msk_imm);
3486 r = ROTL(mkexpr(rS), mkU8(sh_imm));
3487 mask64 = MASK64(sh_imm, 63-msk_imm);
cerion07b07a92005-12-22 14:32:35 +00003488 assign( rA_orig, getIReg(rA_addr) );
cerionf0de28c2005-12-13 20:21:11 +00003489 assign( rA, binop(Iop_Or64,
3490 binop(Iop_And64, mkU64(mask64), r),
cerion5b2325f2005-12-23 00:55:09 +00003491 binop(Iop_And64, mkU64(~mask64),
3492 mkexpr(rA_orig))) );
cerionf0de28c2005-12-13 20:21:11 +00003493 break;
cerion07b07a92005-12-22 14:32:35 +00003494 }
cerionf0de28c2005-12-13 20:21:11 +00003495 default:
cerion5b2325f2005-12-23 00:55:09 +00003496 vex_printf("dis_int_rot(ppc)(opc2)\n");
cerionf0de28c2005-12-13 20:21:11 +00003497 return False;
3498 }
3499 break;
3500 }
3501
cerionb85e8bb2005-02-16 08:54:33 +00003502 default:
cerion5b2325f2005-12-23 00:55:09 +00003503 vex_printf("dis_int_rot(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003504 return False;
3505 }
cerion645c9302005-01-31 10:09:59 +00003506
cerion76de5cf2005-11-18 18:25:12 +00003507 putIReg( rA_addr, mkexpr(rA) );
3508
3509 if (flag_rC) {
3510 set_CR0( mkexpr(rA) );
cerionb85e8bb2005-02-16 08:54:33 +00003511 }
3512 return True;
cerion645c9302005-01-31 10:09:59 +00003513}
3514
3515
cerion3d870a32005-03-18 12:23:33 +00003516/*
3517 Integer Load Instructions
3518*/
cerion645c9302005-01-31 10:09:59 +00003519static Bool dis_int_load ( UInt theInstr )
3520{
cerionf0de28c2005-12-13 20:21:11 +00003521 /* D-Form, X-Form, DS-Form */
cerion76de5cf2005-11-18 18:25:12 +00003522 UChar opc1 = ifieldOPC(theInstr);
3523 UChar rD_addr = ifieldRegDS(theInstr);
3524 UChar rA_addr = ifieldRegA(theInstr);
ceriond953ebb2005-11-29 13:27:20 +00003525 UInt uimm16 = ifieldUIMM16(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00003526 UChar rB_addr = ifieldRegB(theInstr);
3527 UInt opc2 = ifieldOPClo10(theInstr);
cerionf0de28c2005-12-13 20:21:11 +00003528 UChar b1 = ifieldBIT1(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00003529 UChar b0 = ifieldBIT0(theInstr);
3530
ceriond953ebb2005-11-29 13:27:20 +00003531 Int simm16 = extend_s_16to32(uimm16);
3532 IRType ty = mode64 ? Ity_I64 : Ity_I32;
ceriond953ebb2005-11-29 13:27:20 +00003533 IRTemp EA = newTemp(ty);
3534 IRExpr* val;
cerionedf7fc52005-11-18 20:57:41 +00003535
cerionf0de28c2005-12-13 20:21:11 +00003536 switch (opc1) {
3537 case 0x1F: // register offset
ceriond953ebb2005-11-29 13:27:20 +00003538 assign( EA, ea_rAor0_idxd( rA_addr, rB_addr ) );
cerionf0de28c2005-12-13 20:21:11 +00003539 break;
sewardj6d834222007-03-09 14:24:38 +00003540 case 0x3A: // immediate offset: 64bit: ld/ldu/lwa: mask off
3541 // lowest 2 bits of immediate before forming EA
cerionf0de28c2005-12-13 20:21:11 +00003542 simm16 = simm16 & 0xFFFFFFFC;
3543 default: // immediate offset
3544 assign( EA, ea_rAor0_simm( rA_addr, simm16 ) );
3545 break;
ceriond953ebb2005-11-29 13:27:20 +00003546 }
cerione9d361a2005-03-04 17:35:29 +00003547
cerionb85e8bb2005-02-16 08:54:33 +00003548 switch (opc1) {
cerione9d361a2005-03-04 17:35:29 +00003549 case 0x22: // lbz (Load B & Zero, PPC32 p433)
ceriond953ebb2005-11-29 13:27:20 +00003550 DIP("lbz r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3551 val = loadBE(Ity_I8, mkexpr(EA));
sewardje9d8a262009-07-01 08:06:34 +00003552 putIReg( rD_addr, mkWidenFrom8(ty, val, False) );
cerionb85e8bb2005-02-16 08:54:33 +00003553 break;
3554
cerion5b2325f2005-12-23 00:55:09 +00003555 case 0x23: // lbzu (Load B & Zero, Update, PPC32 p434)
cerion76de5cf2005-11-18 18:25:12 +00003556 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003557 vex_printf("dis_int_load(ppc)(lbzu,rA_addr|rD_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003558 return False;
3559 }
ceriond953ebb2005-11-29 13:27:20 +00003560 DIP("lbzu r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3561 val = loadBE(Ity_I8, mkexpr(EA));
sewardje9d8a262009-07-01 08:06:34 +00003562 putIReg( rD_addr, mkWidenFrom8(ty, val, False) );
ceriond953ebb2005-11-29 13:27:20 +00003563 putIReg( rA_addr, mkexpr(EA) );
cerionb85e8bb2005-02-16 08:54:33 +00003564 break;
3565
cerion5b2325f2005-12-23 00:55:09 +00003566 case 0x2A: // lha (Load HW Alg, PPC32 p445)
ceriond953ebb2005-11-29 13:27:20 +00003567 DIP("lha r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3568 val = loadBE(Ity_I16, mkexpr(EA));
sewardje9d8a262009-07-01 08:06:34 +00003569 putIReg( rD_addr, mkWidenFrom16(ty, val, True) );
cerionb85e8bb2005-02-16 08:54:33 +00003570 break;
cerion645c9302005-01-31 10:09:59 +00003571
cerion5b2325f2005-12-23 00:55:09 +00003572 case 0x2B: // lhau (Load HW Alg, Update, PPC32 p446)
cerion76de5cf2005-11-18 18:25:12 +00003573 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003574 vex_printf("dis_int_load(ppc)(lhau,rA_addr|rD_addr)\n");
cerioncb14e732005-09-09 16:38:19 +00003575 return False;
3576 }
ceriond953ebb2005-11-29 13:27:20 +00003577 DIP("lhau r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3578 val = loadBE(Ity_I16, mkexpr(EA));
sewardje9d8a262009-07-01 08:06:34 +00003579 putIReg( rD_addr, mkWidenFrom16(ty, val, True) );
ceriond953ebb2005-11-29 13:27:20 +00003580 putIReg( rA_addr, mkexpr(EA) );
cerioncb14e732005-09-09 16:38:19 +00003581 break;
cerionb85e8bb2005-02-16 08:54:33 +00003582
cerione9d361a2005-03-04 17:35:29 +00003583 case 0x28: // lhz (Load HW & Zero, PPC32 p450)
ceriond953ebb2005-11-29 13:27:20 +00003584 DIP("lhz r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3585 val = loadBE(Ity_I16, mkexpr(EA));
sewardje9d8a262009-07-01 08:06:34 +00003586 putIReg( rD_addr, mkWidenFrom16(ty, val, False) );
cerionb85e8bb2005-02-16 08:54:33 +00003587 break;
3588
cerion5b2325f2005-12-23 00:55:09 +00003589 case 0x29: // lhzu (Load HW & and Zero, Update, PPC32 p451)
cerion76de5cf2005-11-18 18:25:12 +00003590 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003591 vex_printf("dis_int_load(ppc)(lhzu,rA_addr|rD_addr)\n");
sewardj0e2cc672005-07-29 21:58:51 +00003592 return False;
3593 }
ceriond953ebb2005-11-29 13:27:20 +00003594 DIP("lhzu r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3595 val = loadBE(Ity_I16, mkexpr(EA));
sewardje9d8a262009-07-01 08:06:34 +00003596 putIReg( rD_addr, mkWidenFrom16(ty, val, False) );
ceriond953ebb2005-11-29 13:27:20 +00003597 putIReg( rA_addr, mkexpr(EA) );
sewardj0e2cc672005-07-29 21:58:51 +00003598 break;
cerion645c9302005-01-31 10:09:59 +00003599
cerione9d361a2005-03-04 17:35:29 +00003600 case 0x20: // lwz (Load W & Zero, PPC32 p460)
ceriond953ebb2005-11-29 13:27:20 +00003601 DIP("lwz r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3602 val = loadBE(Ity_I32, mkexpr(EA));
sewardje9d8a262009-07-01 08:06:34 +00003603 putIReg( rD_addr, mkWidenFrom32(ty, val, False) );
cerionb85e8bb2005-02-16 08:54:33 +00003604 break;
3605
cerion5b2325f2005-12-23 00:55:09 +00003606 case 0x21: // lwzu (Load W & Zero, Update, PPC32 p461))
cerion76de5cf2005-11-18 18:25:12 +00003607 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003608 vex_printf("dis_int_load(ppc)(lwzu,rA_addr|rD_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003609 return False;
3610 }
ceriond953ebb2005-11-29 13:27:20 +00003611 DIP("lwzu r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3612 val = loadBE(Ity_I32, mkexpr(EA));
sewardje9d8a262009-07-01 08:06:34 +00003613 putIReg( rD_addr, mkWidenFrom32(ty, val, False) );
ceriond953ebb2005-11-29 13:27:20 +00003614 putIReg( rA_addr, mkexpr(EA) );
cerionb85e8bb2005-02-16 08:54:33 +00003615 break;
3616
3617 /* X Form */
3618 case 0x1F:
3619 if (b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00003620 vex_printf("dis_int_load(ppc)(Ox1F,b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003621 return False;
3622 }
cerion645c9302005-01-31 10:09:59 +00003623
cerionb85e8bb2005-02-16 08:54:33 +00003624 switch (opc2) {
cerion5b2325f2005-12-23 00:55:09 +00003625 case 0x077: // lbzux (Load B & Zero, Update Indexed, PPC32 p435)
ceriond953ebb2005-11-29 13:27:20 +00003626 DIP("lbzux r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
cerion76de5cf2005-11-18 18:25:12 +00003627 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003628 vex_printf("dis_int_load(ppc)(lwzux,rA_addr|rD_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003629 return False;
3630 }
ceriond953ebb2005-11-29 13:27:20 +00003631 val = loadBE(Ity_I8, mkexpr(EA));
sewardje9d8a262009-07-01 08:06:34 +00003632 putIReg( rD_addr, mkWidenFrom8(ty, val, False) );
ceriond953ebb2005-11-29 13:27:20 +00003633 putIReg( rA_addr, mkexpr(EA) );
cerionb85e8bb2005-02-16 08:54:33 +00003634 break;
3635
cerion5b2325f2005-12-23 00:55:09 +00003636 case 0x057: // lbzx (Load B & Zero, Indexed, PPC32 p436)
ceriond953ebb2005-11-29 13:27:20 +00003637 DIP("lbzx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3638 val = loadBE(Ity_I8, mkexpr(EA));
sewardje9d8a262009-07-01 08:06:34 +00003639 putIReg( rD_addr, mkWidenFrom8(ty, val, False) );
cerionb85e8bb2005-02-16 08:54:33 +00003640 break;
3641
cerion5b2325f2005-12-23 00:55:09 +00003642 case 0x177: // lhaux (Load HW Alg, Update Indexed, PPC32 p447)
cerion76de5cf2005-11-18 18:25:12 +00003643 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003644 vex_printf("dis_int_load(ppc)(lhaux,rA_addr|rD_addr)\n");
cerioncb14e732005-09-09 16:38:19 +00003645 return False;
3646 }
ceriond953ebb2005-11-29 13:27:20 +00003647 DIP("lhaux r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3648 val = loadBE(Ity_I16, mkexpr(EA));
sewardje9d8a262009-07-01 08:06:34 +00003649 putIReg( rD_addr, mkWidenFrom16(ty, val, True) );
ceriond953ebb2005-11-29 13:27:20 +00003650 putIReg( rA_addr, mkexpr(EA) );
cerioncb14e732005-09-09 16:38:19 +00003651 break;
cerionb85e8bb2005-02-16 08:54:33 +00003652
cerion5b2325f2005-12-23 00:55:09 +00003653 case 0x157: // lhax (Load HW Alg, Indexed, PPC32 p448)
ceriond953ebb2005-11-29 13:27:20 +00003654 DIP("lhax r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3655 val = loadBE(Ity_I16, mkexpr(EA));
sewardje9d8a262009-07-01 08:06:34 +00003656 putIReg( rD_addr, mkWidenFrom16(ty, val, True) );
cerionb85e8bb2005-02-16 08:54:33 +00003657 break;
3658
cerion5b2325f2005-12-23 00:55:09 +00003659 case 0x137: // lhzux (Load HW & Zero, Update Indexed, PPC32 p452)
cerion76de5cf2005-11-18 18:25:12 +00003660 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003661 vex_printf("dis_int_load(ppc)(lhzux,rA_addr|rD_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003662 return False;
3663 }
ceriond953ebb2005-11-29 13:27:20 +00003664 DIP("lhzux r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3665 val = loadBE(Ity_I16, mkexpr(EA));
sewardje9d8a262009-07-01 08:06:34 +00003666 putIReg( rD_addr, mkWidenFrom16(ty, val, False) );
ceriond953ebb2005-11-29 13:27:20 +00003667 putIReg( rA_addr, mkexpr(EA) );
cerionb85e8bb2005-02-16 08:54:33 +00003668 break;
3669
cerion5b2325f2005-12-23 00:55:09 +00003670 case 0x117: // lhzx (Load HW & Zero, Indexed, PPC32 p453)
ceriond953ebb2005-11-29 13:27:20 +00003671 DIP("lhzx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3672 val = loadBE(Ity_I16, mkexpr(EA));
sewardje9d8a262009-07-01 08:06:34 +00003673 putIReg( rD_addr, mkWidenFrom16(ty, val, False) );
cerionb85e8bb2005-02-16 08:54:33 +00003674 break;
cerion44997f22005-01-31 18:45:59 +00003675
cerion5b2325f2005-12-23 00:55:09 +00003676 case 0x037: // lwzux (Load W & Zero, Update Indexed, PPC32 p462)
cerion76de5cf2005-11-18 18:25:12 +00003677 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003678 vex_printf("dis_int_load(ppc)(lwzux,rA_addr|rD_addr)\n");
sewardj7787af42005-08-04 18:32:19 +00003679 return False;
3680 }
ceriond953ebb2005-11-29 13:27:20 +00003681 DIP("lwzux r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3682 val = loadBE(Ity_I32, mkexpr(EA));
sewardje9d8a262009-07-01 08:06:34 +00003683 putIReg( rD_addr, mkWidenFrom32(ty, val, False) );
ceriond953ebb2005-11-29 13:27:20 +00003684 putIReg( rA_addr, mkexpr(EA) );
sewardj7787af42005-08-04 18:32:19 +00003685 break;
cerionb85e8bb2005-02-16 08:54:33 +00003686
cerion5b2325f2005-12-23 00:55:09 +00003687 case 0x017: // lwzx (Load W & Zero, Indexed, PPC32 p463)
ceriond953ebb2005-11-29 13:27:20 +00003688 DIP("lwzx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3689 val = loadBE(Ity_I32, mkexpr(EA));
sewardje9d8a262009-07-01 08:06:34 +00003690 putIReg( rD_addr, mkWidenFrom32(ty, val, False) );
cerionb85e8bb2005-02-16 08:54:33 +00003691 break;
cerion44997f22005-01-31 18:45:59 +00003692
cerionf0de28c2005-12-13 20:21:11 +00003693
3694 /* 64bit Loads */
cerion5b2325f2005-12-23 00:55:09 +00003695 case 0x035: // ldux (Load DWord, Update Indexed, PPC64 p475)
cerionf0de28c2005-12-13 20:21:11 +00003696 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003697 vex_printf("dis_int_load(ppc)(ldux,rA_addr|rD_addr)\n");
cerionf0de28c2005-12-13 20:21:11 +00003698 return False;
3699 }
3700 DIP("ldux r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3701 putIReg( rD_addr, loadBE(Ity_I64, mkexpr(EA)) );
3702 putIReg( rA_addr, mkexpr(EA) );
3703 break;
3704
cerion5b2325f2005-12-23 00:55:09 +00003705 case 0x015: // ldx (Load DWord, Indexed, PPC64 p476)
cerionf0de28c2005-12-13 20:21:11 +00003706 DIP("ldx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3707 putIReg( rD_addr, loadBE(Ity_I64, mkexpr(EA)) );
3708 break;
3709
cerion5b2325f2005-12-23 00:55:09 +00003710 case 0x175: // lwaux (Load W Alg, Update Indexed, PPC64 p501)
cerionf0de28c2005-12-13 20:21:11 +00003711 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003712 vex_printf("dis_int_load(ppc)(lwaux,rA_addr|rD_addr)\n");
cerionf0de28c2005-12-13 20:21:11 +00003713 return False;
3714 }
3715 DIP("lwaux r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
cerion5b2325f2005-12-23 00:55:09 +00003716 putIReg( rD_addr,
3717 unop(Iop_32Sto64, loadBE(Ity_I32, mkexpr(EA))) );
cerionf0de28c2005-12-13 20:21:11 +00003718 putIReg( rA_addr, mkexpr(EA) );
3719 break;
3720
cerion5b2325f2005-12-23 00:55:09 +00003721 case 0x155: // lwax (Load W Alg, Indexed, PPC64 p502)
cerionf0de28c2005-12-13 20:21:11 +00003722 DIP("lwax r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
cerion5b2325f2005-12-23 00:55:09 +00003723 putIReg( rD_addr,
3724 unop(Iop_32Sto64, loadBE(Ity_I32, mkexpr(EA))) );
cerionf0de28c2005-12-13 20:21:11 +00003725 break;
3726
cerionb85e8bb2005-02-16 08:54:33 +00003727 default:
cerion5b2325f2005-12-23 00:55:09 +00003728 vex_printf("dis_int_load(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003729 return False;
3730 }
3731 break;
cerionf0de28c2005-12-13 20:21:11 +00003732
sewardj6d834222007-03-09 14:24:38 +00003733 /* DS Form - 64bit Loads. In each case EA will have been formed
3734 with the lowest 2 bits masked off the immediate offset. */
cerionf0de28c2005-12-13 20:21:11 +00003735 case 0x3A:
sewardj6d834222007-03-09 14:24:38 +00003736 switch ((b1<<1) | b0) {
cerion5b2325f2005-12-23 00:55:09 +00003737 case 0x0: // ld (Load DWord, PPC64 p472)
cerionf0de28c2005-12-13 20:21:11 +00003738 DIP("ld r%u,%d(r%u)\n", rD_addr, simm16, rA_addr);
3739 putIReg( rD_addr, loadBE(Ity_I64, mkexpr(EA)) );
3740 break;
3741
cerion5b2325f2005-12-23 00:55:09 +00003742 case 0x1: // ldu (Load DWord, Update, PPC64 p474)
cerionf0de28c2005-12-13 20:21:11 +00003743 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003744 vex_printf("dis_int_load(ppc)(ldu,rA_addr|rD_addr)\n");
cerionf0de28c2005-12-13 20:21:11 +00003745 return False;
3746 }
3747 DIP("ldu r%u,%d(r%u)\n", rD_addr, simm16, rA_addr);
cerionf0de28c2005-12-13 20:21:11 +00003748 putIReg( rD_addr, loadBE(Ity_I64, mkexpr(EA)) );
3749 putIReg( rA_addr, mkexpr(EA) );
3750 break;
3751
cerion5b2325f2005-12-23 00:55:09 +00003752 case 0x2: // lwa (Load Word Alg, PPC64 p499)
cerionf0de28c2005-12-13 20:21:11 +00003753 DIP("lwa r%u,%d(r%u)\n", rD_addr, simm16, rA_addr);
cerion5b2325f2005-12-23 00:55:09 +00003754 putIReg( rD_addr,
3755 unop(Iop_32Sto64, loadBE(Ity_I32, mkexpr(EA))) );
cerionf0de28c2005-12-13 20:21:11 +00003756 break;
3757
3758 default:
cerion5b2325f2005-12-23 00:55:09 +00003759 vex_printf("dis_int_load(ppc)(0x3A, opc2)\n");
cerionf0de28c2005-12-13 20:21:11 +00003760 return False;
3761 }
3762 break;
3763
cerionb85e8bb2005-02-16 08:54:33 +00003764 default:
cerion5b2325f2005-12-23 00:55:09 +00003765 vex_printf("dis_int_load(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003766 return False;
3767 }
3768 return True;
cerion7aa4bbc2005-01-29 09:32:07 +00003769}
3770
3771
3772
cerion3d870a32005-03-18 12:23:33 +00003773/*
3774 Integer Store Instructions
3775*/
sewardjdd40fdf2006-12-24 02:20:24 +00003776static Bool dis_int_store ( UInt theInstr, VexAbiInfo* vbi )
ceriond23be4e2005-01-31 07:23:07 +00003777{
cerionf0de28c2005-12-13 20:21:11 +00003778 /* D-Form, X-Form, DS-Form */
cerionedf7fc52005-11-18 20:57:41 +00003779 UChar opc1 = ifieldOPC(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00003780 UInt rS_addr = ifieldRegDS(theInstr);
3781 UInt rA_addr = ifieldRegA(theInstr);
ceriond953ebb2005-11-29 13:27:20 +00003782 UInt uimm16 = ifieldUIMM16(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00003783 UInt rB_addr = ifieldRegB(theInstr);
3784 UInt opc2 = ifieldOPClo10(theInstr);
cerionf0de28c2005-12-13 20:21:11 +00003785 UChar b1 = ifieldBIT1(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00003786 UChar b0 = ifieldBIT0(theInstr);
3787
ceriond953ebb2005-11-29 13:27:20 +00003788 Int simm16 = extend_s_16to32(uimm16);
3789 IRType ty = mode64 ? Ity_I64 : Ity_I32;
3790 IRTemp rS = newTemp(ty);
3791 IRTemp rB = newTemp(ty);
3792 IRTemp EA = newTemp(ty);
cerionb85e8bb2005-02-16 08:54:33 +00003793
cerion76de5cf2005-11-18 18:25:12 +00003794 assign( rB, getIReg(rB_addr) );
3795 assign( rS, getIReg(rS_addr) );
cerionb85e8bb2005-02-16 08:54:33 +00003796
cerionf0de28c2005-12-13 20:21:11 +00003797 switch (opc1) {
3798 case 0x1F: // register offset
ceriond953ebb2005-11-29 13:27:20 +00003799 assign( EA, ea_rAor0_idxd( rA_addr, rB_addr ) );
cerionf0de28c2005-12-13 20:21:11 +00003800 break;
sewardj6d834222007-03-09 14:24:38 +00003801 case 0x3E: // immediate offset: 64bit: std/stdu: mask off
3802 // lowest 2 bits of immediate before forming EA
cerionf0de28c2005-12-13 20:21:11 +00003803 simm16 = simm16 & 0xFFFFFFFC;
3804 default: // immediate offset
3805 assign( EA, ea_rAor0_simm( rA_addr, simm16 ) );
3806 break;
ceriond953ebb2005-11-29 13:27:20 +00003807 }
3808
cerionb85e8bb2005-02-16 08:54:33 +00003809 switch (opc1) {
sewardjafe85832005-09-09 10:25:39 +00003810 case 0x26: // stb (Store B, PPC32 p509)
cerion76de5cf2005-11-18 18:25:12 +00003811 DIP("stb r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
sewardje9d8a262009-07-01 08:06:34 +00003812 storeBE( mkexpr(EA), mkNarrowTo8(ty, mkexpr(rS)) );
sewardjafe85832005-09-09 10:25:39 +00003813 break;
sewardjb51f0f42005-07-18 11:38:02 +00003814
cerion5b2325f2005-12-23 00:55:09 +00003815 case 0x27: // stbu (Store B, Update, PPC32 p510)
cerion76de5cf2005-11-18 18:25:12 +00003816 if (rA_addr == 0 ) {
cerion5b2325f2005-12-23 00:55:09 +00003817 vex_printf("dis_int_store(ppc)(stbu,rA_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003818 return False;
3819 }
cerion76de5cf2005-11-18 18:25:12 +00003820 DIP("stbu r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
ceriond953ebb2005-11-29 13:27:20 +00003821 putIReg( rA_addr, mkexpr(EA) );
sewardje9d8a262009-07-01 08:06:34 +00003822 storeBE( mkexpr(EA), mkNarrowTo8(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003823 break;
ceriond23be4e2005-01-31 07:23:07 +00003824
cerione9d361a2005-03-04 17:35:29 +00003825 case 0x2C: // sth (Store HW, PPC32 p522)
cerion76de5cf2005-11-18 18:25:12 +00003826 DIP("sth r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
sewardje9d8a262009-07-01 08:06:34 +00003827 storeBE( mkexpr(EA), mkNarrowTo16(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003828 break;
3829
cerion5b2325f2005-12-23 00:55:09 +00003830 case 0x2D: // sthu (Store HW, Update, PPC32 p524)
cerion76de5cf2005-11-18 18:25:12 +00003831 if (rA_addr == 0) {
cerion5b2325f2005-12-23 00:55:09 +00003832 vex_printf("dis_int_store(ppc)(sthu,rA_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003833 return False;
3834 }
cerion76de5cf2005-11-18 18:25:12 +00003835 DIP("sthu r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
ceriond953ebb2005-11-29 13:27:20 +00003836 putIReg( rA_addr, mkexpr(EA) );
sewardje9d8a262009-07-01 08:06:34 +00003837 storeBE( mkexpr(EA), mkNarrowTo16(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003838 break;
ceriond23be4e2005-01-31 07:23:07 +00003839
cerione9d361a2005-03-04 17:35:29 +00003840 case 0x24: // stw (Store W, PPC32 p530)
cerion76de5cf2005-11-18 18:25:12 +00003841 DIP("stw r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
sewardje9d8a262009-07-01 08:06:34 +00003842 storeBE( mkexpr(EA), mkNarrowTo32(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003843 break;
ceriond23be4e2005-01-31 07:23:07 +00003844
cerion5b2325f2005-12-23 00:55:09 +00003845 case 0x25: // stwu (Store W, Update, PPC32 p534)
cerion76de5cf2005-11-18 18:25:12 +00003846 if (rA_addr == 0) {
cerion5b2325f2005-12-23 00:55:09 +00003847 vex_printf("dis_int_store(ppc)(stwu,rA_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003848 return False;
3849 }
cerion76de5cf2005-11-18 18:25:12 +00003850 DIP("stwu r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
ceriond953ebb2005-11-29 13:27:20 +00003851 putIReg( rA_addr, mkexpr(EA) );
sewardje9d8a262009-07-01 08:06:34 +00003852 storeBE( mkexpr(EA), mkNarrowTo32(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003853 break;
3854
cerionf0de28c2005-12-13 20:21:11 +00003855 /* X Form : all these use EA_indexed */
cerionb85e8bb2005-02-16 08:54:33 +00003856 case 0x1F:
3857 if (b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00003858 vex_printf("dis_int_store(ppc)(0x1F,b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003859 return False;
3860 }
cerion44997f22005-01-31 18:45:59 +00003861
cerionb85e8bb2005-02-16 08:54:33 +00003862 switch (opc2) {
cerion5b2325f2005-12-23 00:55:09 +00003863 case 0x0F7: // stbux (Store B, Update Indexed, PPC32 p511)
cerion76de5cf2005-11-18 18:25:12 +00003864 if (rA_addr == 0) {
cerion5b2325f2005-12-23 00:55:09 +00003865 vex_printf("dis_int_store(ppc)(stbux,rA_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003866 return False;
3867 }
cerion76de5cf2005-11-18 18:25:12 +00003868 DIP("stbux r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
ceriond953ebb2005-11-29 13:27:20 +00003869 putIReg( rA_addr, mkexpr(EA) );
sewardje9d8a262009-07-01 08:06:34 +00003870 storeBE( mkexpr(EA), mkNarrowTo8(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003871 break;
3872
cerione9d361a2005-03-04 17:35:29 +00003873 case 0x0D7: // stbx (Store B Indexed, PPC32 p512)
cerion76de5cf2005-11-18 18:25:12 +00003874 DIP("stbx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
sewardje9d8a262009-07-01 08:06:34 +00003875 storeBE( mkexpr(EA), mkNarrowTo8(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003876 break;
3877
cerion5b2325f2005-12-23 00:55:09 +00003878 case 0x1B7: // sthux (Store HW, Update Indexed, PPC32 p525)
cerion76de5cf2005-11-18 18:25:12 +00003879 if (rA_addr == 0) {
cerion5b2325f2005-12-23 00:55:09 +00003880 vex_printf("dis_int_store(ppc)(sthux,rA_addr)\n");
cerioncb14e732005-09-09 16:38:19 +00003881 return False;
3882 }
cerion76de5cf2005-11-18 18:25:12 +00003883 DIP("sthux r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
ceriond953ebb2005-11-29 13:27:20 +00003884 putIReg( rA_addr, mkexpr(EA) );
sewardje9d8a262009-07-01 08:06:34 +00003885 storeBE( mkexpr(EA), mkNarrowTo16(ty, mkexpr(rS)) );
cerioncb14e732005-09-09 16:38:19 +00003886 break;
cerionb85e8bb2005-02-16 08:54:33 +00003887
cerione9d361a2005-03-04 17:35:29 +00003888 case 0x197: // sthx (Store HW Indexed, PPC32 p526)
cerion76de5cf2005-11-18 18:25:12 +00003889 DIP("sthx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
sewardje9d8a262009-07-01 08:06:34 +00003890 storeBE( mkexpr(EA), mkNarrowTo16(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003891 break;
3892
cerion5b2325f2005-12-23 00:55:09 +00003893 case 0x0B7: // stwux (Store W, Update Indexed, PPC32 p535)
cerion76de5cf2005-11-18 18:25:12 +00003894 if (rA_addr == 0) {
cerion5b2325f2005-12-23 00:55:09 +00003895 vex_printf("dis_int_store(ppc)(stwux,rA_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003896 return False;
3897 }
cerion76de5cf2005-11-18 18:25:12 +00003898 DIP("stwux r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
ceriond953ebb2005-11-29 13:27:20 +00003899 putIReg( rA_addr, mkexpr(EA) );
sewardje9d8a262009-07-01 08:06:34 +00003900 storeBE( mkexpr(EA), mkNarrowTo32(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003901 break;
cerion44997f22005-01-31 18:45:59 +00003902
cerione9d361a2005-03-04 17:35:29 +00003903 case 0x097: // stwx (Store W Indexed, PPC32 p536)
cerion76de5cf2005-11-18 18:25:12 +00003904 DIP("stwx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
sewardje9d8a262009-07-01 08:06:34 +00003905 storeBE( mkexpr(EA), mkNarrowTo32(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003906 break;
3907
cerionf0de28c2005-12-13 20:21:11 +00003908
3909 /* 64bit Stores */
cerion5b2325f2005-12-23 00:55:09 +00003910 case 0x0B5: // stdux (Store DWord, Update Indexed, PPC64 p584)
cerionf0de28c2005-12-13 20:21:11 +00003911 if (rA_addr == 0) {
cerion5b2325f2005-12-23 00:55:09 +00003912 vex_printf("dis_int_store(ppc)(stdux,rA_addr)\n");
cerionf0de28c2005-12-13 20:21:11 +00003913 return False;
3914 }
3915 DIP("stdux r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
3916 putIReg( rA_addr, mkexpr(EA) );
3917 storeBE( mkexpr(EA), mkexpr(rS) );
3918 break;
3919
cerion5b2325f2005-12-23 00:55:09 +00003920 case 0x095: // stdx (Store DWord Indexed, PPC64 p585)
cerionf0de28c2005-12-13 20:21:11 +00003921 DIP("stdx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
3922 storeBE( mkexpr(EA), mkexpr(rS) );
3923 break;
3924
cerionb85e8bb2005-02-16 08:54:33 +00003925 default:
cerion5b2325f2005-12-23 00:55:09 +00003926 vex_printf("dis_int_store(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003927 return False;
3928 }
3929 break;
cerionf0de28c2005-12-13 20:21:11 +00003930
sewardj6d834222007-03-09 14:24:38 +00003931 /* DS Form - 64bit Stores. In each case EA will have been formed
3932 with the lowest 2 bits masked off the immediate offset. */
cerionf0de28c2005-12-13 20:21:11 +00003933 case 0x3E:
sewardj6d834222007-03-09 14:24:38 +00003934 switch ((b1<<1) | b0) {
cerion5b2325f2005-12-23 00:55:09 +00003935 case 0x0: // std (Store DWord, PPC64 p580)
cerionf0de28c2005-12-13 20:21:11 +00003936 DIP("std r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
3937 storeBE( mkexpr(EA), mkexpr(rS) );
3938 break;
3939
cerion5b2325f2005-12-23 00:55:09 +00003940 case 0x1: // stdu (Store DWord, Update, PPC64 p583)
cerionf0de28c2005-12-13 20:21:11 +00003941 DIP("stdu r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
3942 putIReg( rA_addr, mkexpr(EA) );
3943 storeBE( mkexpr(EA), mkexpr(rS) );
3944 break;
3945
3946 default:
cerion5b2325f2005-12-23 00:55:09 +00003947 vex_printf("dis_int_load(ppc)(0x3A, opc2)\n");
cerionf0de28c2005-12-13 20:21:11 +00003948 return False;
3949 }
3950 break;
3951
cerionb85e8bb2005-02-16 08:54:33 +00003952 default:
cerion5b2325f2005-12-23 00:55:09 +00003953 vex_printf("dis_int_store(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003954 return False;
3955 }
3956 return True;
ceriond23be4e2005-01-31 07:23:07 +00003957}
3958
3959
3960
sewardj7787af42005-08-04 18:32:19 +00003961/*
3962 Integer Load/Store Multiple Instructions
3963*/
3964static Bool dis_int_ldst_mult ( UInt theInstr )
3965{
3966 /* D-Form */
cerion76de5cf2005-11-18 18:25:12 +00003967 UChar opc1 = ifieldOPC(theInstr);
3968 UChar rD_addr = ifieldRegDS(theInstr);
3969 UChar rS_addr = rD_addr;
3970 UChar rA_addr = ifieldRegA(theInstr);
cerion2831b002005-11-30 19:55:22 +00003971 UInt uimm16 = ifieldUIMM16(theInstr);
cerionedf7fc52005-11-18 20:57:41 +00003972
ceriond953ebb2005-11-29 13:27:20 +00003973 Int simm16 = extend_s_16to32(uimm16);
3974 IRType ty = mode64 ? Ity_I64 : Ity_I32;
3975 IRTemp EA = newTemp(ty);
3976 UInt r = 0;
3977 UInt ea_off = 0;
sewardj7787af42005-08-04 18:32:19 +00003978 IRExpr* irx_addr;
cerionedf7fc52005-11-18 20:57:41 +00003979
ceriond953ebb2005-11-29 13:27:20 +00003980 assign( EA, ea_rAor0_simm( rA_addr, simm16 ) );
3981
sewardj7787af42005-08-04 18:32:19 +00003982 switch (opc1) {
ceriond953ebb2005-11-29 13:27:20 +00003983 case 0x2E: // lmw (Load Multiple Word, PPC32 p454)
3984 if (rA_addr >= rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003985 vex_printf("dis_int_ldst_mult(ppc)(lmw,rA_addr)\n");
sewardj7787af42005-08-04 18:32:19 +00003986 return False;
ceriond953ebb2005-11-29 13:27:20 +00003987 }
3988 DIP("lmw r%u,%d(r%u)\n", rD_addr, simm16, rA_addr);
3989 for (r = rD_addr; r <= 31; r++) {
3990 irx_addr = binop(Iop_Add32, mkexpr(EA), mkU32(ea_off));
sewardje9d8a262009-07-01 08:06:34 +00003991 putIReg( r, mkWidenFrom32(ty, loadBE(Ity_I32, irx_addr ),
3992 False) );
ceriond953ebb2005-11-29 13:27:20 +00003993 ea_off += 4;
3994 }
3995 break;
3996
3997 case 0x2F: // stmw (Store Multiple Word, PPC32 p527)
3998 DIP("stmw r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
3999 for (r = rS_addr; r <= 31; r++) {
4000 irx_addr = binop(Iop_Add32, mkexpr(EA), mkU32(ea_off));
sewardje9d8a262009-07-01 08:06:34 +00004001 storeBE( irx_addr, mkNarrowTo32(ty, getIReg(r)) );
ceriond953ebb2005-11-29 13:27:20 +00004002 ea_off += 4;
4003 }
4004 break;
4005
4006 default:
cerion5b2325f2005-12-23 00:55:09 +00004007 vex_printf("dis_int_ldst_mult(ppc)(opc1)\n");
ceriond953ebb2005-11-29 13:27:20 +00004008 return False;
sewardj7787af42005-08-04 18:32:19 +00004009 }
4010 return True;
4011}
4012
4013
4014
sewardj87e651f2005-09-09 08:31:18 +00004015/*
4016 Integer Load/Store String Instructions
4017*/
4018static
4019void generate_lsw_sequence ( IRTemp tNBytes, // # bytes, :: Ity_I32
4020 IRTemp EA, // EA
4021 Int rD, // first dst register
ceriond953ebb2005-11-29 13:27:20 +00004022 Int maxBytes ) // 32 or 128
sewardj87e651f2005-09-09 08:31:18 +00004023{
4024 Int i, shift = 24;
4025 IRExpr* e_nbytes = mkexpr(tNBytes);
ceriond953ebb2005-11-29 13:27:20 +00004026 IRExpr* e_EA = mkexpr(EA);
4027 IRType ty = mode64 ? Ity_I64 : Ity_I32;
sewardj87e651f2005-09-09 08:31:18 +00004028
sewardj5876fa12005-09-09 09:35:29 +00004029 vassert(rD >= 0 && rD < 32);
sewardj87e651f2005-09-09 08:31:18 +00004030 rD--; if (rD < 0) rD = 31;
4031
4032 for (i = 0; i < maxBytes; i++) {
sewardj87e651f2005-09-09 08:31:18 +00004033 /* if (nBytes < (i+1)) goto NIA; */
4034 stmt( IRStmt_Exit( binop(Iop_CmpLT32U, e_nbytes, mkU32(i+1)),
4035 Ijk_Boring,
cerion2831b002005-11-30 19:55:22 +00004036 mkSzConst( ty, nextInsnAddr()) ));
sewardj87e651f2005-09-09 08:31:18 +00004037 /* when crossing into a new dest register, set it to zero. */
4038 if ((i % 4) == 0) {
4039 rD++; if (rD == 32) rD = 0;
cerion2831b002005-11-30 19:55:22 +00004040 putIReg(rD, mkSzImm(ty, 0));
sewardj87e651f2005-09-09 08:31:18 +00004041 shift = 24;
4042 }
4043 /* rD |= (8Uto32(*(EA+i))) << shift */
4044 vassert(shift == 0 || shift == 8 || shift == 16 || shift == 24);
sewardj2ef8a372006-01-28 17:07:19 +00004045 putIReg(
4046 rD,
sewardje9d8a262009-07-01 08:06:34 +00004047 mkWidenFrom32(
sewardj2ef8a372006-01-28 17:07:19 +00004048 ty,
4049 binop(
4050 Iop_Or32,
sewardje9d8a262009-07-01 08:06:34 +00004051 mkNarrowTo32(ty, getIReg(rD)),
sewardj2ef8a372006-01-28 17:07:19 +00004052 binop(
4053 Iop_Shl32,
4054 unop(
4055 Iop_8Uto32,
4056 loadBE(Ity_I8,
4057 binop(mkSzOp(ty,Iop_Add8), e_EA, mkSzImm(ty,i)))
4058 ),
4059 mkU8(toUChar(shift))
4060 )
4061 ),
4062 /*Signed*/False
4063 )
4064 );
sewardj87e651f2005-09-09 08:31:18 +00004065 shift -= 8;
4066 }
4067}
4068
sewardj5876fa12005-09-09 09:35:29 +00004069static
4070void generate_stsw_sequence ( IRTemp tNBytes, // # bytes, :: Ity_I32
4071 IRTemp EA, // EA
4072 Int rS, // first src register
ceriond953ebb2005-11-29 13:27:20 +00004073 Int maxBytes ) // 32 or 128
sewardj5876fa12005-09-09 09:35:29 +00004074{
4075 Int i, shift = 24;
4076 IRExpr* e_nbytes = mkexpr(tNBytes);
ceriond953ebb2005-11-29 13:27:20 +00004077 IRExpr* e_EA = mkexpr(EA);
4078 IRType ty = mode64 ? Ity_I64 : Ity_I32;
sewardj5876fa12005-09-09 09:35:29 +00004079
4080 vassert(rS >= 0 && rS < 32);
4081 rS--; if (rS < 0) rS = 31;
4082
4083 for (i = 0; i < maxBytes; i++) {
4084 /* if (nBytes < (i+1)) goto NIA; */
4085 stmt( IRStmt_Exit( binop(Iop_CmpLT32U, e_nbytes, mkU32(i+1)),
4086 Ijk_Boring,
cerion2831b002005-11-30 19:55:22 +00004087 mkSzConst( ty, nextInsnAddr() ) ));
sewardj5876fa12005-09-09 09:35:29 +00004088 /* check for crossing into a new src register. */
4089 if ((i % 4) == 0) {
4090 rS++; if (rS == 32) rS = 0;
4091 shift = 24;
4092 }
4093 /* *(EA+i) = 32to8(rS >> shift) */
4094 vassert(shift == 0 || shift == 8 || shift == 16 || shift == 24);
4095 storeBE(
cerion2831b002005-11-30 19:55:22 +00004096 binop(mkSzOp(ty,Iop_Add8), e_EA, mkSzImm(ty,i)),
sewardj5876fa12005-09-09 09:35:29 +00004097 unop(Iop_32to8,
cerion2831b002005-11-30 19:55:22 +00004098 binop(Iop_Shr32,
sewardje9d8a262009-07-01 08:06:34 +00004099 mkNarrowTo32(ty, getIReg(rS)),
cerion2831b002005-11-30 19:55:22 +00004100 mkU8(toUChar(shift))))
sewardj5876fa12005-09-09 09:35:29 +00004101 );
4102 shift -= 8;
4103 }
4104}
4105
sewardj87e651f2005-09-09 08:31:18 +00004106static Bool dis_int_ldst_str ( UInt theInstr, /*OUT*/Bool* stopHere )
4107{
4108 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00004109 UChar opc1 = ifieldOPC(theInstr);
4110 UChar rD_addr = ifieldRegDS(theInstr);
4111 UChar rS_addr = rD_addr;
4112 UChar rA_addr = ifieldRegA(theInstr);
4113 UChar rB_addr = ifieldRegB(theInstr);
4114 UChar NumBytes = rB_addr;
4115 UInt opc2 = ifieldOPClo10(theInstr);
4116 UChar b0 = ifieldBIT0(theInstr);
cerionedf7fc52005-11-18 20:57:41 +00004117
ceriond953ebb2005-11-29 13:27:20 +00004118 IRType ty = mode64 ? Ity_I64 : Ity_I32;
4119 IRTemp t_EA = newTemp(ty);
sewardj87e651f2005-09-09 08:31:18 +00004120 IRTemp t_nbytes = IRTemp_INVALID;
cerionedf7fc52005-11-18 20:57:41 +00004121
sewardj87e651f2005-09-09 08:31:18 +00004122 *stopHere = False;
cerionedf7fc52005-11-18 20:57:41 +00004123
sewardj87e651f2005-09-09 08:31:18 +00004124 if (opc1 != 0x1F || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00004125 vex_printf("dis_int_ldst_str(ppc)(opc1)\n");
sewardj87e651f2005-09-09 08:31:18 +00004126 return False;
4127 }
4128
4129 switch (opc2) {
4130 case 0x255: // lswi (Load String Word Immediate, PPC32 p455)
4131 /* NB: does not reject the case where RA is in the range of
4132 registers to be loaded. It should. */
ceriond953ebb2005-11-29 13:27:20 +00004133 DIP("lswi r%u,r%u,%d\n", rD_addr, rA_addr, NumBytes);
4134 assign( t_EA, ea_rAor0(rA_addr) );
sewardj2ef8a372006-01-28 17:07:19 +00004135 if (NumBytes == 8 && !mode64) {
sewardj87e651f2005-09-09 08:31:18 +00004136 /* Special case hack */
cerion76de5cf2005-11-18 18:25:12 +00004137 /* rD = Mem[EA]; (rD+1)%32 = Mem[EA+4] */
4138 putIReg( rD_addr,
sewardj87e651f2005-09-09 08:31:18 +00004139 loadBE(Ity_I32, mkexpr(t_EA)) );
cerion76de5cf2005-11-18 18:25:12 +00004140 putIReg( (rD_addr+1) % 32,
ceriond953ebb2005-11-29 13:27:20 +00004141 loadBE(Ity_I32,
4142 binop(Iop_Add32, mkexpr(t_EA), mkU32(4))) );
sewardj87e651f2005-09-09 08:31:18 +00004143 } else {
4144 t_nbytes = newTemp(Ity_I32);
cerion76de5cf2005-11-18 18:25:12 +00004145 assign( t_nbytes, mkU32(NumBytes==0 ? 32 : NumBytes) );
ceriond953ebb2005-11-29 13:27:20 +00004146 generate_lsw_sequence( t_nbytes, t_EA, rD_addr, 32 );
sewardj87e651f2005-09-09 08:31:18 +00004147 *stopHere = True;
4148 }
4149 return True;
4150
4151 case 0x215: // lswx (Load String Word Indexed, PPC32 p456)
4152 /* NB: does not reject the case where RA is in the range of
4153 registers to be loaded. It should. Although considering
4154 that that can only be detected at run time, it's not easy to
4155 do so. */
cerion76de5cf2005-11-18 18:25:12 +00004156 if (rD_addr == rA_addr || rD_addr == rB_addr)
sewardj87e651f2005-09-09 08:31:18 +00004157 return False;
cerion76de5cf2005-11-18 18:25:12 +00004158 if (rD_addr == 0 && rA_addr == 0)
sewardj87e651f2005-09-09 08:31:18 +00004159 return False;
ceriond953ebb2005-11-29 13:27:20 +00004160 DIP("lswx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
sewardj87e651f2005-09-09 08:31:18 +00004161 t_nbytes = newTemp(Ity_I32);
ceriond953ebb2005-11-29 13:27:20 +00004162 assign( t_EA, ea_rAor0_idxd(rA_addr,rB_addr) );
cerionedf7fc52005-11-18 20:57:41 +00004163 assign( t_nbytes, unop( Iop_8Uto32, getXER_BC() ) );
ceriond953ebb2005-11-29 13:27:20 +00004164 generate_lsw_sequence( t_nbytes, t_EA, rD_addr, 128 );
sewardj87e651f2005-09-09 08:31:18 +00004165 *stopHere = True;
4166 return True;
4167
sewardj5876fa12005-09-09 09:35:29 +00004168 case 0x2D5: // stswi (Store String Word Immediate, PPC32 p528)
ceriond953ebb2005-11-29 13:27:20 +00004169 DIP("stswi r%u,r%u,%d\n", rS_addr, rA_addr, NumBytes);
4170 assign( t_EA, ea_rAor0(rA_addr) );
sewardj2ef8a372006-01-28 17:07:19 +00004171 if (NumBytes == 8 && !mode64) {
sewardj5876fa12005-09-09 09:35:29 +00004172 /* Special case hack */
cerion76de5cf2005-11-18 18:25:12 +00004173 /* Mem[EA] = rD; Mem[EA+4] = (rD+1)%32 */
4174 storeBE( mkexpr(t_EA),
4175 getIReg(rD_addr) );
4176 storeBE( binop(Iop_Add32, mkexpr(t_EA), mkU32(4)),
4177 getIReg((rD_addr+1) % 32) );
sewardj5876fa12005-09-09 09:35:29 +00004178 } else {
4179 t_nbytes = newTemp(Ity_I32);
cerion76de5cf2005-11-18 18:25:12 +00004180 assign( t_nbytes, mkU32(NumBytes==0 ? 32 : NumBytes) );
ceriond953ebb2005-11-29 13:27:20 +00004181 generate_stsw_sequence( t_nbytes, t_EA, rD_addr, 32 );
sewardj5876fa12005-09-09 09:35:29 +00004182 *stopHere = True;
4183 }
4184 return True;
4185
sewardj5876fa12005-09-09 09:35:29 +00004186 case 0x295: // stswx (Store String Word Indexed, PPC32 p529)
ceriond953ebb2005-11-29 13:27:20 +00004187 DIP("stswx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
sewardj5876fa12005-09-09 09:35:29 +00004188 t_nbytes = newTemp(Ity_I32);
ceriond953ebb2005-11-29 13:27:20 +00004189 assign( t_EA, ea_rAor0_idxd(rA_addr,rB_addr) );
cerionedf7fc52005-11-18 20:57:41 +00004190 assign( t_nbytes, unop( Iop_8Uto32, getXER_BC() ) );
ceriond953ebb2005-11-29 13:27:20 +00004191 generate_stsw_sequence( t_nbytes, t_EA, rS_addr, 128 );
sewardj5876fa12005-09-09 09:35:29 +00004192 *stopHere = True;
4193 return True;
sewardj87e651f2005-09-09 08:31:18 +00004194
4195 default:
cerion5b2325f2005-12-23 00:55:09 +00004196 vex_printf("dis_int_ldst_str(ppc)(opc2)\n");
sewardj87e651f2005-09-09 08:31:18 +00004197 return False;
4198 }
4199 return True;
4200}
4201
cerion094d1392005-06-20 13:45:57 +00004202
sewardjb51f0f42005-07-18 11:38:02 +00004203/* ------------------------------------------------------------------
4204 Integer Branch Instructions
4205 ------------------------------------------------------------------ */
cerion645c9302005-01-31 10:09:59 +00004206
cerion45552a92005-02-03 18:20:22 +00004207/*
4208 Branch helper function
4209 ok = BO[2] | ((CTR[0] != 0) ^ BO[1])
sewardjb51f0f42005-07-18 11:38:02 +00004210 Returns an I32 which is 0x00000000 if the ctr condition failed
4211 and 0xFFFFFFFF otherwise.
cerion45552a92005-02-03 18:20:22 +00004212*/
sewardjb51f0f42005-07-18 11:38:02 +00004213static IRExpr* /* :: Ity_I32 */ branch_ctr_ok( UInt BO )
cerion45552a92005-02-03 18:20:22 +00004214{
ceriond953ebb2005-11-29 13:27:20 +00004215 IRType ty = mode64 ? Ity_I64 : Ity_I32;
sewardjb51f0f42005-07-18 11:38:02 +00004216 IRTemp ok = newTemp(Ity_I32);
cerioned623db2005-06-20 12:42:04 +00004217
cerionf0de28c2005-12-13 20:21:11 +00004218 if ((BO >> 2) & 1) { // independent of ctr
sewardjb51f0f42005-07-18 11:38:02 +00004219 assign( ok, mkU32(0xFFFFFFFF) );
cerionb85e8bb2005-02-16 08:54:33 +00004220 } else {
cerionf0de28c2005-12-13 20:21:11 +00004221 if ((BO >> 1) & 1) { // ctr == 0 ?
ceriond953ebb2005-11-29 13:27:20 +00004222 assign( ok, unop( Iop_1Sto32,
cerion4e2c2b32006-01-02 13:35:51 +00004223 binop( mkSzOp(ty, Iop_CmpEQ8),
4224 getGST( PPC_GST_CTR ),
4225 mkSzImm(ty,0))) );
cerionf0de28c2005-12-13 20:21:11 +00004226 } else { // ctr != 0 ?
sewardjb51f0f42005-07-18 11:38:02 +00004227 assign( ok, unop( Iop_1Sto32,
cerion4e2c2b32006-01-02 13:35:51 +00004228 binop( mkSzOp(ty, Iop_CmpNE8),
4229 getGST( PPC_GST_CTR ),
4230 mkSzImm(ty,0))) );
cerionb85e8bb2005-02-16 08:54:33 +00004231 }
4232 }
4233 return mkexpr(ok);
cerion45552a92005-02-03 18:20:22 +00004234}
4235
sewardjb51f0f42005-07-18 11:38:02 +00004236
cerion45552a92005-02-03 18:20:22 +00004237/*
sewardjb51f0f42005-07-18 11:38:02 +00004238 Branch helper function cond_ok = BO[4] | (CR[BI] == BO[3])
4239 Returns an I32 which is either 0 if the condition failed or
4240 some arbitrary nonzero value otherwise. */
4241
4242static IRExpr* /* :: Ity_I32 */ branch_cond_ok( UInt BO, UInt BI )
cerion45552a92005-02-03 18:20:22 +00004243{
sewardjb51f0f42005-07-18 11:38:02 +00004244 Int where;
4245 IRTemp res = newTemp(Ity_I32);
cerionb85e8bb2005-02-16 08:54:33 +00004246 IRTemp cr_bi = newTemp(Ity_I32);
4247
sewardjb51f0f42005-07-18 11:38:02 +00004248 if ((BO >> 4) & 1) {
4249 assign( res, mkU32(1) );
cerionb85e8bb2005-02-16 08:54:33 +00004250 } else {
sewardjb51f0f42005-07-18 11:38:02 +00004251 // ok = (CR[BI] == BO[3]) Note, the following relies on
4252 // getCRbit_anywhere returning a value which
4253 // is either zero or has exactly 1 bit set.
4254 assign( cr_bi, getCRbit_anywhere( BI, &where ) );
cerione9d361a2005-03-04 17:35:29 +00004255
4256 if ((BO >> 3) & 1) {
sewardjb51f0f42005-07-18 11:38:02 +00004257 /* We can use cr_bi as-is. */
4258 assign( res, mkexpr(cr_bi) );
cerione9d361a2005-03-04 17:35:29 +00004259 } else {
sewardjb51f0f42005-07-18 11:38:02 +00004260 /* We have to invert the sense of the information held in
4261 cr_bi. For that we need to know which bit
cerion76de5cf2005-11-18 18:25:12 +00004262 getCRbit_anywhere regards as significant. */
cerion5b2325f2005-12-23 00:55:09 +00004263 assign( res, binop(Iop_Xor32, mkexpr(cr_bi),
4264 mkU32(1<<where)) );
cerionb85e8bb2005-02-16 08:54:33 +00004265 }
4266 }
sewardjb51f0f42005-07-18 11:38:02 +00004267 return mkexpr(res);
cerion45552a92005-02-03 18:20:22 +00004268}
4269
4270
cerion3d870a32005-03-18 12:23:33 +00004271/*
4272 Integer Branch Instructions
4273*/
sewardj9d540e52005-10-08 11:28:16 +00004274static Bool dis_branch ( UInt theInstr,
sewardjdd40fdf2006-12-24 02:20:24 +00004275 VexAbiInfo* vbi,
sewardj9d540e52005-10-08 11:28:16 +00004276 /*OUT*/DisResult* dres,
sewardjc716aea2006-01-17 01:48:46 +00004277 Bool (*resteerOkFn)(void*,Addr64),
4278 void* callback_opaque )
cerion91ad5362005-01-27 23:02:41 +00004279{
ceriond953ebb2005-11-29 13:27:20 +00004280 UChar opc1 = ifieldOPC(theInstr);
4281 UChar BO = ifieldRegDS(theInstr);
4282 UChar BI = ifieldRegA(theInstr);
4283 UInt BD_u16 = ifieldUIMM16(theInstr) & 0xFFFFFFFC; /* mask off */
4284 UChar b11to15 = ifieldRegB(theInstr);
4285 UInt opc2 = ifieldOPClo10(theInstr);
4286 UInt LI_u26 = ifieldUIMM26(theInstr) & 0xFFFFFFFC; /* mask off */
4287 UChar flag_AA = ifieldBIT1(theInstr);
4288 UChar flag_LK = ifieldBIT0(theInstr);
4289
cerion2831b002005-11-30 19:55:22 +00004290 IRType ty = mode64 ? Ity_I64 : Ity_I32;
ceriond953ebb2005-11-29 13:27:20 +00004291 Addr64 tgt = 0;
4292 Int BD = extend_s_16to32(BD_u16);
cerion2831b002005-11-30 19:55:22 +00004293 IRTemp do_branch = newTemp(Ity_I32);
4294 IRTemp ctr_ok = newTemp(Ity_I32);
4295 IRTemp cond_ok = newTemp(Ity_I32);
4296 IRExpr* e_nia = mkSzImm(ty, nextInsnAddr());
4297 IRConst* c_nia = mkSzConst(ty, nextInsnAddr());
sewardjdf07b882005-11-29 18:19:11 +00004298 IRTemp lr_old = newTemp(ty);
ceriond953ebb2005-11-29 13:27:20 +00004299
cerionb85e8bb2005-02-16 08:54:33 +00004300 /* Hack to pass through code that just wants to read the PC */
4301 if (theInstr == 0x429F0005) {
sewardjb51f0f42005-07-18 11:38:02 +00004302 DIP("bcl 0x%x, 0x%x (a.k.a mr lr,cia+4)\n", BO, BI);
ceriond953ebb2005-11-29 13:27:20 +00004303 putGST( PPC_GST_LR, e_nia );
cerionb85e8bb2005-02-16 08:54:33 +00004304 return True;
sewardjb51f0f42005-07-18 11:38:02 +00004305 }
sewardj9d540e52005-10-08 11:28:16 +00004306
4307 /* The default what-next. Individual cases can override it. */
4308 dres->whatNext = Dis_StopHere;
4309
cerionb85e8bb2005-02-16 08:54:33 +00004310 switch (opc1) {
cerione9d361a2005-03-04 17:35:29 +00004311 case 0x12: // b (Branch, PPC32 p360)
cerion4561acb2005-02-21 14:07:48 +00004312 if (flag_AA) {
cerion2831b002005-11-30 19:55:22 +00004313 tgt = mkSzAddr( ty, extend_s_26to64(LI_u26) );
cerion4561acb2005-02-21 14:07:48 +00004314 } else {
cerion2831b002005-11-30 19:55:22 +00004315 tgt = mkSzAddr( ty, guest_CIA_curr_instr +
4316 (Long)extend_s_26to64(LI_u26) );
cerionb85e8bb2005-02-16 08:54:33 +00004317 }
ceriond953ebb2005-11-29 13:27:20 +00004318 if (mode64) {
4319 DIP("b%s%s 0x%llx\n",
4320 flag_LK ? "l" : "", flag_AA ? "a" : "", tgt);
4321 } else {
4322 DIP("b%s%s 0x%x\n",
4323 flag_LK ? "l" : "", flag_AA ? "a" : "", (Addr32)tgt);
sewardj9d540e52005-10-08 11:28:16 +00004324 }
4325
sewardjcf8986c2006-01-18 04:14:52 +00004326 if (flag_LK) {
ceriond953ebb2005-11-29 13:27:20 +00004327 putGST( PPC_GST_LR, e_nia );
sewardjdd40fdf2006-12-24 02:20:24 +00004328 if (vbi->guest_ppc_zap_RZ_at_bl
sewardj478646f2008-05-01 20:13:04 +00004329 && vbi->guest_ppc_zap_RZ_at_bl( (ULong)tgt) ) {
4330 IRTemp t_tgt = newTemp(ty);
4331 assign(t_tgt, mode64 ? mkU64(tgt) : mkU32(tgt) );
4332 make_redzone_AbiHint( vbi, t_tgt,
sewardjaca070a2006-10-17 00:28:22 +00004333 "branch-and-link (unconditional call)" );
sewardj478646f2008-05-01 20:13:04 +00004334 }
sewardjcf8986c2006-01-18 04:14:52 +00004335 }
ceriond953ebb2005-11-29 13:27:20 +00004336
sewardjc716aea2006-01-17 01:48:46 +00004337 if (resteerOkFn( callback_opaque, tgt )) {
ceriond953ebb2005-11-29 13:27:20 +00004338 dres->whatNext = Dis_Resteer;
4339 dres->continueAt = tgt;
sewardj9d540e52005-10-08 11:28:16 +00004340 } else {
sewardjdd40fdf2006-12-24 02:20:24 +00004341 irsb->jumpkind = flag_LK ? Ijk_Call : Ijk_Boring;
4342 irsb->next = mkSzImm(ty, tgt);
sewardj9d540e52005-10-08 11:28:16 +00004343 }
cerionb85e8bb2005-02-16 08:54:33 +00004344 break;
4345
cerione9d361a2005-03-04 17:35:29 +00004346 case 0x10: // bc (Branch Conditional, PPC32 p361)
cerionb85e8bb2005-02-16 08:54:33 +00004347 DIP("bc%s%s 0x%x, 0x%x, 0x%x\n",
ceriond953ebb2005-11-29 13:27:20 +00004348 flag_LK ? "l" : "", flag_AA ? "a" : "", BO, BI, BD);
cerionb85e8bb2005-02-16 08:54:33 +00004349
4350 if (!(BO & 0x4)) {
ceriond953ebb2005-11-29 13:27:20 +00004351 putGST( PPC_GST_CTR,
cerion2831b002005-11-30 19:55:22 +00004352 binop(mkSzOp(ty, Iop_Sub8),
4353 getGST( PPC_GST_CTR ), mkSzImm(ty, 1)) );
cerionb85e8bb2005-02-16 08:54:33 +00004354 }
sewardjb51f0f42005-07-18 11:38:02 +00004355
4356 /* This is a bit subtle. ctr_ok is either all 0s or all 1s.
cerion76de5cf2005-11-18 18:25:12 +00004357 cond_ok is either zero or nonzero, since that's the cheapest
4358 way to compute it. Anding them together gives a value which
4359 is either zero or non zero and so that's what we must test
4360 for in the IRStmt_Exit. */
sewardjb51f0f42005-07-18 11:38:02 +00004361 assign( ctr_ok, branch_ctr_ok( BO ) );
cerionb85e8bb2005-02-16 08:54:33 +00004362 assign( cond_ok, branch_cond_ok( BO, BI ) );
sewardjb51f0f42005-07-18 11:38:02 +00004363 assign( do_branch,
4364 binop(Iop_And32, mkexpr(cond_ok), mkexpr(ctr_ok)) );
4365
cerion4561acb2005-02-21 14:07:48 +00004366 if (flag_AA) {
cerion2831b002005-11-30 19:55:22 +00004367 tgt = mkSzAddr(ty, extend_s_16to64(BD_u16));
cerion4561acb2005-02-21 14:07:48 +00004368 } else {
cerion2831b002005-11-30 19:55:22 +00004369 tgt = mkSzAddr(ty, guest_CIA_curr_instr +
4370 (Long)extend_s_16to64(BD_u16));
cerionb85e8bb2005-02-16 08:54:33 +00004371 }
ceriond953ebb2005-11-29 13:27:20 +00004372 if (flag_LK)
4373 putGST( PPC_GST_LR, e_nia );
cerionb85e8bb2005-02-16 08:54:33 +00004374
ceriond953ebb2005-11-29 13:27:20 +00004375 stmt( IRStmt_Exit(
4376 binop(Iop_CmpNE32, mkexpr(do_branch), mkU32(0)),
4377 flag_LK ? Ijk_Call : Ijk_Boring,
cerion2831b002005-11-30 19:55:22 +00004378 mkSzConst(ty, tgt) ) );
cerionb85e8bb2005-02-16 08:54:33 +00004379
sewardjdd40fdf2006-12-24 02:20:24 +00004380 irsb->jumpkind = Ijk_Boring;
4381 irsb->next = e_nia;
cerionb85e8bb2005-02-16 08:54:33 +00004382 break;
4383
4384 case 0x13:
sewardj6be67232006-01-24 19:00:05 +00004385 /* For bclr and bcctr, it appears that the lowest two bits of
4386 b11to15 are a branch hint, and so we only need to ensure it's
4387 of the form 000XX. */
4388 if ((b11to15 & ~3) != 0) {
4389 vex_printf("dis_int_branch(ppc)(0x13,b11to15)(%d)\n", (Int)b11to15);
cerionb85e8bb2005-02-16 08:54:33 +00004390 return False;
4391 }
cerion91ad5362005-01-27 23:02:41 +00004392
cerionb85e8bb2005-02-16 08:54:33 +00004393 switch (opc2) {
cerione9d361a2005-03-04 17:35:29 +00004394 case 0x210: // bcctr (Branch Cond. to Count Register, PPC32 p363)
cerion5b2325f2005-12-23 00:55:09 +00004395 if ((BO & 0x4) == 0) { // "decr and test CTR" option invalid
4396 vex_printf("dis_int_branch(ppc)(bcctr,BO)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004397 return False;
4398 }
ceriona31e8b52005-02-21 16:30:45 +00004399 DIP("bcctr%s 0x%x, 0x%x\n", flag_LK ? "l" : "", BO, BI);
cerionb85e8bb2005-02-16 08:54:33 +00004400
4401 assign( cond_ok, branch_cond_ok( BO, BI ) );
ceriond953ebb2005-11-29 13:27:20 +00004402
sewardj478646f2008-05-01 20:13:04 +00004403 /* FIXME: this is confusing. lr_old holds the old value
4404 of ctr, not lr :-) */
cerion2831b002005-11-30 19:55:22 +00004405 assign( lr_old, addr_align( getGST( PPC_GST_CTR ), 4 ));
sewardjdf07b882005-11-29 18:19:11 +00004406
ceriond953ebb2005-11-29 13:27:20 +00004407 if (flag_LK)
4408 putGST( PPC_GST_LR, e_nia );
cerionb85e8bb2005-02-16 08:54:33 +00004409
sewardjb51f0f42005-07-18 11:38:02 +00004410 stmt( IRStmt_Exit(
4411 binop(Iop_CmpEQ32, mkexpr(cond_ok), mkU32(0)),
4412 Ijk_Boring,
ceriond953ebb2005-11-29 13:27:20 +00004413 c_nia ));
sewardj478646f2008-05-01 20:13:04 +00004414
4415 if (flag_LK && vbi->guest_ppc_zap_RZ_at_bl) {
4416 make_redzone_AbiHint( vbi, lr_old,
4417 "b-ctr-l (indirect call)" );
4418 }
4419
sewardjdd40fdf2006-12-24 02:20:24 +00004420 irsb->jumpkind = flag_LK ? Ijk_Call : Ijk_Boring;
4421 irsb->next = mkexpr(lr_old);
cerionb85e8bb2005-02-16 08:54:33 +00004422 break;
4423
sewardjcf8986c2006-01-18 04:14:52 +00004424 case 0x010: { // bclr (Branch Cond. to Link Register, PPC32 p365)
4425 Bool vanilla_return = False;
sewardjb51f0f42005-07-18 11:38:02 +00004426 if ((BO & 0x14 /* 1z1zz */) == 0x14 && flag_LK == 0) {
cerion225a0342005-09-12 20:49:09 +00004427 DIP("blr\n");
sewardjcf8986c2006-01-18 04:14:52 +00004428 vanilla_return = True;
sewardjb51f0f42005-07-18 11:38:02 +00004429 } else {
4430 DIP("bclr%s 0x%x, 0x%x\n", flag_LK ? "l" : "", BO, BI);
4431 }
cerion91ad5362005-01-27 23:02:41 +00004432
cerionb85e8bb2005-02-16 08:54:33 +00004433 if (!(BO & 0x4)) {
ceriond953ebb2005-11-29 13:27:20 +00004434 putGST( PPC_GST_CTR,
cerion2831b002005-11-30 19:55:22 +00004435 binop(mkSzOp(ty, Iop_Sub8),
4436 getGST( PPC_GST_CTR ), mkSzImm(ty, 1)) );
cerionb85e8bb2005-02-16 08:54:33 +00004437 }
4438
sewardjb51f0f42005-07-18 11:38:02 +00004439 /* See comments above for 'bc' about this */
4440 assign( ctr_ok, branch_ctr_ok( BO ) );
4441 assign( cond_ok, branch_cond_ok( BO, BI ) );
4442 assign( do_branch,
4443 binop(Iop_And32, mkexpr(cond_ok), mkexpr(ctr_ok)) );
cerion2831b002005-11-30 19:55:22 +00004444
sewardjdf07b882005-11-29 18:19:11 +00004445 assign( lr_old, addr_align( getGST( PPC_GST_LR ), 4 ));
4446
ceriond953ebb2005-11-29 13:27:20 +00004447 if (flag_LK)
4448 putGST( PPC_GST_LR, e_nia );
sewardjb51f0f42005-07-18 11:38:02 +00004449
4450 stmt( IRStmt_Exit(
4451 binop(Iop_CmpEQ32, mkexpr(do_branch), mkU32(0)),
4452 Ijk_Boring,
ceriond953ebb2005-11-29 13:27:20 +00004453 c_nia ));
sewardjb51f0f42005-07-18 11:38:02 +00004454
sewardj478646f2008-05-01 20:13:04 +00004455 if (vanilla_return && vbi->guest_ppc_zap_RZ_at_blr) {
4456 make_redzone_AbiHint( vbi, lr_old,
4457 "branch-to-lr (unconditional return)" );
4458 }
sewardjcf8986c2006-01-18 04:14:52 +00004459
sewardjd37be032005-11-12 12:56:31 +00004460 /* blrl is pretty strange; it's like a return that sets the
4461 return address of its caller to the insn following this
4462 one. Mark it as a return. */
sewardjdd40fdf2006-12-24 02:20:24 +00004463 irsb->jumpkind = Ijk_Ret; /* was flag_LK ? Ijk_Call : Ijk_Ret; */
4464 irsb->next = mkexpr(lr_old);
cerionb85e8bb2005-02-16 08:54:33 +00004465 break;
sewardjcf8986c2006-01-18 04:14:52 +00004466 }
cerionb85e8bb2005-02-16 08:54:33 +00004467 default:
cerion5b2325f2005-12-23 00:55:09 +00004468 vex_printf("dis_int_branch(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004469 return False;
4470 }
4471 break;
cerion2831b002005-11-30 19:55:22 +00004472
cerionb85e8bb2005-02-16 08:54:33 +00004473 default:
cerion5b2325f2005-12-23 00:55:09 +00004474 vex_printf("dis_int_branch(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004475 return False;
4476 }
cerion2831b002005-11-30 19:55:22 +00004477
cerionb85e8bb2005-02-16 08:54:33 +00004478 return True;
cerion91ad5362005-01-27 23:02:41 +00004479}
4480
4481
4482
cerion3d870a32005-03-18 12:23:33 +00004483/*
4484 Condition Register Logical Instructions
4485*/
cerion3007c7f2005-02-23 23:13:29 +00004486static Bool dis_cond_logic ( UInt theInstr )
4487{
4488 /* XL-Form */
cerion76de5cf2005-11-18 18:25:12 +00004489 UChar opc1 = ifieldOPC(theInstr);
4490 UChar crbD_addr = ifieldRegDS(theInstr);
4491 UChar crfD_addr = toUChar( IFIELD(theInstr, 23, 3) );
4492 UChar crbA_addr = ifieldRegA(theInstr);
4493 UChar crfS_addr = toUChar( IFIELD(theInstr, 18, 3) );
4494 UChar crbB_addr = ifieldRegB(theInstr);
4495 UInt opc2 = ifieldOPClo10(theInstr);
4496 UChar b0 = ifieldBIT0(theInstr);
cerion3007c7f2005-02-23 23:13:29 +00004497
cerionf0de28c2005-12-13 20:21:11 +00004498 IRTemp crbD = newTemp(Ity_I32);
4499 IRTemp crbA = newTemp(Ity_I32);
4500 IRTemp crbB = newTemp(Ity_I32);
cerion3007c7f2005-02-23 23:13:29 +00004501
4502 if (opc1 != 19 || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00004503 vex_printf("dis_cond_logic(ppc)(opc1)\n");
cerion3007c7f2005-02-23 23:13:29 +00004504 return False;
4505 }
4506
cerione9d361a2005-03-04 17:35:29 +00004507 if (opc2 == 0) { // mcrf (Move Cond Reg Field, PPC32 p464)
cerion3007c7f2005-02-23 23:13:29 +00004508 if (((crbD_addr & 0x3) != 0) ||
cerion76de5cf2005-11-18 18:25:12 +00004509 ((crbA_addr & 0x3) != 0) || (crbB_addr != 0)) {
cerion5b2325f2005-12-23 00:55:09 +00004510 vex_printf("dis_cond_logic(ppc)(crbD|crbA|crbB != 0)\n");
cerion3007c7f2005-02-23 23:13:29 +00004511 return False;
cerion76de5cf2005-11-18 18:25:12 +00004512 }
ceriond953ebb2005-11-29 13:27:20 +00004513 DIP("mcrf cr%u,cr%u\n", crfD_addr, crfS_addr);
sewardjb51f0f42005-07-18 11:38:02 +00004514 putCR0( crfD_addr, getCR0( crfS_addr) );
4515 putCR321( crfD_addr, getCR321(crfS_addr) );
cerion3007c7f2005-02-23 23:13:29 +00004516 } else {
sewardjb51f0f42005-07-18 11:38:02 +00004517 assign( crbA, getCRbit(crbA_addr) );
ceriona50fde52005-07-01 21:16:10 +00004518 if (crbA_addr == crbB_addr)
sewardjb51f0f42005-07-18 11:38:02 +00004519 crbB = crbA;
ceriona50fde52005-07-01 21:16:10 +00004520 else
sewardjb51f0f42005-07-18 11:38:02 +00004521 assign( crbB, getCRbit(crbB_addr) );
cerion3007c7f2005-02-23 23:13:29 +00004522
4523 switch (opc2) {
sewardj7c2dc712005-09-08 17:33:27 +00004524 case 0x101: // crand (Cond Reg AND, PPC32 p372)
4525 DIP("crand crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4526 assign( crbD, binop(Iop_And32, mkexpr(crbA), mkexpr(crbB)) );
4527 break;
sewardj7787af42005-08-04 18:32:19 +00004528 case 0x081: // crandc (Cond Reg AND w. Complement, PPC32 p373)
4529 DIP("crandc crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4530 assign( crbD, binop(Iop_And32,
4531 mkexpr(crbA),
4532 unop(Iop_Not32, mkexpr(crbB))) );
4533 break;
sewardje14bb9f2005-07-22 09:39:02 +00004534 case 0x121: // creqv (Cond Reg Equivalent, PPC32 p374)
4535 DIP("creqv crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4536 assign( crbD, unop(Iop_Not32,
4537 binop(Iop_Xor32, mkexpr(crbA), mkexpr(crbB))) );
4538 break;
sewardj7c2dc712005-09-08 17:33:27 +00004539 case 0x0E1: // crnand (Cond Reg NAND, PPC32 p375)
4540 DIP("crnand crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4541 assign( crbD, unop(Iop_Not32,
4542 binop(Iop_And32, mkexpr(crbA), mkexpr(crbB))) );
4543 break;
cerione9d361a2005-03-04 17:35:29 +00004544 case 0x021: // crnor (Cond Reg NOR, PPC32 p376)
cerion3007c7f2005-02-23 23:13:29 +00004545 DIP("crnor crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4546 assign( crbD, unop(Iop_Not32,
4547 binop(Iop_Or32, mkexpr(crbA), mkexpr(crbB))) );
4548 break;
cerione9d361a2005-03-04 17:35:29 +00004549 case 0x1C1: // cror (Cond Reg OR, PPC32 p377)
cerion3007c7f2005-02-23 23:13:29 +00004550 DIP("cror crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4551 assign( crbD, binop(Iop_Or32, mkexpr(crbA), mkexpr(crbB)) );
4552 break;
sewardj7c2dc712005-09-08 17:33:27 +00004553 case 0x1A1: // crorc (Cond Reg OR w. Complement, PPC32 p378)
4554 DIP("crorc crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4555 assign( crbD, binop(Iop_Or32,
4556 mkexpr(crbA),
4557 unop(Iop_Not32, mkexpr(crbB))) );
4558 break;
cerione9d361a2005-03-04 17:35:29 +00004559 case 0x0C1: // crxor (Cond Reg XOR, PPC32 p379)
cerion3007c7f2005-02-23 23:13:29 +00004560 DIP("crxor crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4561 assign( crbD, binop(Iop_Xor32, mkexpr(crbA), mkexpr(crbB)) );
4562 break;
cerion3007c7f2005-02-23 23:13:29 +00004563 default:
cerion5b2325f2005-12-23 00:55:09 +00004564 vex_printf("dis_cond_logic(ppc)(opc2)\n");
cerion3007c7f2005-02-23 23:13:29 +00004565 return False;
4566 }
4567
sewardjb51f0f42005-07-18 11:38:02 +00004568 putCRbit( crbD_addr, mkexpr(crbD) );
cerion3007c7f2005-02-23 23:13:29 +00004569 }
4570 return True;
4571}
4572
4573
sewardj334870d2006-02-07 16:42:39 +00004574/*
4575 Trap instructions
4576*/
4577
4578/* Do the code generation for a trap. Returned Bool is true iff
sewardj59c0d8f2007-08-28 14:48:35 +00004579 this is an unconditional trap. If the two arg IRExpr*s are
4580 Ity_I32s then the comparison is 32-bit. If they are Ity_I64s
4581 then they are 64-bit, and we must be disassembling 64-bit
4582 instructions. */
4583static Bool do_trap ( UChar TO,
4584 IRExpr* argL0, IRExpr* argR0, Addr64 cia )
sewardj334870d2006-02-07 16:42:39 +00004585{
4586 IRTemp argL, argR;
4587 IRExpr *argLe, *argRe, *cond, *tmp;
4588
sewardj59c0d8f2007-08-28 14:48:35 +00004589 Bool is32bit = typeOfIRExpr(irsb->tyenv, argL0 ) == Ity_I32;
4590
4591 IROp opAND = is32bit ? Iop_And32 : Iop_And64;
4592 IROp opOR = is32bit ? Iop_Or32 : Iop_Or64;
4593 IROp opCMPORDS = is32bit ? Iop_CmpORD32S : Iop_CmpORD64S;
4594 IROp opCMPORDU = is32bit ? Iop_CmpORD32U : Iop_CmpORD64U;
4595 IROp opCMPNE = is32bit ? Iop_CmpNE32 : Iop_CmpNE64;
4596 IROp opCMPEQ = is32bit ? Iop_CmpEQ32 : Iop_CmpEQ64;
4597 IRExpr* const0 = is32bit ? mkU32(0) : mkU64(0);
4598 IRExpr* const2 = is32bit ? mkU32(2) : mkU64(2);
4599 IRExpr* const4 = is32bit ? mkU32(4) : mkU64(4);
4600 IRExpr* const8 = is32bit ? mkU32(8) : mkU64(8);
sewardj334870d2006-02-07 16:42:39 +00004601
4602 const UChar b11100 = 0x1C;
4603 const UChar b00111 = 0x07;
4604
sewardj59c0d8f2007-08-28 14:48:35 +00004605 if (is32bit) {
4606 vassert( typeOfIRExpr(irsb->tyenv, argL0) == Ity_I32 );
4607 vassert( typeOfIRExpr(irsb->tyenv, argR0) == Ity_I32 );
4608 } else {
4609 vassert( typeOfIRExpr(irsb->tyenv, argL0) == Ity_I64 );
4610 vassert( typeOfIRExpr(irsb->tyenv, argR0) == Ity_I64 );
4611 vassert( mode64 );
4612 }
4613
sewardj334870d2006-02-07 16:42:39 +00004614 if ((TO & b11100) == b11100 || (TO & b00111) == b00111) {
4615 /* Unconditional trap. Just do the exit without
4616 testing the arguments. */
4617 stmt( IRStmt_Exit(
4618 binop(opCMPEQ, const0, const0),
sewardj0f500042007-08-29 09:09:17 +00004619 Ijk_SigTRAP,
sewardj334870d2006-02-07 16:42:39 +00004620 mode64 ? IRConst_U64(cia) : IRConst_U32((UInt)cia)
4621 ));
4622 return True; /* unconditional trap */
4623 }
4624
sewardj59c0d8f2007-08-28 14:48:35 +00004625 if (is32bit) {
sewardj2d19fe32006-02-07 20:55:08 +00004626 argL = newTemp(Ity_I32);
4627 argR = newTemp(Ity_I32);
sewardj334870d2006-02-07 16:42:39 +00004628 } else {
sewardj2d19fe32006-02-07 20:55:08 +00004629 argL = newTemp(Ity_I64);
4630 argR = newTemp(Ity_I64);
sewardj334870d2006-02-07 16:42:39 +00004631 }
sewardj59c0d8f2007-08-28 14:48:35 +00004632
4633 assign( argL, argL0 );
4634 assign( argR, argR0 );
4635
sewardj334870d2006-02-07 16:42:39 +00004636 argLe = mkexpr(argL);
4637 argRe = mkexpr(argR);
sewardj59c0d8f2007-08-28 14:48:35 +00004638
sewardj334870d2006-02-07 16:42:39 +00004639 cond = const0;
4640 if (TO & 16) { // L <s R
4641 tmp = binop(opAND, binop(opCMPORDS, argLe, argRe), const8);
4642 cond = binop(opOR, tmp, cond);
4643 }
4644 if (TO & 8) { // L >s R
4645 tmp = binop(opAND, binop(opCMPORDS, argLe, argRe), const4);
4646 cond = binop(opOR, tmp, cond);
4647 }
4648 if (TO & 4) { // L == R
4649 tmp = binop(opAND, binop(opCMPORDS, argLe, argRe), const2);
4650 cond = binop(opOR, tmp, cond);
4651 }
4652 if (TO & 2) { // L <u R
4653 tmp = binop(opAND, binop(opCMPORDU, argLe, argRe), const8);
4654 cond = binop(opOR, tmp, cond);
4655 }
4656 if (TO & 1) { // L >u R
4657 tmp = binop(opAND, binop(opCMPORDU, argLe, argRe), const4);
4658 cond = binop(opOR, tmp, cond);
4659 }
4660 stmt( IRStmt_Exit(
4661 binop(opCMPNE, cond, const0),
sewardj0f500042007-08-29 09:09:17 +00004662 Ijk_SigTRAP,
sewardj334870d2006-02-07 16:42:39 +00004663 mode64 ? IRConst_U64(cia) : IRConst_U32((UInt)cia)
4664 ));
4665 return False; /* not an unconditional trap */
4666}
4667
4668static Bool dis_trapi ( UInt theInstr,
4669 /*OUT*/DisResult* dres )
4670{
4671 /* D-Form */
4672 UChar opc1 = ifieldOPC(theInstr);
4673 UChar TO = ifieldRegDS(theInstr);
4674 UChar rA_addr = ifieldRegA(theInstr);
4675 UInt uimm16 = ifieldUIMM16(theInstr);
4676 ULong simm16 = extend_s_16to64(uimm16);
4677 Addr64 cia = guest_CIA_curr_instr;
4678 IRType ty = mode64 ? Ity_I64 : Ity_I32;
4679 Bool uncond = False;
4680
4681 switch (opc1) {
4682 case 0x03: // twi (Trap Word Immediate, PPC32 p548)
sewardj59c0d8f2007-08-28 14:48:35 +00004683 uncond = do_trap( TO,
4684 mode64 ? unop(Iop_64to32, getIReg(rA_addr))
4685 : getIReg(rA_addr),
sewardjb4f6d6b2007-08-28 16:39:52 +00004686 mkU32( (UInt)simm16 ),
sewardj59c0d8f2007-08-28 14:48:35 +00004687 cia );
sewardj334870d2006-02-07 16:42:39 +00004688 if (TO == 4) {
4689 DIP("tweqi r%u,%d\n", (UInt)rA_addr, (Int)simm16);
4690 } else {
4691 DIP("tw%di r%u,%d\n", (Int)TO, (UInt)rA_addr, (Int)simm16);
4692 }
4693 break;
4694 case 0x02: // tdi
4695 if (!mode64)
4696 return False;
sewardj59c0d8f2007-08-28 14:48:35 +00004697 uncond = do_trap( TO, getIReg(rA_addr), mkU64( (ULong)simm16 ), cia );
sewardj2d19fe32006-02-07 20:55:08 +00004698 if (TO == 4) {
4699 DIP("tdeqi r%u,%d\n", (UInt)rA_addr, (Int)simm16);
4700 } else {
4701 DIP("td%di r%u,%d\n", (Int)TO, (UInt)rA_addr, (Int)simm16);
4702 }
sewardj334870d2006-02-07 16:42:39 +00004703 break;
4704 default:
4705 return False;
4706 }
4707
4708 if (uncond) {
4709 /* If the trap shows signs of being unconditional, don't
4710 continue decoding past it. */
sewardjdd40fdf2006-12-24 02:20:24 +00004711 irsb->next = mkSzImm( ty, nextInsnAddr() );
4712 irsb->jumpkind = Ijk_Boring;
sewardj334870d2006-02-07 16:42:39 +00004713 dres->whatNext = Dis_StopHere;
4714 }
4715
4716 return True;
4717}
4718
sewardj59c0d8f2007-08-28 14:48:35 +00004719static Bool dis_trap ( UInt theInstr,
4720 /*OUT*/DisResult* dres )
4721{
4722 /* X-Form */
4723 UInt opc2 = ifieldOPClo10(theInstr);
4724 UChar TO = ifieldRegDS(theInstr);
4725 UChar rA_addr = ifieldRegA(theInstr);
4726 UChar rB_addr = ifieldRegB(theInstr);
4727 Addr64 cia = guest_CIA_curr_instr;
4728 IRType ty = mode64 ? Ity_I64 : Ity_I32;
4729 Bool uncond = False;
4730
4731 if (ifieldBIT0(theInstr) != 0)
4732 return False;
4733
4734 switch (opc2) {
4735 case 0x004: // tw (Trap Word, PPC64 p540)
4736 uncond = do_trap( TO,
4737 mode64 ? unop(Iop_64to32, getIReg(rA_addr))
4738 : getIReg(rA_addr),
4739 mode64 ? unop(Iop_64to32, getIReg(rB_addr))
4740 : getIReg(rB_addr),
4741 cia );
4742 if (TO == 4) {
4743 DIP("tweq r%u,r%u\n", (UInt)rA_addr, (UInt)rB_addr);
4744 } else {
4745 DIP("tw%d r%u,r%u\n", (Int)TO, (UInt)rA_addr, (UInt)rB_addr);
4746 }
4747 break;
sewardjb4f6d6b2007-08-28 16:39:52 +00004748 case 0x044: // td (Trap Doubleword, PPC64 p534)
4749 if (!mode64)
4750 return False;
4751 uncond = do_trap( TO, getIReg(rA_addr), getIReg(rB_addr), cia );
4752 if (TO == 4) {
4753 DIP("tdeq r%u,r%u\n", (UInt)rA_addr, (UInt)rB_addr);
4754 } else {
4755 DIP("td%d r%u,r%u\n", (Int)TO, (UInt)rA_addr, (UInt)rB_addr);
4756 }
4757 break;
sewardj59c0d8f2007-08-28 14:48:35 +00004758 default:
4759 return False;
4760 }
4761
4762 if (uncond) {
4763 /* If the trap shows signs of being unconditional, don't
4764 continue decoding past it. */
4765 irsb->next = mkSzImm( ty, nextInsnAddr() );
4766 irsb->jumpkind = Ijk_Boring;
4767 dres->whatNext = Dis_StopHere;
4768 }
4769
4770 return True;
4771}
4772
sewardj334870d2006-02-07 16:42:39 +00004773
cerion3d870a32005-03-18 12:23:33 +00004774/*
4775 System Linkage Instructions
4776*/
sewardjaca070a2006-10-17 00:28:22 +00004777static Bool dis_syslink ( UInt theInstr,
sewardjdd40fdf2006-12-24 02:20:24 +00004778 VexAbiInfo* abiinfo, DisResult* dres )
cerion8c3adda2005-01-31 11:54:05 +00004779{
ceriond953ebb2005-11-29 13:27:20 +00004780 IRType ty = mode64 ? Ity_I64 : Ity_I32;
4781
cerionb85e8bb2005-02-16 08:54:33 +00004782 if (theInstr != 0x44000002) {
cerion5b2325f2005-12-23 00:55:09 +00004783 vex_printf("dis_syslink(ppc)(theInstr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004784 return False;
4785 }
cerione1d857b2005-02-04 18:29:05 +00004786
cerione9d361a2005-03-04 17:35:29 +00004787 // sc (System Call, PPC32 p504)
cerionb85e8bb2005-02-16 08:54:33 +00004788 DIP("sc\n");
sewardjaca070a2006-10-17 00:28:22 +00004789
sewardje86310f2009-03-19 22:21:40 +00004790 /* Copy CIA into the IP_AT_SYSCALL pseudo-register, so that on AIX
sewardjaca070a2006-10-17 00:28:22 +00004791 Valgrind can back the guest up to this instruction if it needs
4792 to restart the syscall. */
sewardje86310f2009-03-19 22:21:40 +00004793 putGST( PPC_GST_IP_AT_SYSCALL, getGST( PPC_GST_CIA ) );
sewardjaca070a2006-10-17 00:28:22 +00004794
cerionb85e8bb2005-02-16 08:54:33 +00004795 /* It's important that all ArchRegs carry their up-to-date value
4796 at this point. So we declare an end-of-block here, which
4797 forces any TempRegs caching ArchRegs to be flushed. */
sewardjdd40fdf2006-12-24 02:20:24 +00004798 irsb->next = abiinfo->guest_ppc_sc_continues_at_LR
4799 ? getGST( PPC_GST_LR )
4800 : mkSzImm( ty, nextInsnAddr() );
4801 irsb->jumpkind = Ijk_Sys_syscall;
ceriond953ebb2005-11-29 13:27:20 +00004802
sewardj9e6491a2005-07-02 19:24:10 +00004803 dres->whatNext = Dis_StopHere;
cerionb85e8bb2005-02-16 08:54:33 +00004804 return True;
cerion8c3adda2005-01-31 11:54:05 +00004805}
4806
cerion3d870a32005-03-18 12:23:33 +00004807
4808/*
4809 Memory Synchronization Instructions
cerion07b07a92005-12-22 14:32:35 +00004810
4811 Note on Reservations:
4812 We rely on the assumption that V will in fact only allow one thread at
4813 once to run. In effect, a thread can make a reservation, but we don't
4814 check any stores it does. Instead, the reservation is cancelled when
4815 the scheduler switches to another thread (run_thread_for_a_while()).
cerion3d870a32005-03-18 12:23:33 +00004816*/
cerion8c3adda2005-01-31 11:54:05 +00004817static Bool dis_memsync ( UInt theInstr )
4818{
cerionb85e8bb2005-02-16 08:54:33 +00004819 /* X-Form, XL-Form */
ceriond953ebb2005-11-29 13:27:20 +00004820 UChar opc1 = ifieldOPC(theInstr);
4821 UInt b11to25 = IFIELD(theInstr, 11, 15);
cerione43bc882006-01-05 13:11:59 +00004822 UChar flag_L = ifieldRegDS(theInstr);
4823 UInt b11to20 = IFIELD(theInstr, 11, 10);
ceriond953ebb2005-11-29 13:27:20 +00004824 UChar rD_addr = ifieldRegDS(theInstr);
4825 UChar rS_addr = rD_addr;
4826 UChar rA_addr = ifieldRegA(theInstr);
4827 UChar rB_addr = ifieldRegB(theInstr);
4828 UInt opc2 = ifieldOPClo10(theInstr);
4829 UChar b0 = ifieldBIT0(theInstr);
cerionedf7fc52005-11-18 20:57:41 +00004830
ceriond953ebb2005-11-29 13:27:20 +00004831 IRType ty = mode64 ? Ity_I64 : Ity_I32;
4832 IRTemp EA = newTemp(ty);
ceriond953ebb2005-11-29 13:27:20 +00004833
4834 assign( EA, ea_rAor0_idxd( rA_addr, rB_addr ) );
4835
cerionb85e8bb2005-02-16 08:54:33 +00004836 switch (opc1) {
sewardjafe85832005-09-09 10:25:39 +00004837 /* XL-Form */
cerione9d361a2005-03-04 17:35:29 +00004838 case 0x13: // isync (Instruction Synchronize, PPC32 p432)
cerionb85e8bb2005-02-16 08:54:33 +00004839 if (opc2 != 0x096) {
cerion5b2325f2005-12-23 00:55:09 +00004840 vex_printf("dis_memsync(ppc)(0x13,opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004841 return False;
4842 }
4843 if (b11to25 != 0 || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00004844 vex_printf("dis_memsync(ppc)(0x13,b11to25|b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004845 return False;
4846 }
4847 DIP("isync\n");
sewardjc4356f02007-11-09 21:15:04 +00004848 stmt( IRStmt_MBE(Imbe_Fence) );
cerionb85e8bb2005-02-16 08:54:33 +00004849 break;
cerion8c3adda2005-01-31 11:54:05 +00004850
cerionb85e8bb2005-02-16 08:54:33 +00004851 /* X-Form */
4852 case 0x1F:
4853 switch (opc2) {
cerion5b2325f2005-12-23 00:55:09 +00004854 case 0x356: // eieio (Enforce In-Order Exec of I/O, PPC32 p394)
sewardj7787af42005-08-04 18:32:19 +00004855 if (b11to25 != 0 || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00004856 vex_printf("dis_memsync(ppc)(eiei0,b11to25|b0)\n");
sewardj7787af42005-08-04 18:32:19 +00004857 return False;
4858 }
4859 DIP("eieio\n");
4860 /* Insert a memory fence, just to be on the safe side. */
sewardjc4356f02007-11-09 21:15:04 +00004861 stmt( IRStmt_MBE(Imbe_Fence) );
sewardj7787af42005-08-04 18:32:19 +00004862 break;
cerion8c3adda2005-01-31 11:54:05 +00004863
cerione9d361a2005-03-04 17:35:29 +00004864 case 0x014: // lwarx (Load Word and Reserve Indexed, PPC32 p458)
sewardjfe397a22008-08-06 19:13:42 +00004865 /* According to the PowerPC ISA version 2.05, b0 (called EH
4866 in the documentation) is merely a hint bit to the
4867 hardware, I think as to whether or not contention is
4868 likely. So we can just ignore it. */
4869 DIP("lwarx r%u,r%u,r%u,EH=%u\n", rD_addr, rA_addr, rB_addr, (UInt)b0);
sewardje9d8a262009-07-01 08:06:34 +00004870
4871 // trap if misaligned
4872 gen_SIGBUS_if_misaligned( EA, 4 );
4873
4874 // and actually do the load
4875 putIReg( rD_addr, mkWidenFrom32(ty, loadlinkedBE(Ity_I32, mkexpr(EA)),
4876 False) );
cerionb85e8bb2005-02-16 08:54:33 +00004877 break;
4878
sewardj7787af42005-08-04 18:32:19 +00004879 case 0x096: {
4880 // stwcx. (Store Word Conditional Indexed, PPC32 p532)
sewardje9d8a262009-07-01 08:06:34 +00004881 // Note this has to handle stwcx. in both 32- and 64-bit modes,
4882 // so isn't quite as straightforward as it might otherwise be.
4883 IRTemp rS = newTemp(Ity_I32);
4884 IRTemp resSC;
cerionb85e8bb2005-02-16 08:54:33 +00004885 if (b0 != 1) {
cerion5b2325f2005-12-23 00:55:09 +00004886 vex_printf("dis_memsync(ppc)(stwcx.,b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004887 return False;
4888 }
ceriond953ebb2005-11-29 13:27:20 +00004889 DIP("stwcx. r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
sewardjafe85832005-09-09 10:25:39 +00004890
sewardje9d8a262009-07-01 08:06:34 +00004891 // trap if misaligned
4892 gen_SIGBUS_if_misaligned( EA, 4 );
4893
4894 // Get the data to be stored, and narrow to 32 bits if necessary
4895 assign( rS, mkNarrowTo32(ty, getIReg(rS_addr)) );
4896
4897 // Do the store, and get success/failure bit into resSC
4898 resSC = newTemp(Ity_I1);
4899 stmt( IRStmt_Store(Iend_BE, resSC, mkexpr(EA), mkexpr(rS)) );
4900
4901 // Set CR0[LT GT EQ S0] = 0b000 || XER[SO] on failure
4902 // Set CR0[LT GT EQ S0] = 0b001 || XER[SO] on success
4903 putCR321(0, binop(Iop_Shl8, unop(Iop_1Uto8, mkexpr(resSC)), mkU8(1)));
sewardj7787af42005-08-04 18:32:19 +00004904 putCR0(0, getXER_SO());
4905
sewardje9d8a262009-07-01 08:06:34 +00004906 /* Note:
ceriond953ebb2005-11-29 13:27:20 +00004907 If resaddr != lwarx_resaddr, CR0[EQ] is undefined, and
4908 whether rS is stored is dependent on that value. */
sewardje9d8a262009-07-01 08:06:34 +00004909 /* So I guess we can just ignore this case? */
cerionb85e8bb2005-02-16 08:54:33 +00004910 break;
sewardj7787af42005-08-04 18:32:19 +00004911 }
4912
sewardjb029a612005-12-30 15:04:29 +00004913 case 0x256: // sync (Synchronize, PPC32 p543),
cerione43bc882006-01-05 13:11:59 +00004914 // also lwsync (L==1), ptesync (L==2)
sewardjb029a612005-12-30 15:04:29 +00004915 /* http://sources.redhat.com/ml/binutils/2000-12/msg00311.html
4916
4917 The PowerPC architecture used in IBM chips has expanded
4918 the sync instruction into two variants: lightweight sync
4919 and heavyweight sync. The original sync instruction is
4920 the new heavyweight sync and lightweight sync is a strict
4921 subset of the heavyweight sync functionality. This allows
4922 the programmer to specify a less expensive operation on
4923 high-end systems when the full sync functionality is not
4924 necessary.
4925
4926 The basic "sync" mnemonic now utilizes an operand. "sync"
4927 without an operand now becomes a extended mnemonic for
4928 heavyweight sync. Processors without the lwsync
4929 instruction will not decode the L field and will perform a
4930 heavyweight sync. Everything is backward compatible.
4931
4932 sync = sync 0
4933 lwsync = sync 1
cerione43bc882006-01-05 13:11:59 +00004934 ptesync = sync 2 *** TODO - not implemented ***
cerion4e2c2b32006-01-02 13:35:51 +00004935 */
cerione43bc882006-01-05 13:11:59 +00004936 if (b11to20 != 0 || b0 != 0) {
4937 vex_printf("dis_memsync(ppc)(sync/lwsync,b11to20|b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004938 return False;
4939 }
cerione43bc882006-01-05 13:11:59 +00004940 if (flag_L != 0/*sync*/ && flag_L != 1/*lwsync*/) {
4941 vex_printf("dis_memsync(ppc)(sync/lwsync,flag_L)\n");
4942 return False;
4943 }
4944 DIP("%ssync\n", flag_L == 1 ? "lw" : "");
cerionb85e8bb2005-02-16 08:54:33 +00004945 /* Insert a memory fence. It's sometimes important that these
4946 are carried through to the generated code. */
sewardjc4356f02007-11-09 21:15:04 +00004947 stmt( IRStmt_MBE(Imbe_Fence) );
cerionb85e8bb2005-02-16 08:54:33 +00004948 break;
cerionf0de28c2005-12-13 20:21:11 +00004949
cerionf0de28c2005-12-13 20:21:11 +00004950 /* 64bit Memsync */
cerion5b2325f2005-12-23 00:55:09 +00004951 case 0x054: // ldarx (Load DWord and Reserve Indexed, PPC64 p473)
sewardjfe397a22008-08-06 19:13:42 +00004952 /* According to the PowerPC ISA version 2.05, b0 (called EH
4953 in the documentation) is merely a hint bit to the
4954 hardware, I think as to whether or not contention is
4955 likely. So we can just ignore it. */
sewardje9d8a262009-07-01 08:06:34 +00004956 if (!mode64)
4957 return False;
sewardjfe397a22008-08-06 19:13:42 +00004958 DIP("ldarx r%u,r%u,r%u,EH=%u\n", rD_addr, rA_addr, rB_addr, (UInt)b0);
sewardje9d8a262009-07-01 08:06:34 +00004959
4960 // trap if misaligned
4961 gen_SIGBUS_if_misaligned( EA, 8 );
4962
4963 // and actually do the load
4964 putIReg( rD_addr, loadlinkedBE(Ity_I64, mkexpr(EA)) );
cerion07b07a92005-12-22 14:32:35 +00004965 break;
4966
cerion5b2325f2005-12-23 00:55:09 +00004967 case 0x0D6: { // stdcx. (Store DWord Condition Indexd, PPC64 p581)
sewardje9d8a262009-07-01 08:06:34 +00004968 // A marginally simplified version of the stwcx. case
4969 IRTemp rS = newTemp(Ity_I64);
4970 IRTemp resSC;
cerionf0de28c2005-12-13 20:21:11 +00004971 if (b0 != 1) {
cerion5b2325f2005-12-23 00:55:09 +00004972 vex_printf("dis_memsync(ppc)(stdcx.,b0)\n");
cerionf0de28c2005-12-13 20:21:11 +00004973 return False;
4974 }
sewardje9d8a262009-07-01 08:06:34 +00004975 if (!mode64)
4976 return False;
cerionf0de28c2005-12-13 20:21:11 +00004977 DIP("stdcx. r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
sewardje9d8a262009-07-01 08:06:34 +00004978
4979 // trap if misaligned
4980 gen_SIGBUS_if_misaligned( EA, 8 );
4981
4982 // Get the data to be stored
cerion07b07a92005-12-22 14:32:35 +00004983 assign( rS, getIReg(rS_addr) );
cerionf0de28c2005-12-13 20:21:11 +00004984
sewardje9d8a262009-07-01 08:06:34 +00004985 // Do the store, and get success/failure bit into resSC
4986 resSC = newTemp(Ity_I1);
4987 stmt( IRStmt_Store(Iend_BE, resSC, mkexpr(EA), mkexpr(rS)) );
4988
4989 // Set CR0[LT GT EQ S0] = 0b000 || XER[SO] on failure
4990 // Set CR0[LT GT EQ S0] = 0b001 || XER[SO] on success
4991 putCR321(0, binop(Iop_Shl8, unop(Iop_1Uto8, mkexpr(resSC)), mkU8(1)));
cerion07b07a92005-12-22 14:32:35 +00004992 putCR0(0, getXER_SO());
cerionf0de28c2005-12-13 20:21:11 +00004993
sewardje9d8a262009-07-01 08:06:34 +00004994 /* Note:
4995 If resaddr != lwarx_resaddr, CR0[EQ] is undefined, and
4996 whether rS is stored is dependent on that value. */
4997 /* So I guess we can just ignore this case? */
cerion07b07a92005-12-22 14:32:35 +00004998 break;
4999 }
5000
cerionb85e8bb2005-02-16 08:54:33 +00005001 default:
cerion5b2325f2005-12-23 00:55:09 +00005002 vex_printf("dis_memsync(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00005003 return False;
5004 }
5005 break;
cerion8c3adda2005-01-31 11:54:05 +00005006
cerionb85e8bb2005-02-16 08:54:33 +00005007 default:
cerion5b2325f2005-12-23 00:55:09 +00005008 vex_printf("dis_memsync(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00005009 return False;
5010 }
5011 return True;
cerion8c3adda2005-01-31 11:54:05 +00005012}
5013
5014
5015
cerion3d870a32005-03-18 12:23:33 +00005016/*
5017 Integer Shift Instructions
5018*/
cerion645c9302005-01-31 10:09:59 +00005019static Bool dis_int_shift ( UInt theInstr )
5020{
cerionf0de28c2005-12-13 20:21:11 +00005021 /* X-Form, XS-Form */
cerion76de5cf2005-11-18 18:25:12 +00005022 UChar opc1 = ifieldOPC(theInstr);
5023 UChar rS_addr = ifieldRegDS(theInstr);
5024 UChar rA_addr = ifieldRegA(theInstr);
5025 UChar rB_addr = ifieldRegB(theInstr);
5026 UChar sh_imm = rB_addr;
5027 UInt opc2 = ifieldOPClo10(theInstr);
cerionf0de28c2005-12-13 20:21:11 +00005028 UChar b1 = ifieldBIT1(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00005029 UChar flag_rC = ifieldBIT0(theInstr);
5030
ceriond953ebb2005-11-29 13:27:20 +00005031 IRType ty = mode64 ? Ity_I64 : Ity_I32;
5032 IRTemp rA = newTemp(ty);
cerion07b07a92005-12-22 14:32:35 +00005033 IRTemp rS = newTemp(ty);
5034 IRTemp rB = newTemp(ty);
ceriond953ebb2005-11-29 13:27:20 +00005035 IRTemp outofrange = newTemp(Ity_I8);
ceriond953ebb2005-11-29 13:27:20 +00005036 IRTemp rS_lo32 = newTemp(Ity_I32);
5037 IRTemp rB_lo32 = newTemp(Ity_I32);
5038 IRExpr* e_tmp;
5039
cerion07b07a92005-12-22 14:32:35 +00005040 assign( rS, getIReg(rS_addr) );
5041 assign( rB, getIReg(rB_addr) );
sewardje9d8a262009-07-01 08:06:34 +00005042 assign( rS_lo32, mkNarrowTo32(ty, mkexpr(rS)) );
5043 assign( rB_lo32, mkNarrowTo32(ty, mkexpr(rB)) );
cerionb85e8bb2005-02-16 08:54:33 +00005044
5045 if (opc1 == 0x1F) {
5046 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00005047 case 0x018: { // slw (Shift Left Word, PPC32 p505)
cerion5b2325f2005-12-23 00:55:09 +00005048 DIP("slw%s r%u,r%u,r%u\n", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00005049 rA_addr, rS_addr, rB_addr);
5050 /* rA = rS << rB */
5051 /* ppc32 semantics are:
sewardjdfb11442005-10-08 19:58:48 +00005052 slw(x,y) = (x << (y & 31)) -- primary result
5053 & ~((y << 26) >>s 31) -- make result 0
5054 for y in 32 .. 63
5055 */
ceriond953ebb2005-11-29 13:27:20 +00005056 e_tmp =
5057 binop( Iop_And32,
5058 binop( Iop_Shl32,
5059 mkexpr(rS_lo32),
5060 unop( Iop_32to8,
5061 binop(Iop_And32,
5062 mkexpr(rB_lo32), mkU32(31)))),
5063 unop( Iop_Not32,
5064 binop( Iop_Sar32,
5065 binop(Iop_Shl32, mkexpr(rB_lo32), mkU8(26)),
5066 mkU8(31))) );
sewardje9d8a262009-07-01 08:06:34 +00005067 assign( rA, mkWidenFrom32(ty, e_tmp, /* Signed */False) );
cerionb85e8bb2005-02-16 08:54:33 +00005068 break;
ceriond953ebb2005-11-29 13:27:20 +00005069 }
5070
cerion5b2325f2005-12-23 00:55:09 +00005071 case 0x318: { // sraw (Shift Right Alg Word, PPC32 p506)
cerion07b07a92005-12-22 14:32:35 +00005072 IRTemp sh_amt = newTemp(Ity_I32);
cerion5b2325f2005-12-23 00:55:09 +00005073 DIP("sraw%s r%u,r%u,r%u\n", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00005074 rA_addr, rS_addr, rB_addr);
cerion76de5cf2005-11-18 18:25:12 +00005075 /* JRS: my reading of the (poorly worded) PPC32 doc p506 is:
5076 amt = rB & 63
5077 rA = Sar32( rS, amt > 31 ? 31 : amt )
5078 XER.CA = amt > 31 ? sign-of-rS : (computation as per srawi)
5079 */
cerion5b2325f2005-12-23 00:55:09 +00005080 assign( sh_amt, binop(Iop_And32, mkU32(0x3F),
5081 mkexpr(rB_lo32)) );
cerion76de5cf2005-11-18 18:25:12 +00005082 assign( outofrange,
sewardj20ef5472005-07-21 14:48:31 +00005083 unop( Iop_1Uto8,
cerion5b2325f2005-12-23 00:55:09 +00005084 binop(Iop_CmpLT32U, mkU32(31),
5085 mkexpr(sh_amt)) ));
ceriond953ebb2005-11-29 13:27:20 +00005086 e_tmp = binop( Iop_Sar32,
5087 mkexpr(rS_lo32),
sewardj20ef5472005-07-21 14:48:31 +00005088 unop( Iop_32to8,
5089 IRExpr_Mux0X( mkexpr(outofrange),
cerion07b07a92005-12-22 14:32:35 +00005090 mkexpr(sh_amt),
ceriond953ebb2005-11-29 13:27:20 +00005091 mkU32(31)) ) );
sewardje9d8a262009-07-01 08:06:34 +00005092 assign( rA, mkWidenFrom32(ty, e_tmp, /* Signed */True) );
cerion2831b002005-11-30 19:55:22 +00005093
cerion5b2325f2005-12-23 00:55:09 +00005094 set_XER_CA( ty, PPCG_FLAG_OP_SRAW,
cerionf0de28c2005-12-13 20:21:11 +00005095 mkexpr(rA),
sewardje9d8a262009-07-01 08:06:34 +00005096 mkWidenFrom32(ty, mkexpr(rS_lo32), True),
5097 mkWidenFrom32(ty, mkexpr(sh_amt), True ),
5098 mkWidenFrom32(ty, getXER_CA32(), True) );
cerionb85e8bb2005-02-16 08:54:33 +00005099 break;
cerion07b07a92005-12-22 14:32:35 +00005100 }
cerionb85e8bb2005-02-16 08:54:33 +00005101
cerion5b2325f2005-12-23 00:55:09 +00005102 case 0x338: // srawi (Shift Right Alg Word Immediate, PPC32 p507)
5103 DIP("srawi%s r%u,r%u,%d\n", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00005104 rA_addr, rS_addr, sh_imm);
5105 vassert(sh_imm < 32);
cerionf0de28c2005-12-13 20:21:11 +00005106 if (mode64) {
5107 assign( rA, binop(Iop_Sar64,
cerion5b2325f2005-12-23 00:55:09 +00005108 binop(Iop_Shl64, getIReg(rS_addr),
5109 mkU8(32)),
cerionf0de28c2005-12-13 20:21:11 +00005110 mkU8(32 + sh_imm)) );
5111 } else {
cerion5b2325f2005-12-23 00:55:09 +00005112 assign( rA, binop(Iop_Sar32, mkexpr(rS_lo32),
5113 mkU8(sh_imm)) );
cerionf0de28c2005-12-13 20:21:11 +00005114 }
cerion2831b002005-11-30 19:55:22 +00005115
cerion5b2325f2005-12-23 00:55:09 +00005116 set_XER_CA( ty, PPCG_FLAG_OP_SRAWI,
cerionf0de28c2005-12-13 20:21:11 +00005117 mkexpr(rA),
sewardje9d8a262009-07-01 08:06:34 +00005118 mkWidenFrom32(ty, mkexpr(rS_lo32), /* Syned */True),
cerionf0de28c2005-12-13 20:21:11 +00005119 mkSzImm(ty, sh_imm),
sewardje9d8a262009-07-01 08:06:34 +00005120 mkWidenFrom32(ty, getXER_CA32(), /* Syned */False) );
cerionb85e8bb2005-02-16 08:54:33 +00005121 break;
5122
cerione9d361a2005-03-04 17:35:29 +00005123 case 0x218: // srw (Shift Right Word, PPC32 p508)
cerion5b2325f2005-12-23 00:55:09 +00005124 DIP("srw%s r%u,r%u,r%u\n", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00005125 rA_addr, rS_addr, rB_addr);
5126 /* rA = rS >>u rB */
5127 /* ppc32 semantics are:
cerionf0de28c2005-12-13 20:21:11 +00005128 srw(x,y) = (x >>u (y & 31)) -- primary result
sewardjdfb11442005-10-08 19:58:48 +00005129 & ~((y << 26) >>s 31) -- make result 0
5130 for y in 32 .. 63
5131 */
ceriond953ebb2005-11-29 13:27:20 +00005132 e_tmp =
sewardjdfb11442005-10-08 19:58:48 +00005133 binop(
5134 Iop_And32,
5135 binop( Iop_Shr32,
ceriond953ebb2005-11-29 13:27:20 +00005136 mkexpr(rS_lo32),
sewardjdfb11442005-10-08 19:58:48 +00005137 unop( Iop_32to8,
cerion5b2325f2005-12-23 00:55:09 +00005138 binop(Iop_And32, mkexpr(rB_lo32),
5139 mkU32(31)))),
sewardjdfb11442005-10-08 19:58:48 +00005140 unop( Iop_Not32,
5141 binop( Iop_Sar32,
cerion5b2325f2005-12-23 00:55:09 +00005142 binop(Iop_Shl32, mkexpr(rB_lo32),
5143 mkU8(26)),
ceriond953ebb2005-11-29 13:27:20 +00005144 mkU8(31))));
sewardje9d8a262009-07-01 08:06:34 +00005145 assign( rA, mkWidenFrom32(ty, e_tmp, /* Signed */False) );
cerionb85e8bb2005-02-16 08:54:33 +00005146 break;
cerionf0de28c2005-12-13 20:21:11 +00005147
5148
5149 /* 64bit Shifts */
cerion5b2325f2005-12-23 00:55:09 +00005150 case 0x01B: // sld (Shift Left DWord, PPC64 p568)
5151 DIP("sld%s r%u,r%u,r%u\n",
5152 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerionf0de28c2005-12-13 20:21:11 +00005153 /* rA = rS << rB */
cerion07b07a92005-12-22 14:32:35 +00005154 /* ppc64 semantics are:
cerionf0de28c2005-12-13 20:21:11 +00005155 slw(x,y) = (x << (y & 63)) -- primary result
5156 & ~((y << 57) >>s 63) -- make result 0
5157 for y in 64 ..
5158 */
cerionf0de28c2005-12-13 20:21:11 +00005159 assign( rA,
5160 binop(
5161 Iop_And64,
5162 binop( Iop_Shl64,
5163 mkexpr(rS),
5164 unop( Iop_64to8,
5165 binop(Iop_And64, mkexpr(rB), mkU64(63)))),
5166 unop( Iop_Not64,
5167 binop( Iop_Sar64,
5168 binop(Iop_Shl64, mkexpr(rB), mkU8(57)),
5169 mkU8(63)))) );
cerion07b07a92005-12-22 14:32:35 +00005170 break;
cerionf0de28c2005-12-13 20:21:11 +00005171
cerion5b2325f2005-12-23 00:55:09 +00005172 case 0x31A: { // srad (Shift Right Alg DWord, PPC64 p570)
cerion07b07a92005-12-22 14:32:35 +00005173 IRTemp sh_amt = newTemp(Ity_I64);
cerion5b2325f2005-12-23 00:55:09 +00005174 DIP("srad%s r%u,r%u,r%u\n",
5175 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerionf0de28c2005-12-13 20:21:11 +00005176 /* amt = rB & 127
5177 rA = Sar64( rS, amt > 63 ? 63 : amt )
5178 XER.CA = amt > 63 ? sign-of-rS : (computation as per srawi)
5179 */
cerion07b07a92005-12-22 14:32:35 +00005180 assign( sh_amt, binop(Iop_And64, mkU64(0x7F), mkexpr(rB)) );
cerionf0de28c2005-12-13 20:21:11 +00005181 assign( outofrange,
5182 unop( Iop_1Uto8,
cerion5b2325f2005-12-23 00:55:09 +00005183 binop(Iop_CmpLT64U, mkU64(63),
5184 mkexpr(sh_amt)) ));
cerionf0de28c2005-12-13 20:21:11 +00005185 assign( rA,
5186 binop( Iop_Sar64,
5187 mkexpr(rS),
5188 unop( Iop_64to8,
5189 IRExpr_Mux0X( mkexpr(outofrange),
cerion07b07a92005-12-22 14:32:35 +00005190 mkexpr(sh_amt),
5191 mkU64(63)) ))
cerionf0de28c2005-12-13 20:21:11 +00005192 );
cerion5b2325f2005-12-23 00:55:09 +00005193 set_XER_CA( ty, PPCG_FLAG_OP_SRAD,
cerion07b07a92005-12-22 14:32:35 +00005194 mkexpr(rA), mkexpr(rS), mkexpr(sh_amt),
sewardje9d8a262009-07-01 08:06:34 +00005195 mkWidenFrom32(ty, getXER_CA32(), /* Syned */False) );
cerion07b07a92005-12-22 14:32:35 +00005196 break;
5197 }
5198
cerion5b2325f2005-12-23 00:55:09 +00005199 case 0x33A: case 0x33B: // sradi (Shr Alg DWord Imm, PPC64 p571)
cerionf0de28c2005-12-13 20:21:11 +00005200 sh_imm |= b1<<5;
5201 vassert(sh_imm < 64);
cerion5b2325f2005-12-23 00:55:09 +00005202 DIP("sradi%s r%u,r%u,%u\n",
5203 flag_rC ? ".":"", rA_addr, rS_addr, sh_imm);
cerionf0de28c2005-12-13 20:21:11 +00005204 assign( rA, binop(Iop_Sar64, getIReg(rS_addr), mkU8(sh_imm)) );
5205
cerion5b2325f2005-12-23 00:55:09 +00005206 set_XER_CA( ty, PPCG_FLAG_OP_SRADI,
cerionf0de28c2005-12-13 20:21:11 +00005207 mkexpr(rA),
5208 getIReg(rS_addr),
5209 mkU64(sh_imm),
sewardje9d8a262009-07-01 08:06:34 +00005210 mkWidenFrom32(ty, getXER_CA32(), /* Syned */False) );
cerionf0de28c2005-12-13 20:21:11 +00005211 break;
5212
cerion5b2325f2005-12-23 00:55:09 +00005213 case 0x21B: // srd (Shift Right DWord, PPC64 p574)
5214 DIP("srd%s r%u,r%u,r%u\n",
5215 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerionf0de28c2005-12-13 20:21:11 +00005216 /* rA = rS >>u rB */
cerion07b07a92005-12-22 14:32:35 +00005217 /* ppc semantics are:
cerionf0de28c2005-12-13 20:21:11 +00005218 srw(x,y) = (x >>u (y & 63)) -- primary result
5219 & ~((y << 57) >>s 63) -- make result 0
5220 for y in 64 .. 127
5221 */
cerionf0de28c2005-12-13 20:21:11 +00005222 assign( rA,
5223 binop(
5224 Iop_And64,
5225 binop( Iop_Shr64,
5226 mkexpr(rS),
5227 unop( Iop_64to8,
5228 binop(Iop_And64, mkexpr(rB), mkU64(63)))),
5229 unop( Iop_Not64,
5230 binop( Iop_Sar64,
5231 binop(Iop_Shl64, mkexpr(rB), mkU8(57)),
5232 mkU8(63)))) );
cerion07b07a92005-12-22 14:32:35 +00005233 break;
cerionf0de28c2005-12-13 20:21:11 +00005234
cerionb85e8bb2005-02-16 08:54:33 +00005235 default:
cerion5b2325f2005-12-23 00:55:09 +00005236 vex_printf("dis_int_shift(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00005237 return False;
5238 }
5239 } else {
cerion5b2325f2005-12-23 00:55:09 +00005240 vex_printf("dis_int_shift(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00005241 return False;
5242 }
cerion0d330c52005-02-28 16:43:16 +00005243
cerion76de5cf2005-11-18 18:25:12 +00005244 putIReg( rA_addr, mkexpr(rA) );
cerionb85e8bb2005-02-16 08:54:33 +00005245
cerion76de5cf2005-11-18 18:25:12 +00005246 if (flag_rC) {
5247 set_CR0( mkexpr(rA) );
cerionb85e8bb2005-02-16 08:54:33 +00005248 }
5249 return True;
cerion645c9302005-01-31 10:09:59 +00005250}
5251
5252
5253
sewardj602857d2005-09-06 09:10:09 +00005254/*
5255 Integer Load/Store Reverse Instructions
5256*/
sewardj40d8c092006-05-05 13:26:14 +00005257/* Generates code to swap the byte order in an Ity_I32. */
sewardjfb957972005-09-08 17:53:03 +00005258static IRExpr* /* :: Ity_I32 */ gen_byterev32 ( IRTemp t )
5259{
sewardjdd40fdf2006-12-24 02:20:24 +00005260 vassert(typeOfIRTemp(irsb->tyenv, t) == Ity_I32);
sewardjfb957972005-09-08 17:53:03 +00005261 return
5262 binop(Iop_Or32,
5263 binop(Iop_Shl32, mkexpr(t), mkU8(24)),
5264 binop(Iop_Or32,
5265 binop(Iop_And32, binop(Iop_Shl32, mkexpr(t), mkU8(8)),
5266 mkU32(0x00FF0000)),
5267 binop(Iop_Or32,
5268 binop(Iop_And32, binop(Iop_Shr32, mkexpr(t), mkU8(8)),
5269 mkU32(0x0000FF00)),
5270 binop(Iop_And32, binop(Iop_Shr32, mkexpr(t), mkU8(24)),
5271 mkU32(0x000000FF) )
5272 )));
5273}
5274
sewardj40d8c092006-05-05 13:26:14 +00005275/* Generates code to swap the byte order in the lower half of an Ity_I32,
5276 and zeroes the upper half. */
5277static IRExpr* /* :: Ity_I32 */ gen_byterev16 ( IRTemp t )
5278{
sewardjdd40fdf2006-12-24 02:20:24 +00005279 vassert(typeOfIRTemp(irsb->tyenv, t) == Ity_I32);
sewardj40d8c092006-05-05 13:26:14 +00005280 return
5281 binop(Iop_Or32,
5282 binop(Iop_And32, binop(Iop_Shl32, mkexpr(t), mkU8(8)),
5283 mkU32(0x0000FF00)),
5284 binop(Iop_And32, binop(Iop_Shr32, mkexpr(t), mkU8(8)),
5285 mkU32(0x000000FF))
5286 );
5287}
5288
sewardj602857d2005-09-06 09:10:09 +00005289static Bool dis_int_ldst_rev ( UInt theInstr )
5290{
5291 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00005292 UChar opc1 = ifieldOPC(theInstr);
5293 UChar rD_addr = ifieldRegDS(theInstr);
5294 UChar rS_addr = rD_addr;
5295 UChar rA_addr = ifieldRegA(theInstr);
5296 UChar rB_addr = ifieldRegB(theInstr);
5297 UInt opc2 = ifieldOPClo10(theInstr);
5298 UChar b0 = ifieldBIT0(theInstr);
cerionedf7fc52005-11-18 20:57:41 +00005299
ceriond953ebb2005-11-29 13:27:20 +00005300 IRType ty = mode64 ? Ity_I64 : Ity_I32;
5301 IRTemp EA = newTemp(ty);
cerionedf7fc52005-11-18 20:57:41 +00005302 IRTemp w1 = newTemp(Ity_I32);
5303 IRTemp w2 = newTemp(Ity_I32);
sewardj602857d2005-09-06 09:10:09 +00005304
5305 if (opc1 != 0x1F || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00005306 vex_printf("dis_int_ldst_rev(ppc)(opc1|b0)\n");
sewardj602857d2005-09-06 09:10:09 +00005307 return False;
5308 }
sewardjafe85832005-09-09 10:25:39 +00005309
ceriond953ebb2005-11-29 13:27:20 +00005310 assign( EA, ea_rAor0_idxd( rA_addr, rB_addr ) );
sewardj602857d2005-09-06 09:10:09 +00005311
5312 switch (opc2) {
sewardj40d8c092006-05-05 13:26:14 +00005313
5314 case 0x316: // lhbrx (Load Halfword Byte-Reverse Indexed, PPC32 p449)
5315 DIP("lhbrx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
5316 assign( w1, unop(Iop_16Uto32, loadBE(Ity_I16, mkexpr(EA))) );
5317 assign( w2, gen_byterev16(w1) );
sewardje9d8a262009-07-01 08:06:34 +00005318 putIReg( rD_addr, mkWidenFrom32(ty, mkexpr(w2),
5319 /* Signed */False) );
sewardj40d8c092006-05-05 13:26:14 +00005320 break;
5321
sewardjfb957972005-09-08 17:53:03 +00005322 case 0x216: // lwbrx (Load Word Byte-Reverse Indexed, PPC32 p459)
ceriond953ebb2005-11-29 13:27:20 +00005323 DIP("lwbrx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
sewardjfb957972005-09-08 17:53:03 +00005324 assign( w1, loadBE(Ity_I32, mkexpr(EA)) );
5325 assign( w2, gen_byterev32(w1) );
sewardje9d8a262009-07-01 08:06:34 +00005326 putIReg( rD_addr, mkWidenFrom32(ty, mkexpr(w2),
5327 /* Signed */False) );
sewardjfb957972005-09-08 17:53:03 +00005328 break;
sewardj602857d2005-09-06 09:10:09 +00005329
sewardj413a4682006-05-05 13:44:17 +00005330 case 0x396: // sthbrx (Store Half Word Byte-Reverse Indexed, PPC32 p523)
5331 DIP("sthbrx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
sewardje9d8a262009-07-01 08:06:34 +00005332 assign( w1, mkNarrowTo32(ty, getIReg(rS_addr)) );
sewardj413a4682006-05-05 13:44:17 +00005333 storeBE( mkexpr(EA), unop(Iop_32to16, gen_byterev16(w1)) );
5334 break;
sewardj602857d2005-09-06 09:10:09 +00005335
cerion5b2325f2005-12-23 00:55:09 +00005336 case 0x296: // stwbrx (Store Word Byte-Reverse Indxd, PPC32 p531)
ceriond953ebb2005-11-29 13:27:20 +00005337 DIP("stwbrx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
sewardje9d8a262009-07-01 08:06:34 +00005338 assign( w1, mkNarrowTo32(ty, getIReg(rS_addr)) );
sewardjfb957972005-09-08 17:53:03 +00005339 storeBE( mkexpr(EA), gen_byterev32(w1) );
5340 break;
5341
5342 default:
cerion5b2325f2005-12-23 00:55:09 +00005343 vex_printf("dis_int_ldst_rev(ppc)(opc2)\n");
sewardjfb957972005-09-08 17:53:03 +00005344 return False;
sewardj602857d2005-09-06 09:10:09 +00005345 }
5346 return True;
5347}
cerion645c9302005-01-31 10:09:59 +00005348
5349
5350
cerion3d870a32005-03-18 12:23:33 +00005351/*
5352 Processor Control Instructions
5353*/
sewardjdd40fdf2006-12-24 02:20:24 +00005354static Bool dis_proc_ctl ( VexAbiInfo* vbi, UInt theInstr )
cerion8c3adda2005-01-31 11:54:05 +00005355{
cerion76de5cf2005-11-18 18:25:12 +00005356 UChar opc1 = ifieldOPC(theInstr);
cerionb85e8bb2005-02-16 08:54:33 +00005357
5358 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00005359 UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) );
5360 UChar b21to22 = toUChar( IFIELD( theInstr, 21, 2 ) );
5361 UChar rD_addr = ifieldRegDS(theInstr);
5362 UInt b11to20 = IFIELD( theInstr, 11, 10 );
cerione9d361a2005-03-04 17:35:29 +00005363
cerion76de5cf2005-11-18 18:25:12 +00005364 /* XFX-Form */
5365 UChar rS_addr = rD_addr;
5366 UInt SPR = b11to20;
cerionedf7fc52005-11-18 20:57:41 +00005367 UInt TBR = b11to20;
cerion76de5cf2005-11-18 18:25:12 +00005368 UChar b20 = toUChar( IFIELD( theInstr, 20, 1 ) );
5369 UInt CRM = IFIELD( theInstr, 12, 8 );
5370 UChar b11 = toUChar( IFIELD( theInstr, 11, 1 ) );
5371
5372 UInt opc2 = ifieldOPClo10(theInstr);
5373 UChar b0 = ifieldBIT0(theInstr);
5374
cerion2831b002005-11-30 19:55:22 +00005375 IRType ty = mode64 ? Ity_I64 : Ity_I32;
cerionf0de28c2005-12-13 20:21:11 +00005376 IRTemp rS = newTemp(ty);
ceriond953ebb2005-11-29 13:27:20 +00005377 assign( rS, getIReg(rS_addr) );
sewardj41a7b702005-11-18 22:18:23 +00005378
cerion76de5cf2005-11-18 18:25:12 +00005379 /* Reorder SPR field as per PPC32 p470 */
5380 SPR = ((SPR & 0x1F) << 5) | ((SPR >> 5) & 0x1F);
sewardj73a91972005-09-06 10:25:46 +00005381 /* Reorder TBR field as per PPC32 p475 */
5382 TBR = ((TBR & 31) << 5) | ((TBR >> 5) & 31);
cerionb85e8bb2005-02-16 08:54:33 +00005383
5384 if (opc1 != 0x1F || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00005385 vex_printf("dis_proc_ctl(ppc)(opc1|b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00005386 return False;
5387 }
5388
5389 switch (opc2) {
cerioncb14e732005-09-09 16:38:19 +00005390 /* X-Form */
cerion5b2325f2005-12-23 00:55:09 +00005391 case 0x200: { // mcrxr (Move to Cond Register from XER, PPC32 p466)
cerioncb14e732005-09-09 16:38:19 +00005392 if (b21to22 != 0 || b11to20 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00005393 vex_printf("dis_proc_ctl(ppc)(mcrxr,b21to22|b11to20)\n");
cerioncb14e732005-09-09 16:38:19 +00005394 return False;
5395 }
5396 DIP("mcrxr crf%d\n", crfD);
cerionedf7fc52005-11-18 20:57:41 +00005397 /* Move XER[0-3] (the top 4 bits of XER) to CR[crfD] */
ceriond953ebb2005-11-29 13:27:20 +00005398 putGST_field( PPC_GST_CR,
5399 getGST_field( PPC_GST_XER, 7 ),
cerionedf7fc52005-11-18 20:57:41 +00005400 crfD );
sewardj55ccc3e2005-09-09 19:45:02 +00005401
5402 // Clear XER[0-3]
cerionedf7fc52005-11-18 20:57:41 +00005403 putXER_SO( mkU8(0) );
5404 putXER_OV( mkU8(0) );
5405 putXER_CA( mkU8(0) );
cerioncb14e732005-09-09 16:38:19 +00005406 break;
sewardj55ccc3e2005-09-09 19:45:02 +00005407 }
cerionb85e8bb2005-02-16 08:54:33 +00005408
sewardj72aefb22006-03-01 18:58:39 +00005409 case 0x013:
5410 // b11to20==0: mfcr (Move from Cond Register, PPC32 p467)
5411 // b20==1 & b11==0: mfocrf (Move from One CR Field)
5412 // However it seems that the 'mfcr' behaviour is an acceptable
5413 // implementation of mfocr (from the 2.02 arch spec)
5414 if (b11to20 == 0) {
5415 DIP("mfcr r%u\n", rD_addr);
sewardje9d8a262009-07-01 08:06:34 +00005416 putIReg( rD_addr, mkWidenFrom32(ty, getGST( PPC_GST_CR ),
5417 /* Signed */False) );
sewardj72aefb22006-03-01 18:58:39 +00005418 break;
cerionb85e8bb2005-02-16 08:54:33 +00005419 }
sewardj72aefb22006-03-01 18:58:39 +00005420 if (b20 == 1 && b11 == 0) {
5421 DIP("mfocrf r%u,%u\n", rD_addr, CRM);
sewardje9d8a262009-07-01 08:06:34 +00005422 putIReg( rD_addr, mkWidenFrom32(ty, getGST( PPC_GST_CR ),
5423 /* Signed */False) );
sewardj72aefb22006-03-01 18:58:39 +00005424 break;
5425 }
5426 /* not decodable */
5427 return False;
5428
cerionb85e8bb2005-02-16 08:54:33 +00005429 /* XFX-Form */
cerione9d361a2005-03-04 17:35:29 +00005430 case 0x153: // mfspr (Move from Special-Purpose Register, PPC32 p470)
cerionb85e8bb2005-02-16 08:54:33 +00005431
cerion76de5cf2005-11-18 18:25:12 +00005432 switch (SPR) { // Choose a register...
ceriond953ebb2005-11-29 13:27:20 +00005433 case 0x1:
5434 DIP("mfxer r%u\n", rD_addr);
sewardje9d8a262009-07-01 08:06:34 +00005435 putIReg( rD_addr, mkWidenFrom32(ty, getGST( PPC_GST_XER ),
5436 /* Signed */False) );
ceriond953ebb2005-11-29 13:27:20 +00005437 break;
5438 case 0x8:
5439 DIP("mflr r%u\n", rD_addr);
5440 putIReg( rD_addr, getGST( PPC_GST_LR ) );
5441 break;
5442 case 0x9:
5443 DIP("mfctr r%u\n", rD_addr);
5444 putIReg( rD_addr, getGST( PPC_GST_CTR ) );
5445 break;
5446 case 0x100:
5447 DIP("mfvrsave r%u\n", rD_addr);
sewardje9d8a262009-07-01 08:06:34 +00005448 putIReg( rD_addr, mkWidenFrom32(ty, getGST( PPC_GST_VRSAVE ),
5449 /* Signed */False) );
ceriond953ebb2005-11-29 13:27:20 +00005450 break;
sewardjaca070a2006-10-17 00:28:22 +00005451
5452 case 0x103:
5453 DIP("mfspr r%u, SPRG3(readonly)\n", rD_addr);
5454 putIReg( rD_addr, getGST( PPC_GST_SPRG3_RO ) );
5455 break;
5456
sewardjabb321c2006-12-27 18:39:46 +00005457 /* Even a lowly PPC7400 can run the associated helper, so no
5458 obvious need for feature testing at this point. */
5459 case 268 /* 0x10C */:
5460 case 269 /* 0x10D */: {
5461 UInt arg = SPR==268 ? 0 : 1;
5462 IRTemp val = newTemp(Ity_I32);
5463 IRExpr** args = mkIRExprVec_1( mkU32(arg) );
5464 IRDirty* d = unsafeIRDirty_1_N(
5465 val,
5466 0/*regparms*/,
5467 "ppc32g_dirtyhelper_MFSPR_268_269",
5468 fnptr_to_fnentry
5469 (vbi, &ppc32g_dirtyhelper_MFSPR_268_269),
5470 args
5471 );
5472 /* execute the dirty call, dumping the result in val. */
5473 stmt( IRStmt_Dirty(d) );
sewardj37b2ee82009-08-02 14:35:45 +00005474 putIReg( rD_addr,
5475 mkWidenFrom32(ty, mkexpr(val), False/*unsigned*/) );
5476 DIP("mfspr r%u,%u", rD_addr, (UInt)SPR);
5477 break;
5478 }
5479
5480 /* Again, runs natively on PPC7400 (7447, really). Not
5481 bothering with a feature test. */
5482 case 287: /* 0x11F */ {
5483 IRTemp val = newTemp(Ity_I32);
5484 IRExpr** args = mkIRExprVec_0();
5485 IRDirty* d = unsafeIRDirty_1_N(
5486 val,
5487 0/*regparms*/,
5488 "ppc32g_dirtyhelper_MFSPR_287",
5489 fnptr_to_fnentry
5490 (vbi, &ppc32g_dirtyhelper_MFSPR_287),
5491 args
5492 );
5493 /* execute the dirty call, dumping the result in val. */
5494 stmt( IRStmt_Dirty(d) );
5495 putIReg( rD_addr,
5496 mkWidenFrom32(ty, mkexpr(val), False/*unsigned*/) );
sewardjabb321c2006-12-27 18:39:46 +00005497 DIP("mfspr r%u,%u", rD_addr, (UInt)SPR);
5498 break;
5499 }
5500
ceriond953ebb2005-11-29 13:27:20 +00005501 default:
cerion5b2325f2005-12-23 00:55:09 +00005502 vex_printf("dis_proc_ctl(ppc)(mfspr,SPR)(0x%x)\n", SPR);
ceriond953ebb2005-11-29 13:27:20 +00005503 return False;
cerionb85e8bb2005-02-16 08:54:33 +00005504 }
5505 break;
5506
sewardj73a91972005-09-06 10:25:46 +00005507 case 0x173: { // mftb (Move from Time Base, PPC32 p475)
5508 IRTemp val = newTemp(Ity_I64);
5509 IRExpr** args = mkIRExprVec_0();
cerion4c4f5ef2006-01-02 14:41:50 +00005510 IRDirty* d = unsafeIRDirty_1_N(
5511 val,
5512 0/*regparms*/,
5513 "ppcg_dirtyhelper_MFTB",
sewardjdd40fdf2006-12-24 02:20:24 +00005514 fnptr_to_fnentry(vbi, &ppcg_dirtyhelper_MFTB),
cerion4c4f5ef2006-01-02 14:41:50 +00005515 args );
sewardj73a91972005-09-06 10:25:46 +00005516 /* execute the dirty call, dumping the result in val. */
5517 stmt( IRStmt_Dirty(d) );
5518
5519 switch (TBR) {
ceriond953ebb2005-11-29 13:27:20 +00005520 case 269:
ceriond953ebb2005-11-29 13:27:20 +00005521 DIP("mftbu r%u", rD_addr);
cerion2831b002005-11-30 19:55:22 +00005522 putIReg( rD_addr,
sewardje9d8a262009-07-01 08:06:34 +00005523 mkWidenFrom32(ty, unop(Iop_64HIto32, mkexpr(val)),
5524 /* Signed */False) );
ceriond953ebb2005-11-29 13:27:20 +00005525 break;
5526 case 268:
ceriond953ebb2005-11-29 13:27:20 +00005527 DIP("mftb r%u", rD_addr);
cerion2831b002005-11-30 19:55:22 +00005528 putIReg( rD_addr, (mode64) ? mkexpr(val) :
5529 unop(Iop_64to32, mkexpr(val)) );
ceriond953ebb2005-11-29 13:27:20 +00005530 break;
5531 default:
5532 return False; /* illegal instruction */
sewardj73a91972005-09-06 10:25:46 +00005533 }
5534 break;
5535 }
5536
sewardj72aefb22006-03-01 18:58:39 +00005537 case 0x090: {
5538 // b20==0: mtcrf (Move to Cond Register Fields, PPC32 p477)
5539 // b20==1: mtocrf (Move to One Cond Reg Field)
cerionedf7fc52005-11-18 20:57:41 +00005540 Int cr;
5541 UChar shft;
sewardj72aefb22006-03-01 18:58:39 +00005542 if (b11 != 0)
cerionb85e8bb2005-02-16 08:54:33 +00005543 return False;
sewardj72aefb22006-03-01 18:58:39 +00005544 if (b20 == 1) {
5545 /* ppc64 v2.02 spec says mtocrf gives undefined outcome if >
5546 1 field is written. It seems more robust to decline to
5547 decode the insn if so. */
5548 switch (CRM) {
5549 case 0x01: case 0x02: case 0x04: case 0x08:
5550 case 0x10: case 0x20: case 0x40: case 0x80:
5551 break;
5552 default:
5553 return False;
5554 }
cerionb85e8bb2005-02-16 08:54:33 +00005555 }
sewardj72aefb22006-03-01 18:58:39 +00005556 DIP("%s 0x%x,r%u\n", b20==1 ? "mtocrf" : "mtcrf",
5557 CRM, rS_addr);
cerionedf7fc52005-11-18 20:57:41 +00005558 /* Write to each field specified by CRM */
5559 for (cr = 0; cr < 8; cr++) {
5560 if ((CRM & (1 << (7-cr))) == 0)
5561 continue;
5562 shft = 4*(7-cr);
ceriond953ebb2005-11-29 13:27:20 +00005563 putGST_field( PPC_GST_CR,
cerion2831b002005-11-30 19:55:22 +00005564 binop(Iop_Shr32,
sewardje9d8a262009-07-01 08:06:34 +00005565 mkNarrowTo32(ty, mkexpr(rS)),
cerion2831b002005-11-30 19:55:22 +00005566 mkU8(shft)), cr );
cerionedf7fc52005-11-18 20:57:41 +00005567 }
cerionb85e8bb2005-02-16 08:54:33 +00005568 break;
cerionedf7fc52005-11-18 20:57:41 +00005569 }
cerione9d361a2005-03-04 17:35:29 +00005570
5571 case 0x1D3: // mtspr (Move to Special-Purpose Register, PPC32 p483)
cerionb85e8bb2005-02-16 08:54:33 +00005572
cerion76de5cf2005-11-18 18:25:12 +00005573 switch (SPR) { // Choose a register...
ceriond953ebb2005-11-29 13:27:20 +00005574 case 0x1:
5575 DIP("mtxer r%u\n", rS_addr);
sewardje9d8a262009-07-01 08:06:34 +00005576 putGST( PPC_GST_XER, mkNarrowTo32(ty, mkexpr(rS)) );
ceriond953ebb2005-11-29 13:27:20 +00005577 break;
5578 case 0x8:
5579 DIP("mtlr r%u\n", rS_addr);
5580 putGST( PPC_GST_LR, mkexpr(rS) );
5581 break;
5582 case 0x9:
5583 DIP("mtctr r%u\n", rS_addr);
5584 putGST( PPC_GST_CTR, mkexpr(rS) );
5585 break;
5586 case 0x100:
5587 DIP("mtvrsave r%u\n", rS_addr);
sewardje9d8a262009-07-01 08:06:34 +00005588 putGST( PPC_GST_VRSAVE, mkNarrowTo32(ty, mkexpr(rS)) );
ceriond953ebb2005-11-29 13:27:20 +00005589 break;
5590
5591 default:
cerion5b2325f2005-12-23 00:55:09 +00005592 vex_printf("dis_proc_ctl(ppc)(mtspr,SPR)(%u)\n", SPR);
ceriond953ebb2005-11-29 13:27:20 +00005593 return False;
cerionb85e8bb2005-02-16 08:54:33 +00005594 }
5595 break;
5596
5597 default:
cerion5b2325f2005-12-23 00:55:09 +00005598 vex_printf("dis_proc_ctl(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00005599 return False;
5600 }
5601 return True;
cerion8c3adda2005-01-31 11:54:05 +00005602}
5603
5604
cerion3d870a32005-03-18 12:23:33 +00005605/*
5606 Cache Management Instructions
5607*/
sewardjd94b73a2005-06-30 12:08:48 +00005608static Bool dis_cache_manage ( UInt theInstr,
sewardj9e6491a2005-07-02 19:24:10 +00005609 DisResult* dres,
sewardjd94b73a2005-06-30 12:08:48 +00005610 VexArchInfo* guest_archinfo )
cerion8c3adda2005-01-31 11:54:05 +00005611{
cerionb85e8bb2005-02-16 08:54:33 +00005612 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00005613 UChar opc1 = ifieldOPC(theInstr);
5614 UChar b21to25 = ifieldRegDS(theInstr);
5615 UChar rA_addr = ifieldRegA(theInstr);
5616 UChar rB_addr = ifieldRegB(theInstr);
5617 UInt opc2 = ifieldOPClo10(theInstr);
5618 UChar b0 = ifieldBIT0(theInstr);
cerion5b2325f2005-12-23 00:55:09 +00005619 UInt lineszB = guest_archinfo->ppc_cache_line_szB;
ceriond953ebb2005-11-29 13:27:20 +00005620
5621 IRType ty = mode64 ? Ity_I64 : Ity_I32;
cerion094d1392005-06-20 13:45:57 +00005622
sewardj6be67232006-01-24 19:00:05 +00005623 /* For dcbt, the lowest two bits of b21to25 encode an
5624 access-direction hint (TH field) which we ignore. Well, that's
5625 what the PowerPC documentation says. In fact xlc -O4 on POWER5
5626 seems to generate values of 8 and 10 for b21to25. */
5627 if (opc1 == 0x1F && opc2 == 0x116) {
5628 /* b21to25 &= ~3; */ /* if the docs were true */
5629 b21to25 = 0; /* blunt instrument */
5630 }
5631
cerionb85e8bb2005-02-16 08:54:33 +00005632 if (opc1 != 0x1F || b21to25 != 0 || b0 != 0) {
sewardj6be67232006-01-24 19:00:05 +00005633 if (0) vex_printf("dis_cache_manage %d %d %d\n",
5634 (Int)opc1, (Int)b21to25, (Int)b0);
cerion5b2325f2005-12-23 00:55:09 +00005635 vex_printf("dis_cache_manage(ppc)(opc1|b21to25|b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00005636 return False;
5637 }
sewardjd94b73a2005-06-30 12:08:48 +00005638
5639 /* stay sane .. */
sewardjc85bf022008-05-29 16:39:21 +00005640 vassert(lineszB == 32 || lineszB == 64 || lineszB == 128);
cerionb85e8bb2005-02-16 08:54:33 +00005641
5642 switch (opc2) {
sewardjb51f0f42005-07-18 11:38:02 +00005643//zz case 0x2F6: // dcba (Data Cache Block Allocate, PPC32 p380)
5644//zz vassert(0); /* AWAITING TEST CASE */
ceriond953ebb2005-11-29 13:27:20 +00005645//zz DIP("dcba r%u,r%u\n", rA_addr, rB_addr);
cerion5b2325f2005-12-23 00:55:09 +00005646//zz if (0) vex_printf("vex ppc->IR: kludged dcba\n");
sewardjb51f0f42005-07-18 11:38:02 +00005647//zz break;
sewardj20ef5472005-07-21 14:48:31 +00005648
5649 case 0x056: // dcbf (Data Cache Block Flush, PPC32 p382)
ceriond953ebb2005-11-29 13:27:20 +00005650 DIP("dcbf r%u,r%u\n", rA_addr, rB_addr);
sewardj20ef5472005-07-21 14:48:31 +00005651 /* nop as far as vex is concerned */
sewardj20ef5472005-07-21 14:48:31 +00005652 break;
cerionb85e8bb2005-02-16 08:54:33 +00005653
cerione9d361a2005-03-04 17:35:29 +00005654 case 0x036: // dcbst (Data Cache Block Store, PPC32 p384)
ceriond953ebb2005-11-29 13:27:20 +00005655 DIP("dcbst r%u,r%u\n", rA_addr, rB_addr);
sewardjb51f0f42005-07-18 11:38:02 +00005656 /* nop as far as vex is concerned */
cerionb85e8bb2005-02-16 08:54:33 +00005657 break;
cerion8c3adda2005-01-31 11:54:05 +00005658
cerione9d361a2005-03-04 17:35:29 +00005659 case 0x116: // dcbt (Data Cache Block Touch, PPC32 p385)
ceriond953ebb2005-11-29 13:27:20 +00005660 DIP("dcbt r%u,r%u\n", rA_addr, rB_addr);
sewardjb51f0f42005-07-18 11:38:02 +00005661 /* nop as far as vex is concerned */
cerionb85e8bb2005-02-16 08:54:33 +00005662 break;
5663
cerione9d361a2005-03-04 17:35:29 +00005664 case 0x0F6: // dcbtst (Data Cache Block Touch for Store, PPC32 p386)
ceriond953ebb2005-11-29 13:27:20 +00005665 DIP("dcbtst r%u,r%u\n", rA_addr, rB_addr);
sewardjb51f0f42005-07-18 11:38:02 +00005666 /* nop as far as vex is concerned */
cerionb85e8bb2005-02-16 08:54:33 +00005667 break;
5668
cerion094d1392005-06-20 13:45:57 +00005669 case 0x3F6: { // dcbz (Data Cache Block Clear to Zero, PPC32 p387)
sewardjd94b73a2005-06-30 12:08:48 +00005670 /* Clear all bytes in cache block at (rA|0) + rB. */
cerion2831b002005-11-30 19:55:22 +00005671 IRTemp EA = newTemp(ty);
5672 IRTemp addr = newTemp(ty);
cerion094d1392005-06-20 13:45:57 +00005673 IRExpr* irx_addr;
cerion094d1392005-06-20 13:45:57 +00005674 UInt i;
ceriond953ebb2005-11-29 13:27:20 +00005675 DIP("dcbz r%u,r%u\n", rA_addr, rB_addr);
sewardjcb1f68e2005-12-30 03:39:14 +00005676
ceriond953ebb2005-11-29 13:27:20 +00005677 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
cerion094d1392005-06-20 13:45:57 +00005678
cerion2831b002005-11-30 19:55:22 +00005679 if (mode64) {
5680 /* Round EA down to the start of the containing block. */
5681 assign( addr, binop( Iop_And64,
5682 mkexpr(EA),
5683 mkU64( ~((ULong)lineszB-1) )) );
5684
5685 for (i = 0; i < lineszB / 8; i++) {
5686 irx_addr = binop( Iop_Add64, mkexpr(addr), mkU64(i*8) );
5687 storeBE( irx_addr, mkU64(0) );
5688 }
5689 } else {
5690 /* Round EA down to the start of the containing block. */
5691 assign( addr, binop( Iop_And32,
5692 mkexpr(EA),
5693 mkU32( ~(lineszB-1) )) );
5694
5695 for (i = 0; i < lineszB / 4; i++) {
5696 irx_addr = binop( Iop_Add32, mkexpr(addr), mkU32(i*4) );
5697 storeBE( irx_addr, mkU32(0) );
5698 }
cerion094d1392005-06-20 13:45:57 +00005699 }
cerionb85e8bb2005-02-16 08:54:33 +00005700 break;
cerion094d1392005-06-20 13:45:57 +00005701 }
cerion8c3adda2005-01-31 11:54:05 +00005702
sewardj7ce9d152005-03-15 16:54:13 +00005703 case 0x3D6: {
5704 // icbi (Instruction Cache Block Invalidate, PPC32 p431)
5705 /* Invalidate all translations containing code from the cache
sewardjd94b73a2005-06-30 12:08:48 +00005706 block at (rA|0) + rB. */
ceriond953ebb2005-11-29 13:27:20 +00005707 IRTemp EA = newTemp(ty);
5708 IRTemp addr = newTemp(ty);
5709 DIP("icbi r%u,r%u\n", rA_addr, rB_addr);
5710 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
sewardj7ce9d152005-03-15 16:54:13 +00005711
cerion2831b002005-11-30 19:55:22 +00005712 /* Round EA down to the start of the containing block. */
5713 assign( addr, binop( mkSzOp(ty, Iop_And8),
ceriond953ebb2005-11-29 13:27:20 +00005714 mkexpr(EA),
cerion2831b002005-11-30 19:55:22 +00005715 mkSzImm(ty, ~(((ULong)lineszB)-1) )) );
ceriond953ebb2005-11-29 13:27:20 +00005716 putGST( PPC_GST_TISTART, mkexpr(addr) );
cerion2831b002005-11-30 19:55:22 +00005717 putGST( PPC_GST_TILEN, mkSzImm(ty, lineszB) );
sewardj7ce9d152005-03-15 16:54:13 +00005718
sewardja8078f62005-03-15 18:27:40 +00005719 /* be paranoid ... */
sewardjc4356f02007-11-09 21:15:04 +00005720 stmt( IRStmt_MBE(Imbe_Fence) );
sewardja8078f62005-03-15 18:27:40 +00005721
sewardjdd40fdf2006-12-24 02:20:24 +00005722 irsb->jumpkind = Ijk_TInval;
5723 irsb->next = mkSzImm(ty, nextInsnAddr());
sewardj9e6491a2005-07-02 19:24:10 +00005724 dres->whatNext = Dis_StopHere;
cerionb85e8bb2005-02-16 08:54:33 +00005725 break;
sewardj7ce9d152005-03-15 16:54:13 +00005726 }
cerion8c3adda2005-01-31 11:54:05 +00005727
cerionb85e8bb2005-02-16 08:54:33 +00005728 default:
cerion5b2325f2005-12-23 00:55:09 +00005729 vex_printf("dis_cache_manage(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00005730 return False;
5731 }
5732 return True;
cerion8c3adda2005-01-31 11:54:05 +00005733}
5734
5735
sewardje14bb9f2005-07-22 09:39:02 +00005736/*------------------------------------------------------------*/
5737/*--- Floating Point Helpers ---*/
5738/*------------------------------------------------------------*/
5739
sewardje14bb9f2005-07-22 09:39:02 +00005740/* --------- Synthesise a 2-bit FPU rounding mode. --------- */
5741/* Produces a value in 0 .. 3, which is encoded as per the type
cerion5b2325f2005-12-23 00:55:09 +00005742 IRRoundingMode. PPCRoundingMode encoding is different to
sewardje14bb9f2005-07-22 09:39:02 +00005743 IRRoundingMode, so need to map it.
5744*/
sewardjb183b852006-02-03 16:08:03 +00005745static IRExpr* /* :: Ity_I32 */ get_IR_roundingmode ( void )
sewardje14bb9f2005-07-22 09:39:02 +00005746{
5747/*
5748 rounding mode | PPC | IR
5749 ------------------------
5750 to nearest | 00 | 00
5751 to zero | 01 | 11
5752 to +infinity | 10 | 10
5753 to -infinity | 11 | 01
5754*/
5755 IRTemp rm_PPC32 = newTemp(Ity_I32);
ceriond953ebb2005-11-29 13:27:20 +00005756 assign( rm_PPC32, getGST_masked( PPC_GST_FPSCR, MASK_FPSCR_RN ) );
sewardje14bb9f2005-07-22 09:39:02 +00005757
5758 // rm_IR = XOR( rm_PPC32, (rm_PPC32 << 1) & 2)
sewardjb183b852006-02-03 16:08:03 +00005759 return binop( Iop_Xor32,
5760 mkexpr(rm_PPC32),
5761 binop( Iop_And32,
5762 binop(Iop_Shl32, mkexpr(rm_PPC32), mkU8(1)),
5763 mkU32(2) ));
sewardje14bb9f2005-07-22 09:39:02 +00005764}
cerion094d1392005-06-20 13:45:57 +00005765
5766
cerion3d870a32005-03-18 12:23:33 +00005767/*------------------------------------------------------------*/
5768/*--- Floating Point Instruction Translation ---*/
5769/*------------------------------------------------------------*/
5770
5771/*
5772 Floating Point Load Instructions
5773*/
5774static Bool dis_fp_load ( UInt theInstr )
5775{
cerion76de5cf2005-11-18 18:25:12 +00005776 /* X-Form, D-Form */
5777 UChar opc1 = ifieldOPC(theInstr);
5778 UChar frD_addr = ifieldRegDS(theInstr);
5779 UChar rA_addr = ifieldRegA(theInstr);
5780 UChar rB_addr = ifieldRegB(theInstr);
5781 UInt opc2 = ifieldOPClo10(theInstr);
5782 UChar b0 = ifieldBIT0(theInstr);
cerion2831b002005-11-30 19:55:22 +00005783 UInt uimm16 = ifieldUIMM16(theInstr);
cerion094d1392005-06-20 13:45:57 +00005784
cerion2831b002005-11-30 19:55:22 +00005785 Int simm16 = extend_s_16to32(uimm16);
5786 IRType ty = mode64 ? Ity_I64 : Ity_I32;
5787 IRTemp EA = newTemp(ty);
5788 IRTemp rA = newTemp(ty);
5789 IRTemp rB = newTemp(ty);
cerion094d1392005-06-20 13:45:57 +00005790
5791 assign( rA, getIReg(rA_addr) );
5792 assign( rB, getIReg(rB_addr) );
ceriond953ebb2005-11-29 13:27:20 +00005793
sewardjb183b852006-02-03 16:08:03 +00005794 /* These are completely straightforward from a rounding and status
5795 bits perspective: no rounding involved and no funny status or CR
5796 bits affected. */
cerion3d870a32005-03-18 12:23:33 +00005797
sewardjb183b852006-02-03 16:08:03 +00005798 switch (opc1) {
sewardje14bb9f2005-07-22 09:39:02 +00005799 case 0x30: // lfs (Load Float Single, PPC32 p441)
ceriond953ebb2005-11-29 13:27:20 +00005800 DIP("lfs fr%u,%d(r%u)\n", frD_addr, simm16, rA_addr);
5801 assign( EA, ea_rAor0_simm(rA_addr, simm16) );
cerion5b2325f2005-12-23 00:55:09 +00005802 putFReg( frD_addr,
5803 unop(Iop_F32toF64, loadBE(Ity_F32, mkexpr(EA))) );
sewardje14bb9f2005-07-22 09:39:02 +00005804 break;
5805
cerion5b2325f2005-12-23 00:55:09 +00005806 case 0x31: // lfsu (Load Float Single, Update, PPC32 p442)
sewardjb183b852006-02-03 16:08:03 +00005807 if (rA_addr == 0)
cerion729edb72005-12-02 16:03:46 +00005808 return False;
cerion729edb72005-12-02 16:03:46 +00005809 DIP("lfsu fr%u,%d(r%u)\n", frD_addr, simm16, rA_addr);
5810 assign( EA, ea_rA_simm(rA_addr, simm16) );
cerion5b2325f2005-12-23 00:55:09 +00005811 putFReg( frD_addr,
5812 unop(Iop_F32toF64, loadBE(Ity_F32, mkexpr(EA))) );
cerion729edb72005-12-02 16:03:46 +00005813 putIReg( rA_addr, mkexpr(EA) );
5814 break;
cerion094d1392005-06-20 13:45:57 +00005815
5816 case 0x32: // lfd (Load Float Double, PPC32 p437)
ceriond953ebb2005-11-29 13:27:20 +00005817 DIP("lfd fr%u,%d(r%u)\n", frD_addr, simm16, rA_addr);
5818 assign( EA, ea_rAor0_simm(rA_addr, simm16) );
cerion094d1392005-06-20 13:45:57 +00005819 putFReg( frD_addr, loadBE(Ity_F64, mkexpr(EA)) );
5820 break;
cerion3d870a32005-03-18 12:23:33 +00005821
cerion5b2325f2005-12-23 00:55:09 +00005822 case 0x33: // lfdu (Load Float Double, Update, PPC32 p438)
sewardjb183b852006-02-03 16:08:03 +00005823 if (rA_addr == 0)
sewardj0e2cc672005-07-29 21:58:51 +00005824 return False;
ceriond953ebb2005-11-29 13:27:20 +00005825 DIP("lfdu fr%u,%d(r%u)\n", frD_addr, simm16, rA_addr);
5826 assign( EA, ea_rA_simm(rA_addr, simm16) );
sewardj0e2cc672005-07-29 21:58:51 +00005827 putFReg( frD_addr, loadBE(Ity_F64, mkexpr(EA)) );
5828 putIReg( rA_addr, mkexpr(EA) );
5829 break;
sewardje14bb9f2005-07-22 09:39:02 +00005830
5831 case 0x1F:
5832 if (b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00005833 vex_printf("dis_fp_load(ppc)(instr,b0)\n");
sewardje14bb9f2005-07-22 09:39:02 +00005834 return False;
5835 }
5836
5837 switch(opc2) {
ceriond953ebb2005-11-29 13:27:20 +00005838 case 0x217: // lfsx (Load Float Single Indexed, PPC32 p444)
5839 DIP("lfsx fr%u,r%u,r%u\n", frD_addr, rA_addr, rB_addr);
5840 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
5841 putFReg( frD_addr, unop( Iop_F32toF64,
5842 loadBE(Ity_F32, mkexpr(EA))) );
5843 break;
5844
cerion5b2325f2005-12-23 00:55:09 +00005845 case 0x237: // lfsux (Load Float Single, Update Indxd, PPC32 p443)
sewardjb183b852006-02-03 16:08:03 +00005846 if (rA_addr == 0)
sewardje14bb9f2005-07-22 09:39:02 +00005847 return False;
ceriond953ebb2005-11-29 13:27:20 +00005848 DIP("lfsux fr%u,r%u,r%u\n", frD_addr, rA_addr, rB_addr);
5849 assign( EA, ea_rA_idxd(rA_addr, rB_addr) );
cerion5b2325f2005-12-23 00:55:09 +00005850 putFReg( frD_addr,
5851 unop(Iop_F32toF64, loadBE(Ity_F32, mkexpr(EA))) );
ceriond953ebb2005-11-29 13:27:20 +00005852 putIReg( rA_addr, mkexpr(EA) );
5853 break;
5854
5855 case 0x257: // lfdx (Load Float Double Indexed, PPC32 p440)
5856 DIP("lfdx fr%u,r%u,r%u\n", frD_addr, rA_addr, rB_addr);
5857 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
5858 putFReg( frD_addr, loadBE(Ity_F64, mkexpr(EA)) );
5859 break;
5860
cerion5b2325f2005-12-23 00:55:09 +00005861 case 0x277: // lfdux (Load Float Double, Update Indxd, PPC32 p439)
sewardjb183b852006-02-03 16:08:03 +00005862 if (rA_addr == 0)
ceriond953ebb2005-11-29 13:27:20 +00005863 return False;
ceriond953ebb2005-11-29 13:27:20 +00005864 DIP("lfdux fr%u,r%u,r%u\n", frD_addr, rA_addr, rB_addr);
5865 assign( EA, ea_rA_idxd(rA_addr, rB_addr) );
5866 putFReg( frD_addr, loadBE(Ity_F64, mkexpr(EA)) );
5867 putIReg( rA_addr, mkexpr(EA) );
5868 break;
5869
5870 default:
cerion5b2325f2005-12-23 00:55:09 +00005871 vex_printf("dis_fp_load(ppc)(opc2)\n");
ceriond953ebb2005-11-29 13:27:20 +00005872 return False;
sewardje14bb9f2005-07-22 09:39:02 +00005873 }
5874 break;
cerion3d870a32005-03-18 12:23:33 +00005875
5876 default:
cerion5b2325f2005-12-23 00:55:09 +00005877 vex_printf("dis_fp_load(ppc)(opc1)\n");
cerion3d870a32005-03-18 12:23:33 +00005878 return False;
5879 }
5880 return True;
5881}
5882
5883
5884
5885/*
5886 Floating Point Store Instructions
5887*/
5888static Bool dis_fp_store ( UInt theInstr )
5889{
cerion76de5cf2005-11-18 18:25:12 +00005890 /* X-Form, D-Form */
5891 UChar opc1 = ifieldOPC(theInstr);
5892 UChar frS_addr = ifieldRegDS(theInstr);
5893 UChar rA_addr = ifieldRegA(theInstr);
5894 UChar rB_addr = ifieldRegB(theInstr);
5895 UInt opc2 = ifieldOPClo10(theInstr);
5896 UChar b0 = ifieldBIT0(theInstr);
ceriond953ebb2005-11-29 13:27:20 +00005897 Int uimm16 = ifieldUIMM16(theInstr);
cerion094d1392005-06-20 13:45:57 +00005898
cerion2831b002005-11-30 19:55:22 +00005899 Int simm16 = extend_s_16to32(uimm16);
5900 IRTemp frS = newTemp(Ity_F64);
5901 IRType ty = mode64 ? Ity_I64 : Ity_I32;
5902 IRTemp EA = newTemp(ty);
5903 IRTemp rA = newTemp(ty);
5904 IRTemp rB = newTemp(ty);
cerion094d1392005-06-20 13:45:57 +00005905
5906 assign( frS, getFReg(frS_addr) );
cerionedf7fc52005-11-18 20:57:41 +00005907 assign( rA, getIReg(rA_addr) );
5908 assign( rB, getIReg(rB_addr) );
cerion3d870a32005-03-18 12:23:33 +00005909
sewardjb183b852006-02-03 16:08:03 +00005910 /* These are straightforward from a status bits perspective: no
5911 funny status or CR bits affected. For single precision stores,
5912 the values are truncated and denormalised (not rounded) to turn
5913 them into single precision values. */
5914
5915 switch (opc1) {
sewardje14bb9f2005-07-22 09:39:02 +00005916
5917 case 0x34: // stfs (Store Float Single, PPC32 p518)
ceriond953ebb2005-11-29 13:27:20 +00005918 DIP("stfs fr%u,%d(r%u)\n", frS_addr, simm16, rA_addr);
5919 assign( EA, ea_rAor0_simm(rA_addr, simm16) );
sewardjb183b852006-02-03 16:08:03 +00005920 /* Use Iop_TruncF64asF32 to truncate and possible denormalise
5921 the value to be stored in the correct way, without any
5922 rounding. */
sewardje14bb9f2005-07-22 09:39:02 +00005923 storeBE( mkexpr(EA),
sewardjb183b852006-02-03 16:08:03 +00005924 unop(Iop_TruncF64asF32, mkexpr(frS)) );
sewardje14bb9f2005-07-22 09:39:02 +00005925 break;
5926
cerion5b2325f2005-12-23 00:55:09 +00005927 case 0x35: // stfsu (Store Float Single, Update, PPC32 p519)
sewardjb183b852006-02-03 16:08:03 +00005928 if (rA_addr == 0)
cerion729edb72005-12-02 16:03:46 +00005929 return False;
cerion729edb72005-12-02 16:03:46 +00005930 DIP("stfsu fr%u,%d(r%u)\n", frS_addr, simm16, rA_addr);
5931 assign( EA, ea_rA_simm(rA_addr, simm16) );
sewardjb183b852006-02-03 16:08:03 +00005932 /* See comment for stfs */
cerion729edb72005-12-02 16:03:46 +00005933 storeBE( mkexpr(EA),
sewardjb183b852006-02-03 16:08:03 +00005934 unop(Iop_TruncF64asF32, mkexpr(frS)) );
cerion729edb72005-12-02 16:03:46 +00005935 putIReg( rA_addr, mkexpr(EA) );
5936 break;
cerion3d870a32005-03-18 12:23:33 +00005937
cerion094d1392005-06-20 13:45:57 +00005938 case 0x36: // stfd (Store Float Double, PPC32 p513)
ceriond953ebb2005-11-29 13:27:20 +00005939 DIP("stfd fr%u,%d(r%u)\n", frS_addr, simm16, rA_addr);
5940 assign( EA, ea_rAor0_simm(rA_addr, simm16) );
cerion094d1392005-06-20 13:45:57 +00005941 storeBE( mkexpr(EA), mkexpr(frS) );
5942 break;
cerion3d870a32005-03-18 12:23:33 +00005943
cerion5b2325f2005-12-23 00:55:09 +00005944 case 0x37: // stfdu (Store Float Double, Update, PPC32 p514)
sewardjb183b852006-02-03 16:08:03 +00005945 if (rA_addr == 0)
sewardje14bb9f2005-07-22 09:39:02 +00005946 return False;
ceriond953ebb2005-11-29 13:27:20 +00005947 DIP("stfdu fr%u,%d(r%u)\n", frS_addr, simm16, rA_addr);
5948 assign( EA, ea_rA_simm(rA_addr, simm16) );
sewardje14bb9f2005-07-22 09:39:02 +00005949 storeBE( mkexpr(EA), mkexpr(frS) );
5950 putIReg( rA_addr, mkexpr(EA) );
5951 break;
5952
5953 case 0x1F:
5954 if (b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00005955 vex_printf("dis_fp_store(ppc)(instr,b0)\n");
sewardje14bb9f2005-07-22 09:39:02 +00005956 return False;
5957 }
sewardje14bb9f2005-07-22 09:39:02 +00005958 switch(opc2) {
ceriond953ebb2005-11-29 13:27:20 +00005959 case 0x297: // stfsx (Store Float Single Indexed, PPC32 p521)
5960 DIP("stfsx fr%u,r%u,r%u\n", frS_addr, rA_addr, rB_addr);
5961 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
sewardjb183b852006-02-03 16:08:03 +00005962 /* See note for stfs */
5963 storeBE( mkexpr(EA),
5964 unop(Iop_TruncF64asF32, mkexpr(frS)) );
ceriond953ebb2005-11-29 13:27:20 +00005965 break;
5966
cerion5b2325f2005-12-23 00:55:09 +00005967 case 0x2B7: // stfsux (Store Float Sgl, Update Indxd, PPC32 p520)
sewardjb183b852006-02-03 16:08:03 +00005968 if (rA_addr == 0)
cerion729edb72005-12-02 16:03:46 +00005969 return False;
cerion729edb72005-12-02 16:03:46 +00005970 DIP("stfsux fr%u,r%u,r%u\n", frS_addr, rA_addr, rB_addr);
5971 assign( EA, ea_rA_idxd(rA_addr, rB_addr) );
sewardjb183b852006-02-03 16:08:03 +00005972 /* See note for stfs */
5973 storeBE( mkexpr(EA),
5974 unop(Iop_TruncF64asF32, mkexpr(frS)) );
cerion729edb72005-12-02 16:03:46 +00005975 putIReg( rA_addr, mkexpr(EA) );
5976 break;
sewardje14bb9f2005-07-22 09:39:02 +00005977
ceriond953ebb2005-11-29 13:27:20 +00005978 case 0x2D7: // stfdx (Store Float Double Indexed, PPC32 p516)
5979 DIP("stfdx fr%u,r%u,r%u\n", frS_addr, rA_addr, rB_addr);
5980 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
5981 storeBE( mkexpr(EA), mkexpr(frS) );
5982 break;
sewardje14bb9f2005-07-22 09:39:02 +00005983
cerion5b2325f2005-12-23 00:55:09 +00005984 case 0x2F7: // stfdux (Store Float Dbl, Update Indxd, PPC32 p515)
sewardjb183b852006-02-03 16:08:03 +00005985 if (rA_addr == 0)
ceriond953ebb2005-11-29 13:27:20 +00005986 return False;
ceriond953ebb2005-11-29 13:27:20 +00005987 DIP("stfdux fr%u,r%u,r%u\n", frS_addr, rA_addr, rB_addr);
5988 assign( EA, ea_rA_idxd(rA_addr, rB_addr) );
5989 storeBE( mkexpr(EA), mkexpr(frS) );
5990 putIReg( rA_addr, mkexpr(EA) );
5991 break;
sewardj5f63c0c2005-09-09 10:36:55 +00005992
sewardj09e88d12006-01-27 16:05:49 +00005993 case 0x3D7: // stfiwx (Store Float as Int, Indexed, PPC32 p517)
sewardj5117ce12006-01-27 21:20:15 +00005994 // NOTE: POWERPC OPTIONAL, "Graphics Group" (PPC32_GX)
sewardj09e88d12006-01-27 16:05:49 +00005995 DIP("stfiwx fr%u,r%u,r%u\n", frS_addr, rA_addr, rB_addr);
5996 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
5997 storeBE( mkexpr(EA),
5998 unop(Iop_64to32, unop(Iop_ReinterpF64asI64, mkexpr(frS))) );
5999 break;
sewardje14bb9f2005-07-22 09:39:02 +00006000
ceriond953ebb2005-11-29 13:27:20 +00006001 default:
cerion5b2325f2005-12-23 00:55:09 +00006002 vex_printf("dis_fp_store(ppc)(opc2)\n");
ceriond953ebb2005-11-29 13:27:20 +00006003 return False;
sewardje14bb9f2005-07-22 09:39:02 +00006004 }
6005 break;
cerion3d870a32005-03-18 12:23:33 +00006006
6007 default:
cerion5b2325f2005-12-23 00:55:09 +00006008 vex_printf("dis_fp_store(ppc)(opc1)\n");
cerion3d870a32005-03-18 12:23:33 +00006009 return False;
6010 }
6011 return True;
6012}
6013
6014
6015
6016/*
6017 Floating Point Arith Instructions
6018*/
6019static Bool dis_fp_arith ( UInt theInstr )
6020{
6021 /* A-Form */
cerion76de5cf2005-11-18 18:25:12 +00006022 UChar opc1 = ifieldOPC(theInstr);
6023 UChar frD_addr = ifieldRegDS(theInstr);
6024 UChar frA_addr = ifieldRegA(theInstr);
6025 UChar frB_addr = ifieldRegB(theInstr);
6026 UChar frC_addr = ifieldRegC(theInstr);
6027 UChar opc2 = ifieldOPClo5(theInstr);
6028 UChar flag_rC = ifieldBIT0(theInstr);
cerion094d1392005-06-20 13:45:57 +00006029
sewardjb183b852006-02-03 16:08:03 +00006030 IRTemp frD = newTemp(Ity_F64);
6031 IRTemp frA = newTemp(Ity_F64);
6032 IRTemp frB = newTemp(Ity_F64);
6033 IRTemp frC = newTemp(Ity_F64);
6034 IRExpr* rm = get_IR_roundingmode();
6035
6036 /* By default, we will examine the results of the operation and set
6037 fpscr[FPRF] accordingly. */
6038 Bool set_FPRF = True;
6039
6040 /* By default, if flag_RC is set, we will clear cr1 after the
6041 operation. In reality we should set cr1 to indicate the
6042 exception status of the operation, but since we're not
6043 simulating exceptions, the exception status will appear to be
6044 zero. Hence cr1 should be cleared if this is a . form insn. */
6045 Bool clear_CR1 = True;
cerion094d1392005-06-20 13:45:57 +00006046
6047 assign( frA, getFReg(frA_addr));
6048 assign( frB, getFReg(frB_addr));
6049 assign( frC, getFReg(frC_addr));
cerion3d870a32005-03-18 12:23:33 +00006050
6051 switch (opc1) {
sewardje14bb9f2005-07-22 09:39:02 +00006052 case 0x3B:
6053 switch (opc2) {
6054 case 0x12: // fdivs (Floating Divide Single, PPC32 p407)
sewardjb183b852006-02-03 16:08:03 +00006055 if (frC_addr != 0)
sewardje14bb9f2005-07-22 09:39:02 +00006056 return False;
cerion5b2325f2005-12-23 00:55:09 +00006057 DIP("fdivs%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00006058 frD_addr, frA_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00006059 assign( frD, triop( Iop_DivF64r32,
6060 rm, mkexpr(frA), mkexpr(frB) ));
sewardje14bb9f2005-07-22 09:39:02 +00006061 break;
6062
6063 case 0x14: // fsubs (Floating Subtract Single, PPC32 p430)
sewardjb183b852006-02-03 16:08:03 +00006064 if (frC_addr != 0)
sewardje14bb9f2005-07-22 09:39:02 +00006065 return False;
cerion5b2325f2005-12-23 00:55:09 +00006066 DIP("fsubs%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00006067 frD_addr, frA_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00006068 assign( frD, triop( Iop_SubF64r32,
6069 rm, mkexpr(frA), mkexpr(frB) ));
sewardje14bb9f2005-07-22 09:39:02 +00006070 break;
6071
6072 case 0x15: // fadds (Floating Add Single, PPC32 p401)
sewardjb183b852006-02-03 16:08:03 +00006073 if (frC_addr != 0)
sewardje14bb9f2005-07-22 09:39:02 +00006074 return False;
cerion5b2325f2005-12-23 00:55:09 +00006075 DIP("fadds%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00006076 frD_addr, frA_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00006077 assign( frD, triop( Iop_AddF64r32,
6078 rm, mkexpr(frA), mkexpr(frB) ));
sewardje14bb9f2005-07-22 09:39:02 +00006079 break;
6080
sewardj870c84b2006-01-24 03:33:43 +00006081 case 0x16: // fsqrts (Floating SqRt (Single-Precision), PPC32 p428)
sewardj5117ce12006-01-27 21:20:15 +00006082 // NOTE: POWERPC OPTIONAL, "General-Purpose Group" (PPC32_FX)
sewardjb183b852006-02-03 16:08:03 +00006083 if (frA_addr != 0 || frC_addr != 0)
sewardj870c84b2006-01-24 03:33:43 +00006084 return False;
sewardj870c84b2006-01-24 03:33:43 +00006085 DIP("fsqrts%s fr%u,fr%u\n", flag_rC ? ".":"",
6086 frD_addr, frB_addr);
sewardj79fd33f2006-01-29 17:07:57 +00006087 // however illogically, on ppc970 this insn behaves identically
sewardjb183b852006-02-03 16:08:03 +00006088 // to fsqrt (double-precision). So use SqrtF64, not SqrtF64r32.
6089 assign( frD, binop( Iop_SqrtF64, rm, mkexpr(frB) ));
sewardj870c84b2006-01-24 03:33:43 +00006090 break;
sewardje14bb9f2005-07-22 09:39:02 +00006091
sewardjbaf971a2006-01-27 15:09:35 +00006092 case 0x18: // fres (Floating Reciprocal Estimate Single, PPC32 p421)
sewardj5117ce12006-01-27 21:20:15 +00006093 // NOTE: POWERPC OPTIONAL, "Graphics Group" (PPC32_GX)
sewardjb183b852006-02-03 16:08:03 +00006094 if (frA_addr != 0 || frC_addr != 0)
sewardjbaf971a2006-01-27 15:09:35 +00006095 return False;
sewardjbaf971a2006-01-27 15:09:35 +00006096 DIP("fres%s fr%u,fr%u\n", flag_rC ? ".":"",
6097 frD_addr, frB_addr);
sewardj157b19b2006-01-31 16:32:25 +00006098 { IRExpr* ieee_one
6099 = IRExpr_Const(IRConst_F64i(0x3ff0000000000000ULL));
sewardjb183b852006-02-03 16:08:03 +00006100 assign( frD, triop( Iop_DivF64r32,
6101 rm,
6102 ieee_one, mkexpr(frB) ));
sewardj157b19b2006-01-31 16:32:25 +00006103 }
sewardjbaf971a2006-01-27 15:09:35 +00006104 break;
sewardje14bb9f2005-07-22 09:39:02 +00006105
6106 case 0x19: // fmuls (Floating Multiply Single, PPC32 p414)
sewardjb183b852006-02-03 16:08:03 +00006107 if (frB_addr != 0)
sewardje14bb9f2005-07-22 09:39:02 +00006108 return False;
cerion5b2325f2005-12-23 00:55:09 +00006109 DIP("fmuls%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00006110 frD_addr, frA_addr, frC_addr);
sewardjb183b852006-02-03 16:08:03 +00006111 assign( frD, triop( Iop_MulF64r32,
6112 rm, mkexpr(frA), mkexpr(frC) ));
sewardje14bb9f2005-07-22 09:39:02 +00006113 break;
6114
sewardj79fd33f2006-01-29 17:07:57 +00006115 case 0x1A: // frsqrtes (Floating Recip SqRt Est Single)
6116 // NOTE: POWERPC OPTIONAL, "Graphics Group" (PPC32_GX)
6117 // Undocumented instruction?
sewardjb183b852006-02-03 16:08:03 +00006118 if (frA_addr != 0 || frC_addr != 0)
sewardj79fd33f2006-01-29 17:07:57 +00006119 return False;
sewardj79fd33f2006-01-29 17:07:57 +00006120 DIP("frsqrtes%s fr%u,fr%u\n", flag_rC ? ".":"",
6121 frD_addr, frB_addr);
6122 assign( frD, unop(Iop_Est5FRSqrt, mkexpr(frB)) );
6123 break;
6124
sewardje14bb9f2005-07-22 09:39:02 +00006125 default:
cerion5b2325f2005-12-23 00:55:09 +00006126 vex_printf("dis_fp_arith(ppc)(3B: opc2)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006127 return False;
6128 }
6129 break;
cerion094d1392005-06-20 13:45:57 +00006130
cerion3d870a32005-03-18 12:23:33 +00006131 case 0x3F:
6132 switch (opc2) {
cerion5b2325f2005-12-23 00:55:09 +00006133 case 0x12: // fdiv (Floating Div (Double-Precision), PPC32 p406)
sewardjb183b852006-02-03 16:08:03 +00006134 if (frC_addr != 0)
sewardje14bb9f2005-07-22 09:39:02 +00006135 return False;
cerion5b2325f2005-12-23 00:55:09 +00006136 DIP("fdiv%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00006137 frD_addr, frA_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00006138 assign( frD, triop(Iop_DivF64, rm, mkexpr(frA), mkexpr(frB)) );
sewardje14bb9f2005-07-22 09:39:02 +00006139 break;
6140
cerion5b2325f2005-12-23 00:55:09 +00006141 case 0x14: // fsub (Floating Sub (Double-Precision), PPC32 p429)
sewardjb183b852006-02-03 16:08:03 +00006142 if (frC_addr != 0)
sewardje14bb9f2005-07-22 09:39:02 +00006143 return False;
cerion5b2325f2005-12-23 00:55:09 +00006144 DIP("fsub%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00006145 frD_addr, frA_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00006146 assign( frD, triop(Iop_SubF64, rm, mkexpr(frA), mkexpr(frB)) );
sewardje14bb9f2005-07-22 09:39:02 +00006147 break;
cerion3d870a32005-03-18 12:23:33 +00006148
6149 case 0x15: // fadd (Floating Add (Double-Precision), PPC32 p400)
sewardjb183b852006-02-03 16:08:03 +00006150 if (frC_addr != 0)
cerion3d870a32005-03-18 12:23:33 +00006151 return False;
cerion5b2325f2005-12-23 00:55:09 +00006152 DIP("fadd%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
cerion3d870a32005-03-18 12:23:33 +00006153 frD_addr, frA_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00006154 assign( frD, triop(Iop_AddF64, rm, mkexpr(frA), mkexpr(frB)) );
cerion094d1392005-06-20 13:45:57 +00006155 break;
cerion3d870a32005-03-18 12:23:33 +00006156
cerion876ef412005-12-14 22:00:53 +00006157 case 0x16: // fsqrt (Floating SqRt (Double-Precision), PPC32 p427)
sewardj5117ce12006-01-27 21:20:15 +00006158 // NOTE: POWERPC OPTIONAL, "General-Purpose Group" (PPC32_FX)
sewardjb183b852006-02-03 16:08:03 +00006159 if (frA_addr != 0 || frC_addr != 0)
cerion876ef412005-12-14 22:00:53 +00006160 return False;
cerion5b2325f2005-12-23 00:55:09 +00006161 DIP("fsqrt%s fr%u,fr%u\n", flag_rC ? ".":"",
cerion876ef412005-12-14 22:00:53 +00006162 frD_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00006163 assign( frD, binop(Iop_SqrtF64, rm, mkexpr(frB)) );
cerion876ef412005-12-14 22:00:53 +00006164 break;
sewardje14bb9f2005-07-22 09:39:02 +00006165
6166 case 0x17: { // fsel (Floating Select, PPC32 p426)
sewardj5117ce12006-01-27 21:20:15 +00006167 // NOTE: POWERPC OPTIONAL, "Graphics Group" (PPC32_GX)
sewardje14bb9f2005-07-22 09:39:02 +00006168 IRTemp cc = newTemp(Ity_I32);
6169 IRTemp cc_b0 = newTemp(Ity_I32);
6170
cerion5b2325f2005-12-23 00:55:09 +00006171 DIP("fsel%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00006172 frD_addr, frA_addr, frC_addr, frB_addr);
6173
6174 // cc: UN == 0x41, LT == 0x01, GT == 0x00, EQ == 0x40
6175 // => GT|EQ == (cc & 0x1 == 0)
cerion5b2325f2005-12-23 00:55:09 +00006176 assign( cc, binop(Iop_CmpF64, mkexpr(frA),
6177 IRExpr_Const(IRConst_F64(0))) );
sewardje14bb9f2005-07-22 09:39:02 +00006178 assign( cc_b0, binop(Iop_And32, mkexpr(cc), mkU32(1)) );
6179
6180 // frD = (frA >= 0.0) ? frC : frB
6181 // = (cc_b0 == 0) ? frC : frB
6182 assign( frD,
6183 IRExpr_Mux0X(
6184 unop(Iop_1Uto8,
6185 binop(Iop_CmpEQ32, mkexpr(cc_b0), mkU32(0))),
6186 mkexpr(frB),
6187 mkexpr(frC) ));
sewardjb183b852006-02-03 16:08:03 +00006188
6189 /* One of the rare ones which don't mess with FPRF */
6190 set_FPRF = False;
sewardje14bb9f2005-07-22 09:39:02 +00006191 break;
6192 }
6193
sewardj79fd33f2006-01-29 17:07:57 +00006194 case 0x18: // fre (Floating Reciprocal Estimate)
6195 // NOTE: POWERPC OPTIONAL, "Graphics Group" (PPC32_GX)
6196 // Note: unclear whether this insn really exists or not
6197 // ppc970 doesn't have it, but POWER5 does
sewardjb183b852006-02-03 16:08:03 +00006198 if (frA_addr != 0 || frC_addr != 0)
sewardj79fd33f2006-01-29 17:07:57 +00006199 return False;
sewardj79fd33f2006-01-29 17:07:57 +00006200 DIP("fre%s fr%u,fr%u\n", flag_rC ? ".":"",
6201 frD_addr, frB_addr);
sewardj157b19b2006-01-31 16:32:25 +00006202 { IRExpr* ieee_one
6203 = IRExpr_Const(IRConst_F64i(0x3ff0000000000000ULL));
sewardjb183b852006-02-03 16:08:03 +00006204 assign( frD, triop( Iop_DivF64,
sewardj56de4212006-02-06 22:19:17 +00006205 rm,
sewardjb183b852006-02-03 16:08:03 +00006206 ieee_one, mkexpr(frB) ));
sewardj157b19b2006-01-31 16:32:25 +00006207 }
sewardj79fd33f2006-01-29 17:07:57 +00006208 break;
6209
cerion5b2325f2005-12-23 00:55:09 +00006210 case 0x19: // fmul (Floating Mult (Double Precision), PPC32 p413)
sewardjb183b852006-02-03 16:08:03 +00006211 if (frB_addr != 0)
cerion5b2325f2005-12-23 00:55:09 +00006212 vex_printf("dis_fp_arith(ppc)(instr,fmul)\n");
cerion5b2325f2005-12-23 00:55:09 +00006213 DIP("fmul%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00006214 frD_addr, frA_addr, frC_addr);
sewardjb183b852006-02-03 16:08:03 +00006215 assign( frD, triop(Iop_MulF64, rm, mkexpr(frA), mkexpr(frC)) );
sewardje14bb9f2005-07-22 09:39:02 +00006216 break;
6217
sewardjbaf971a2006-01-27 15:09:35 +00006218 case 0x1A: // frsqrte (Floating Recip SqRt Est., PPC32 p424)
sewardj5117ce12006-01-27 21:20:15 +00006219 // NOTE: POWERPC OPTIONAL, "Graphics Group" (PPC32_GX)
sewardjb183b852006-02-03 16:08:03 +00006220 if (frA_addr != 0 || frC_addr != 0)
sewardjbaf971a2006-01-27 15:09:35 +00006221 return False;
sewardjbaf971a2006-01-27 15:09:35 +00006222 DIP("frsqrte%s fr%u,fr%u\n", flag_rC ? ".":"",
6223 frD_addr, frB_addr);
6224 assign( frD, unop(Iop_Est5FRSqrt, mkexpr(frB)) );
6225 break;
cerion3d870a32005-03-18 12:23:33 +00006226
6227 default:
cerion5b2325f2005-12-23 00:55:09 +00006228 vex_printf("dis_fp_arith(ppc)(3F: opc2)\n");
cerion3d870a32005-03-18 12:23:33 +00006229 return False;
6230 }
cerion094d1392005-06-20 13:45:57 +00006231 break;
6232
cerion3d870a32005-03-18 12:23:33 +00006233 default:
cerion5b2325f2005-12-23 00:55:09 +00006234 vex_printf("dis_fp_arith(ppc)(opc1)\n");
cerion3d870a32005-03-18 12:23:33 +00006235 return False;
6236 }
cerion094d1392005-06-20 13:45:57 +00006237
6238 putFReg( frD_addr, mkexpr(frD) );
sewardjb183b852006-02-03 16:08:03 +00006239
6240 if (set_FPRF) {
6241 // XXX XXX XXX FIXME
6242 // set FPRF from frD
6243 }
6244
6245 if (flag_rC && clear_CR1) {
6246 putCR321( 1, mkU8(0) );
6247 putCR0( 1, mkU8(0) );
6248 }
6249
cerion3d870a32005-03-18 12:23:33 +00006250 return True;
6251}
6252
6253
6254
sewardje14bb9f2005-07-22 09:39:02 +00006255/*
6256 Floating Point Mult-Add Instructions
6257*/
6258static Bool dis_fp_multadd ( UInt theInstr )
6259{
6260 /* A-Form */
cerion76de5cf2005-11-18 18:25:12 +00006261 UChar opc1 = ifieldOPC(theInstr);
6262 UChar frD_addr = ifieldRegDS(theInstr);
6263 UChar frA_addr = ifieldRegA(theInstr);
6264 UChar frB_addr = ifieldRegB(theInstr);
6265 UChar frC_addr = ifieldRegC(theInstr);
6266 UChar opc2 = ifieldOPClo5(theInstr);
6267 UChar flag_rC = ifieldBIT0(theInstr);
sewardje14bb9f2005-07-22 09:39:02 +00006268
sewardjb183b852006-02-03 16:08:03 +00006269 IRTemp frD = newTemp(Ity_F64);
6270 IRTemp frA = newTemp(Ity_F64);
6271 IRTemp frB = newTemp(Ity_F64);
6272 IRTemp frC = newTemp(Ity_F64);
6273 IRTemp rmt = newTemp(Ity_I32);
6274 IRExpr* rm;
6275
6276 /* By default, we will examine the results of the operation and set
6277 fpscr[FPRF] accordingly. */
6278 Bool set_FPRF = True;
6279
6280 /* By default, if flag_RC is set, we will clear cr1 after the
6281 operation. In reality we should set cr1 to indicate the
6282 exception status of the operation, but since we're not
6283 simulating exceptions, the exception status will appear to be
6284 zero. Hence cr1 should be cleared if this is a . form insn. */
6285 Bool clear_CR1 = True;
6286
6287 /* Bind the rounding mode expression to a temp; there's no
6288 point in creating gratuitous CSEs, as we know we'll need
6289 to use it twice. */
6290 assign( rmt, get_IR_roundingmode() );
6291 rm = mkexpr(rmt);
sewardje14bb9f2005-07-22 09:39:02 +00006292
6293 assign( frA, getFReg(frA_addr));
6294 assign( frB, getFReg(frB_addr));
6295 assign( frC, getFReg(frC_addr));
6296
sewardjb183b852006-02-03 16:08:03 +00006297 /* The rounding in this is all a bit dodgy. The idea is to only do
6298 one rounding. That clearly isn't achieveable without dedicated
6299 four-input IR primops, although in the single precision case we
6300 can sort-of simulate it by doing the inner multiply in double
6301 precision.
6302
6303 In the negated cases, the negation happens after rounding. */
6304
sewardje14bb9f2005-07-22 09:39:02 +00006305 switch (opc1) {
6306 case 0x3B:
6307 switch (opc2) {
6308 case 0x1C: // fmsubs (Floating Mult-Subtr Single, PPC32 p412)
cerion5b2325f2005-12-23 00:55:09 +00006309 DIP("fmsubs%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00006310 frD_addr, frA_addr, frC_addr, frB_addr);
sewardj40c80262006-02-08 19:30:46 +00006311 assign( frD, qop( Iop_MSubF64r32, rm,
6312 mkexpr(frA), mkexpr(frC), mkexpr(frB) ));
sewardjb183b852006-02-03 16:08:03 +00006313 break;
sewardje14bb9f2005-07-22 09:39:02 +00006314
6315 case 0x1D: // fmadds (Floating Mult-Add Single, PPC32 p409)
cerion5b2325f2005-12-23 00:55:09 +00006316 DIP("fmadds%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00006317 frD_addr, frA_addr, frC_addr, frB_addr);
sewardj40c80262006-02-08 19:30:46 +00006318 assign( frD, qop( Iop_MAddF64r32, rm,
6319 mkexpr(frA), mkexpr(frC), mkexpr(frB) ));
sewardje14bb9f2005-07-22 09:39:02 +00006320 break;
6321
6322 case 0x1E: // fnmsubs (Float Neg Mult-Subtr Single, PPC32 p420)
cerion5b2325f2005-12-23 00:55:09 +00006323 DIP("fnmsubs%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00006324 frD_addr, frA_addr, frC_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00006325 assign( frD, unop( Iop_NegF64,
sewardj40c80262006-02-08 19:30:46 +00006326 qop( Iop_MSubF64r32, rm,
6327 mkexpr(frA), mkexpr(frC), mkexpr(frB) )));
sewardje14bb9f2005-07-22 09:39:02 +00006328 break;
6329
6330 case 0x1F: // fnmadds (Floating Negative Multiply-Add Single, PPC32 p418)
cerion5b2325f2005-12-23 00:55:09 +00006331 DIP("fnmadds%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00006332 frD_addr, frA_addr, frC_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00006333 assign( frD, unop( Iop_NegF64,
sewardj40c80262006-02-08 19:30:46 +00006334 qop( Iop_MAddF64r32, rm,
6335 mkexpr(frA), mkexpr(frC), mkexpr(frB) )));
sewardje14bb9f2005-07-22 09:39:02 +00006336 break;
6337
6338 default:
cerion5b2325f2005-12-23 00:55:09 +00006339 vex_printf("dis_fp_multadd(ppc)(3B: opc2)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006340 return False;
6341 }
6342 break;
6343
6344 case 0x3F:
6345 switch (opc2) {
cerion5b2325f2005-12-23 00:55:09 +00006346 case 0x1C: // fmsub (Float Mult-Sub (Dbl Precision), PPC32 p411)
6347 DIP("fmsub%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00006348 frD_addr, frA_addr, frC_addr, frB_addr);
sewardj40c80262006-02-08 19:30:46 +00006349 assign( frD, qop( Iop_MSubF64, rm,
6350 mkexpr(frA), mkexpr(frC), mkexpr(frB) ));
sewardje14bb9f2005-07-22 09:39:02 +00006351 break;
6352
cerion5b2325f2005-12-23 00:55:09 +00006353 case 0x1D: // fmadd (Float Mult-Add (Dbl Precision), PPC32 p408)
6354 DIP("fmadd%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00006355 frD_addr, frA_addr, frC_addr, frB_addr);
sewardj40c80262006-02-08 19:30:46 +00006356 assign( frD, qop( Iop_MAddF64, rm,
6357 mkexpr(frA), mkexpr(frC), mkexpr(frB) ));
sewardje14bb9f2005-07-22 09:39:02 +00006358 break;
6359
cerion5b2325f2005-12-23 00:55:09 +00006360 case 0x1E: // fnmsub (Float Neg Mult-Subtr (Dbl Precision), PPC32 p419)
6361 DIP("fnmsub%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardj0e2cc672005-07-29 21:58:51 +00006362 frD_addr, frA_addr, frC_addr, frB_addr);
6363 assign( frD, unop( Iop_NegF64,
sewardj40c80262006-02-08 19:30:46 +00006364 qop( Iop_MSubF64, rm,
6365 mkexpr(frA), mkexpr(frC), mkexpr(frB) )));
sewardj0e2cc672005-07-29 21:58:51 +00006366 break;
6367
cerion5b2325f2005-12-23 00:55:09 +00006368 case 0x1F: // fnmadd (Float Neg Mult-Add (Dbl Precision), PPC32 p417)
6369 DIP("fnmadd%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardj0e2cc672005-07-29 21:58:51 +00006370 frD_addr, frA_addr, frC_addr, frB_addr);
6371 assign( frD, unop( Iop_NegF64,
sewardj40c80262006-02-08 19:30:46 +00006372 qop( Iop_MAddF64, rm,
6373 mkexpr(frA), mkexpr(frC), mkexpr(frB) )));
sewardj0e2cc672005-07-29 21:58:51 +00006374 break;
sewardje14bb9f2005-07-22 09:39:02 +00006375
6376 default:
cerion5b2325f2005-12-23 00:55:09 +00006377 vex_printf("dis_fp_multadd(ppc)(3F: opc2)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006378 return False;
6379 }
6380 break;
6381
6382 default:
cerion5b2325f2005-12-23 00:55:09 +00006383 vex_printf("dis_fp_multadd(ppc)(opc1)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006384 return False;
6385 }
6386
6387 putFReg( frD_addr, mkexpr(frD) );
sewardjb183b852006-02-03 16:08:03 +00006388
6389 if (set_FPRF) {
6390 // XXX XXX XXX FIXME
6391 // set FPRF from frD
6392 }
6393
6394 if (flag_rC && clear_CR1) {
6395 putCR321( 1, mkU8(0) );
6396 putCR0( 1, mkU8(0) );
6397 }
6398
sewardje14bb9f2005-07-22 09:39:02 +00006399 return True;
6400}
6401
6402
6403
6404/*
6405 Floating Point Compare Instructions
6406*/
6407static Bool dis_fp_cmp ( UInt theInstr )
6408{
6409 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00006410 UChar opc1 = ifieldOPC(theInstr);
6411 UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) );
6412 UChar b21to22 = toUChar( IFIELD( theInstr, 21, 2 ) );
6413 UChar frA_addr = ifieldRegA(theInstr);
6414 UChar frB_addr = ifieldRegB(theInstr);
6415 UInt opc2 = ifieldOPClo10(theInstr);
6416 UChar b0 = ifieldBIT0(theInstr);
sewardje14bb9f2005-07-22 09:39:02 +00006417
6418 IRTemp ccIR = newTemp(Ity_I32);
6419 IRTemp ccPPC32 = newTemp(Ity_I32);
6420
sewardje14bb9f2005-07-22 09:39:02 +00006421 IRTemp frA = newTemp(Ity_F64);
6422 IRTemp frB = newTemp(Ity_F64);
sewardje14bb9f2005-07-22 09:39:02 +00006423
6424 if (opc1 != 0x3F || b21to22 != 0 || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006425 vex_printf("dis_fp_cmp(ppc)(instr)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006426 return False;
6427 }
6428
6429 assign( frA, getFReg(frA_addr));
6430 assign( frB, getFReg(frB_addr));
6431
6432 assign( ccIR, binop(Iop_CmpF64, mkexpr(frA), mkexpr(frB)) );
6433
6434 /* Map compare result from IR to PPC32 */
6435 /*
6436 FP cmp result | PPC | IR
6437 --------------------------
6438 UN | 0x1 | 0x45
6439 EQ | 0x2 | 0x40
6440 GT | 0x4 | 0x00
6441 LT | 0x8 | 0x01
6442 */
6443
sewardjb183b852006-02-03 16:08:03 +00006444 // ccPPC32 = Shl(1, (~(ccIR>>5) & 2)
6445 // | ((ccIR ^ (ccIR>>6)) & 1)
sewardje14bb9f2005-07-22 09:39:02 +00006446 assign(
6447 ccPPC32,
sewardjb183b852006-02-03 16:08:03 +00006448 binop(
6449 Iop_Shl32,
6450 mkU32(1),
6451 unop(
6452 Iop_32to8,
6453 binop(
6454 Iop_Or32,
6455 binop(
6456 Iop_And32,
6457 unop(
6458 Iop_Not32,
6459 binop(Iop_Shr32, mkexpr(ccIR), mkU8(5))
6460 ),
6461 mkU32(2)
6462 ),
6463 binop(
6464 Iop_And32,
6465 binop(
6466 Iop_Xor32,
6467 mkexpr(ccIR),
6468 binop(Iop_Shr32, mkexpr(ccIR), mkU8(6))
6469 ),
6470 mkU32(1)
6471 )
6472 )
6473 )
6474 )
sewardje14bb9f2005-07-22 09:39:02 +00006475 );
6476
ceriond953ebb2005-11-29 13:27:20 +00006477 putGST_field( PPC_GST_CR, mkexpr(ccPPC32), crfD );
sewardje14bb9f2005-07-22 09:39:02 +00006478
cerionedf7fc52005-11-18 20:57:41 +00006479 /* CAB: TODO?: Support writing cc to FPSCR->FPCC ?
ceriond953ebb2005-11-29 13:27:20 +00006480 putGST_field( PPC_GST_FPSCR, mkexpr(ccPPC32), 4 );
cerionedf7fc52005-11-18 20:57:41 +00006481 */
sewardjb183b852006-02-03 16:08:03 +00006482 // XXX XXX XXX FIXME
6483 // Also write the result into FPRF (it's not entirely clear how)
sewardje14bb9f2005-07-22 09:39:02 +00006484
cerionedf7fc52005-11-18 20:57:41 +00006485 /* Note: Differences between fcmpu and fcmpo are only in exception
6486 flag settings, which aren't supported anyway. */
sewardje14bb9f2005-07-22 09:39:02 +00006487 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00006488 case 0x000: // fcmpu (Floating Compare Unordered, PPC32 p403)
6489 DIP("fcmpu crf%d,fr%u,fr%u\n", crfD, frA_addr, frB_addr);
6490 break;
6491 case 0x020: // fcmpo (Floating Compare Ordered, PPC32 p402)
6492 DIP("fcmpo crf%d,fr%u,fr%u\n", crfD, frA_addr, frB_addr);
6493 break;
6494 default:
cerion5b2325f2005-12-23 00:55:09 +00006495 vex_printf("dis_fp_cmp(ppc)(opc2)\n");
ceriond953ebb2005-11-29 13:27:20 +00006496 return False;
sewardje14bb9f2005-07-22 09:39:02 +00006497 }
6498 return True;
6499}
6500
6501
6502
6503/*
6504 Floating Point Rounding/Conversion Instructions
6505*/
6506static Bool dis_fp_round ( UInt theInstr )
6507{
6508 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00006509 UChar opc1 = ifieldOPC(theInstr);
6510 UChar frD_addr = ifieldRegDS(theInstr);
6511 UChar b16to20 = ifieldRegA(theInstr);
6512 UChar frB_addr = ifieldRegB(theInstr);
6513 UInt opc2 = ifieldOPClo10(theInstr);
6514 UChar flag_rC = ifieldBIT0(theInstr);
sewardje14bb9f2005-07-22 09:39:02 +00006515
sewardjb183b852006-02-03 16:08:03 +00006516 IRTemp frD = newTemp(Ity_F64);
6517 IRTemp frB = newTemp(Ity_F64);
6518 IRTemp r_tmp32 = newTemp(Ity_I32);
6519 IRTemp r_tmp64 = newTemp(Ity_I64);
6520 IRExpr* rm = get_IR_roundingmode();
sewardje14bb9f2005-07-22 09:39:02 +00006521
sewardjb183b852006-02-03 16:08:03 +00006522 /* By default, we will examine the results of the operation and set
6523 fpscr[FPRF] accordingly. */
6524 Bool set_FPRF = True;
6525
6526 /* By default, if flag_RC is set, we will clear cr1 after the
6527 operation. In reality we should set cr1 to indicate the
6528 exception status of the operation, but since we're not
6529 simulating exceptions, the exception status will appear to be
6530 zero. Hence cr1 should be cleared if this is a . form insn. */
6531 Bool clear_CR1 = True;
6532
sewardje14bb9f2005-07-22 09:39:02 +00006533 if (opc1 != 0x3F || b16to20 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006534 vex_printf("dis_fp_round(ppc)(instr)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006535 return False;
6536 }
6537
6538 assign( frB, getFReg(frB_addr));
6539
sewardje14bb9f2005-07-22 09:39:02 +00006540 switch (opc2) {
cerionf0de28c2005-12-13 20:21:11 +00006541 case 0x00C: // frsp (Float Round to Single, PPC32 p423)
cerion5b2325f2005-12-23 00:55:09 +00006542 DIP("frsp%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00006543 assign( frD, binop( Iop_RoundF64toF32, rm, mkexpr(frB) ));
ceriond953ebb2005-11-29 13:27:20 +00006544 break;
6545
cerionf0de28c2005-12-13 20:21:11 +00006546 case 0x00E: // fctiw (Float Conv to Int, PPC32 p404)
cerion5b2325f2005-12-23 00:55:09 +00006547 DIP("fctiw%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
6548 assign( r_tmp32,
sewardjb183b852006-02-03 16:08:03 +00006549 binop(Iop_F64toI32, rm, mkexpr(frB)) );
ceriond953ebb2005-11-29 13:27:20 +00006550 assign( frD, unop( Iop_ReinterpI64asF64,
cerion07b07a92005-12-22 14:32:35 +00006551 unop( Iop_32Uto64, mkexpr(r_tmp32))));
sewardjb183b852006-02-03 16:08:03 +00006552 /* FPRF is undefined after fctiw. Leave unchanged. */
6553 set_FPRF = False;
ceriond953ebb2005-11-29 13:27:20 +00006554 break;
6555
cerionf0de28c2005-12-13 20:21:11 +00006556 case 0x00F: // fctiwz (Float Conv to Int, Round to Zero, PPC32 p405)
cerion5b2325f2005-12-23 00:55:09 +00006557 DIP("fctiwz%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00006558 assign( r_tmp32,
6559 binop(Iop_F64toI32, mkU32(Irrm_ZERO), mkexpr(frB) ));
ceriond953ebb2005-11-29 13:27:20 +00006560 assign( frD, unop( Iop_ReinterpI64asF64,
cerion07b07a92005-12-22 14:32:35 +00006561 unop( Iop_32Uto64, mkexpr(r_tmp32))));
sewardjb183b852006-02-03 16:08:03 +00006562 /* FPRF is undefined after fctiwz. Leave unchanged. */
6563 set_FPRF = False;
ceriond953ebb2005-11-29 13:27:20 +00006564 break;
cerionf0de28c2005-12-13 20:21:11 +00006565
cerion5b2325f2005-12-23 00:55:09 +00006566 case 0x32E: // fctid (Float Conv to Int DWord, PPC64 p437)
6567 DIP("fctid%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
6568 assign( r_tmp64,
sewardjb183b852006-02-03 16:08:03 +00006569 binop(Iop_F64toI64, rm, mkexpr(frB)) );
cerion07b07a92005-12-22 14:32:35 +00006570 assign( frD, unop( Iop_ReinterpI64asF64, mkexpr(r_tmp64)) );
sewardjb183b852006-02-03 16:08:03 +00006571 /* FPRF is undefined after fctid. Leave unchanged. */
6572 set_FPRF = False;
cerion07b07a92005-12-22 14:32:35 +00006573 break;
cerionf0de28c2005-12-13 20:21:11 +00006574
cerion5b2325f2005-12-23 00:55:09 +00006575 case 0x32F: // fctidz (Float Conv to Int DWord, Round to Zero, PPC64 p437)
6576 DIP("fctidz%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00006577 assign( r_tmp64,
6578 binop(Iop_F64toI64, mkU32(Irrm_ZERO), mkexpr(frB)) );
cerion07b07a92005-12-22 14:32:35 +00006579 assign( frD, unop( Iop_ReinterpI64asF64, mkexpr(r_tmp64)) );
sewardjb183b852006-02-03 16:08:03 +00006580 /* FPRF is undefined after fctidz. Leave unchanged. */
6581 set_FPRF = False;
cerion07b07a92005-12-22 14:32:35 +00006582 break;
cerionf0de28c2005-12-13 20:21:11 +00006583
cerion5b2325f2005-12-23 00:55:09 +00006584 case 0x34E: // fcfid (Float Conv from Int DWord, PPC64 p434)
6585 DIP("fcfid%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
cerion07b07a92005-12-22 14:32:35 +00006586 assign( r_tmp64, unop( Iop_ReinterpF64asI64, mkexpr(frB)) );
sewardjb183b852006-02-03 16:08:03 +00006587 assign( frD,
6588 binop(Iop_I64toF64, rm, mkexpr(r_tmp64)) );
cerion07b07a92005-12-22 14:32:35 +00006589 break;
cerionf0de28c2005-12-13 20:21:11 +00006590
ceriond953ebb2005-11-29 13:27:20 +00006591 default:
cerion5b2325f2005-12-23 00:55:09 +00006592 vex_printf("dis_fp_round(ppc)(opc2)\n");
ceriond953ebb2005-11-29 13:27:20 +00006593 return False;
sewardje14bb9f2005-07-22 09:39:02 +00006594 }
6595
6596 putFReg( frD_addr, mkexpr(frD) );
sewardjb183b852006-02-03 16:08:03 +00006597
6598 if (set_FPRF) {
6599 // XXX XXX XXX FIXME
6600 // set FPRF from frD
6601 }
6602
6603 if (flag_rC && clear_CR1) {
6604 putCR321( 1, mkU8(0) );
6605 putCR0( 1, mkU8(0) );
6606 }
6607
sewardje14bb9f2005-07-22 09:39:02 +00006608 return True;
6609}
6610
6611
6612
6613/*
6614 Floating Point Move Instructions
6615*/
6616static Bool dis_fp_move ( UInt theInstr )
6617{
6618 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00006619 UChar opc1 = ifieldOPC(theInstr);
6620 UChar frD_addr = ifieldRegDS(theInstr);
6621 UChar b16to20 = ifieldRegA(theInstr);
6622 UChar frB_addr = ifieldRegB(theInstr);
6623 UInt opc2 = ifieldOPClo10(theInstr);
6624 UChar flag_rC = ifieldBIT0(theInstr);
sewardje14bb9f2005-07-22 09:39:02 +00006625
6626 IRTemp frD = newTemp(Ity_F64);
6627 IRTemp frB = newTemp(Ity_F64);
6628
6629 if (opc1 != 0x3F || b16to20 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006630 vex_printf("dis_fp_move(ppc)(instr)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006631 return False;
6632 }
6633
6634 assign( frB, getFReg(frB_addr));
6635
sewardje14bb9f2005-07-22 09:39:02 +00006636 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00006637 case 0x028: // fneg (Floating Negate, PPC32 p416)
cerion5b2325f2005-12-23 00:55:09 +00006638 DIP("fneg%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
ceriond953ebb2005-11-29 13:27:20 +00006639 assign( frD, unop( Iop_NegF64, mkexpr(frB) ));
6640 break;
6641
6642 case 0x048: // fmr (Floating Move Register, PPC32 p410)
cerion5b2325f2005-12-23 00:55:09 +00006643 DIP("fmr%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
ceriond953ebb2005-11-29 13:27:20 +00006644 assign( frD, mkexpr(frB) );
6645 break;
6646
6647 case 0x088: // fnabs (Floating Negative Absolute Value, PPC32 p415)
cerion5b2325f2005-12-23 00:55:09 +00006648 DIP("fnabs%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
ceriond953ebb2005-11-29 13:27:20 +00006649 assign( frD, unop( Iop_NegF64, unop( Iop_AbsF64, mkexpr(frB) )));
6650 break;
6651
6652 case 0x108: // fabs (Floating Absolute Value, PPC32 p399)
cerion5b2325f2005-12-23 00:55:09 +00006653 DIP("fabs%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
ceriond953ebb2005-11-29 13:27:20 +00006654 assign( frD, unop( Iop_AbsF64, mkexpr(frB) ));
6655 break;
6656
6657 default:
cerion5b2325f2005-12-23 00:55:09 +00006658 vex_printf("dis_fp_move(ppc)(opc2)\n");
ceriond953ebb2005-11-29 13:27:20 +00006659 return False;
sewardje14bb9f2005-07-22 09:39:02 +00006660 }
6661
6662 putFReg( frD_addr, mkexpr(frD) );
sewardjb183b852006-02-03 16:08:03 +00006663
6664 /* None of these change FPRF. cr1 is set in the usual way though,
6665 if flag_rC is set. */
6666
6667 if (flag_rC) {
6668 putCR321( 1, mkU8(0) );
6669 putCR0( 1, mkU8(0) );
6670 }
6671
sewardje14bb9f2005-07-22 09:39:02 +00006672 return True;
6673}
6674
6675
6676
6677/*
6678 Floating Point Status/Control Register Instructions
6679*/
6680static Bool dis_fp_scr ( UInt theInstr )
6681{
cerion76de5cf2005-11-18 18:25:12 +00006682 /* Many forms - see each switch case */
6683 UChar opc1 = ifieldOPC(theInstr);
6684 UInt opc2 = ifieldOPClo10(theInstr);
6685 UChar flag_rC = ifieldBIT0(theInstr);
sewardje14bb9f2005-07-22 09:39:02 +00006686
6687 if (opc1 != 0x3F) {
cerion5b2325f2005-12-23 00:55:09 +00006688 vex_printf("dis_fp_scr(ppc)(instr)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006689 return False;
6690 }
6691
6692 switch (opc2) {
cerion3ea49ee2006-01-04 10:53:00 +00006693 case 0x026: { // mtfsb1 (Move to FPSCR Bit 1, PPC32 p479)
6694 // Bit crbD of the FPSCR is set.
6695 UChar crbD = ifieldRegDS(theInstr);
6696 UInt b11to20 = IFIELD(theInstr, 11, 10);
6697
6698 if (b11to20 != 0) {
6699 vex_printf("dis_fp_scr(ppc)(instr,mtfsb1)\n");
6700 return False;
6701 }
6702 DIP("mtfsb1%s crb%d \n", flag_rC ? ".":"", crbD);
6703 putGST_masked( PPC_GST_FPSCR, mkU32(1<<(31-crbD)), 1<<(31-crbD) );
6704 break;
6705 }
6706
sewardj496b88f2006-10-04 17:46:11 +00006707 case 0x040: { // mcrfs (Move to Condition Register from FPSCR, PPC32 p465)
6708 UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) );
6709 UChar b21to22 = toUChar( IFIELD( theInstr, 21, 2 ) );
6710 UChar crfS = toUChar( IFIELD( theInstr, 18, 3 ) );
6711 UChar b11to17 = toUChar( IFIELD( theInstr, 11, 7 ) );
6712 IRTemp tmp = newTemp(Ity_I32);
6713 IRExpr* fpscr_all;
6714 if (b21to22 != 0 || b11to17 != 0 || flag_rC != 0) {
6715 vex_printf("dis_fp_scr(ppc)(instr,mcrfs)\n");
6716 return False;
6717 }
6718 DIP("mcrfs crf%d,crf%d\n", crfD, crfS);
6719 vassert(crfD < 8);
6720 vassert(crfS < 8);
6721 fpscr_all = getGST_masked( PPC_GST_FPSCR, MASK_FPSCR_RN );
6722 assign( tmp, binop(Iop_And32,
6723 binop(Iop_Shr32,fpscr_all,mkU8(4 * (7-crfS))),
6724 mkU32(0xF)) );
6725 putGST_field( PPC_GST_CR, mkexpr(tmp), crfD );
6726 break;
6727 }
sewardj0e2cc672005-07-29 21:58:51 +00006728
6729 case 0x046: { // mtfsb0 (Move to FPSCR Bit 0, PPC32 p478)
6730 // Bit crbD of the FPSCR is cleared.
cerion76de5cf2005-11-18 18:25:12 +00006731 UChar crbD = ifieldRegDS(theInstr);
6732 UInt b11to20 = IFIELD(theInstr, 11, 10);
sewardj0e2cc672005-07-29 21:58:51 +00006733
6734 if (b11to20 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006735 vex_printf("dis_fp_scr(ppc)(instr,mtfsb0)\n");
sewardj0e2cc672005-07-29 21:58:51 +00006736 return False;
6737 }
cerion5b2325f2005-12-23 00:55:09 +00006738 DIP("mtfsb0%s crb%d\n", flag_rC ? ".":"", crbD);
ceriond953ebb2005-11-29 13:27:20 +00006739 putGST_masked( PPC_GST_FPSCR, mkU32(0), 1<<(31-crbD) );
sewardj0e2cc672005-07-29 21:58:51 +00006740 break;
6741 }
6742
6743 case 0x086: { // mtfsfi (Move to FPSCR Field Immediate, PPC32 p481)
cerion76de5cf2005-11-18 18:25:12 +00006744 UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) );
6745 UChar b16to22 = toUChar( IFIELD( theInstr, 16, 7 ) );
6746 UChar IMM = toUChar( IFIELD( theInstr, 12, 4 ) );
6747 UChar b11 = toUChar( IFIELD( theInstr, 11, 1 ) );
sewardj0e2cc672005-07-29 21:58:51 +00006748
6749 if (b16to22 != 0 || b11 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006750 vex_printf("dis_fp_scr(ppc)(instr,mtfsfi)\n");
sewardj0e2cc672005-07-29 21:58:51 +00006751 return False;
6752 }
cerion5b2325f2005-12-23 00:55:09 +00006753 DIP("mtfsfi%s crf%d,%d\n", flag_rC ? ".":"", crfD, IMM);
ceriond953ebb2005-11-29 13:27:20 +00006754 putGST_field( PPC_GST_FPSCR, mkU32(IMM), crfD );
sewardj0e2cc672005-07-29 21:58:51 +00006755 break;
6756 }
sewardje14bb9f2005-07-22 09:39:02 +00006757
6758 case 0x247: { // mffs (Move from FPSCR, PPC32 p468)
sewardj496b88f2006-10-04 17:46:11 +00006759 UChar frD_addr = ifieldRegDS(theInstr);
6760 UInt b11to20 = IFIELD(theInstr, 11, 10);
6761 IRExpr* fpscr_all = getGST_masked( PPC_GST_FPSCR, MASK_FPSCR_RN );
sewardje14bb9f2005-07-22 09:39:02 +00006762
6763 if (b11to20 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006764 vex_printf("dis_fp_scr(ppc)(instr,mffs)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006765 return False;
6766 }
cerion5b2325f2005-12-23 00:55:09 +00006767 DIP("mffs%s fr%u\n", flag_rC ? ".":"", frD_addr);
6768 putFReg( frD_addr,
6769 unop( Iop_ReinterpI64asF64,
sewardj496b88f2006-10-04 17:46:11 +00006770 unop( Iop_32Uto64, fpscr_all )));
sewardje14bb9f2005-07-22 09:39:02 +00006771 break;
6772 }
6773
6774 case 0x2C7: { // mtfsf (Move to FPSCR Fields, PPC32 p480)
cerion76de5cf2005-11-18 18:25:12 +00006775 UChar b25 = toUChar( IFIELD(theInstr, 25, 1) );
6776 UChar FM = toUChar( IFIELD(theInstr, 17, 8) );
6777 UChar b16 = toUChar( IFIELD(theInstr, 16, 1) );
6778 UChar frB_addr = ifieldRegB(theInstr);
6779 IRTemp frB = newTemp(Ity_F64);
sewardje14bb9f2005-07-22 09:39:02 +00006780 IRTemp rB_32 = newTemp(Ity_I32);
cerion76de5cf2005-11-18 18:25:12 +00006781 Int i, mask;
sewardje14bb9f2005-07-22 09:39:02 +00006782
6783 if (b25 != 0 || b16 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006784 vex_printf("dis_fp_scr(ppc)(instr,mtfsf)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006785 return False;
6786 }
cerion5b2325f2005-12-23 00:55:09 +00006787 DIP("mtfsf%s %d,fr%u\n", flag_rC ? ".":"", FM, frB_addr);
sewardje14bb9f2005-07-22 09:39:02 +00006788 assign( frB, getFReg(frB_addr));
6789 assign( rB_32, unop( Iop_64to32,
6790 unop( Iop_ReinterpF64asI64, mkexpr(frB) )));
6791 // Build 32bit mask from FM:
cerion76de5cf2005-11-18 18:25:12 +00006792 mask = 0;
sewardje14bb9f2005-07-22 09:39:02 +00006793 for (i=0; i<8; i++) {
6794 if ((FM & (1<<(7-i))) == 1) {
6795 mask |= 0xF << (7-i);
6796 }
6797 }
ceriond953ebb2005-11-29 13:27:20 +00006798 putGST_masked( PPC_GST_FPSCR, mkexpr(rB_32), mask );
sewardje14bb9f2005-07-22 09:39:02 +00006799 break;
6800 }
6801
6802 default:
cerion5b2325f2005-12-23 00:55:09 +00006803 vex_printf("dis_fp_scr(ppc)(opc2)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006804 return False;
6805 }
6806 return True;
6807}
6808
6809
6810
cerion32aad402005-09-10 12:02:24 +00006811/*------------------------------------------------------------*/
6812/*--- AltiVec Instruction Translation ---*/
6813/*------------------------------------------------------------*/
6814
6815/*
6816 Altivec Cache Control Instructions (Data Streams)
6817*/
6818static Bool dis_av_datastream ( UInt theInstr )
6819{
cerion76de5cf2005-11-18 18:25:12 +00006820 /* X-Form */
6821 UChar opc1 = ifieldOPC(theInstr);
6822 UChar flag_T = toUChar( IFIELD( theInstr, 25, 1 ) );
6823 UChar flag_A = flag_T;
6824 UChar b23to24 = toUChar( IFIELD( theInstr, 23, 2 ) );
6825 UChar STRM = toUChar( IFIELD( theInstr, 21, 2 ) );
6826 UChar rA_addr = ifieldRegA(theInstr);
6827 UChar rB_addr = ifieldRegB(theInstr);
6828 UInt opc2 = ifieldOPClo10(theInstr);
6829 UChar b0 = ifieldBIT0(theInstr);
cerion32aad402005-09-10 12:02:24 +00006830
6831 if (opc1 != 0x1F || b23to24 != 0 || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006832 vex_printf("dis_av_datastream(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00006833 return False;
6834 }
6835
6836 switch (opc2) {
6837 case 0x156: // dst (Data Stream Touch, AV p115)
cerion5b2325f2005-12-23 00:55:09 +00006838 DIP("dst%s r%u,r%u,%d\n", flag_T ? "t" : "",
6839 rA_addr, rB_addr, STRM);
sewardj923c65b2006-12-27 23:59:31 +00006840 break;
cerion32aad402005-09-10 12:02:24 +00006841
6842 case 0x176: // dstst (Data Stream Touch for Store, AV p117)
cerion5b2325f2005-12-23 00:55:09 +00006843 DIP("dstst%s r%u,r%u,%d\n", flag_T ? "t" : "",
6844 rA_addr, rB_addr, STRM);
sewardj923c65b2006-12-27 23:59:31 +00006845 break;
cerion32aad402005-09-10 12:02:24 +00006846
6847 case 0x336: // dss (Data Stream Stop, AV p114)
6848 if (rA_addr != 0 || rB_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006849 vex_printf("dis_av_datastream(ppc)(opc2,dst)\n");
cerion32aad402005-09-10 12:02:24 +00006850 return False;
6851 }
6852 if (flag_A == 0) {
6853 DIP("dss %d\n", STRM);
cerion32aad402005-09-10 12:02:24 +00006854 } else {
6855 DIP("dssall\n");
cerion32aad402005-09-10 12:02:24 +00006856 }
sewardj923c65b2006-12-27 23:59:31 +00006857 break;
cerion32aad402005-09-10 12:02:24 +00006858
6859 default:
cerion5b2325f2005-12-23 00:55:09 +00006860 vex_printf("dis_av_datastream(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00006861 return False;
6862 }
6863 return True;
6864}
6865
6866/*
6867 AltiVec Processor Control Instructions
6868*/
6869static Bool dis_av_procctl ( UInt theInstr )
6870{
cerion76de5cf2005-11-18 18:25:12 +00006871 /* VX-Form */
6872 UChar opc1 = ifieldOPC(theInstr);
6873 UChar vD_addr = ifieldRegDS(theInstr);
6874 UChar vA_addr = ifieldRegA(theInstr);
6875 UChar vB_addr = ifieldRegB(theInstr);
6876 UInt opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00006877
6878 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00006879 vex_printf("dis_av_procctl(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00006880 return False;
6881 }
6882
6883 switch (opc2) {
6884 case 0x604: // mfvscr (Move from VSCR, AV p129)
6885 if (vA_addr != 0 || vB_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006886 vex_printf("dis_av_procctl(ppc)(opc2,dst)\n");
cerion32aad402005-09-10 12:02:24 +00006887 return False;
6888 }
6889 DIP("mfvscr v%d\n", vD_addr);
ceriond953ebb2005-11-29 13:27:20 +00006890 putVReg( vD_addr, unop(Iop_32UtoV128, getGST( PPC_GST_VSCR )) );
cerion225a0342005-09-12 20:49:09 +00006891 break;
cerion32aad402005-09-10 12:02:24 +00006892
cerion225a0342005-09-12 20:49:09 +00006893 case 0x644: { // mtvscr (Move to VSCR, AV p130)
sewardj197bd172005-10-12 11:34:33 +00006894 IRTemp vB = newTemp(Ity_V128);
cerion32aad402005-09-10 12:02:24 +00006895 if (vD_addr != 0 || vA_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006896 vex_printf("dis_av_procctl(ppc)(opc2,dst)\n");
cerion32aad402005-09-10 12:02:24 +00006897 return False;
6898 }
6899 DIP("mtvscr v%d\n", vB_addr);
cerion225a0342005-09-12 20:49:09 +00006900 assign( vB, getVReg(vB_addr));
ceriond953ebb2005-11-29 13:27:20 +00006901 putGST( PPC_GST_VSCR, unop(Iop_V128to32, mkexpr(vB)) );
cerion225a0342005-09-12 20:49:09 +00006902 break;
6903 }
cerion32aad402005-09-10 12:02:24 +00006904 default:
cerion5b2325f2005-12-23 00:55:09 +00006905 vex_printf("dis_av_procctl(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00006906 return False;
6907 }
6908 return True;
6909}
ceriona982c052005-06-28 17:23:09 +00006910
6911/*
6912 AltiVec Load Instructions
6913*/
sewardjdd40fdf2006-12-24 02:20:24 +00006914static Bool dis_av_load ( VexAbiInfo* vbi, UInt theInstr )
ceriona982c052005-06-28 17:23:09 +00006915{
cerion76de5cf2005-11-18 18:25:12 +00006916 /* X-Form */
6917 UChar opc1 = ifieldOPC(theInstr);
6918 UChar vD_addr = ifieldRegDS(theInstr);
6919 UChar rA_addr = ifieldRegA(theInstr);
6920 UChar rB_addr = ifieldRegB(theInstr);
6921 UInt opc2 = ifieldOPClo10(theInstr);
6922 UChar b0 = ifieldBIT0(theInstr);
ceriona982c052005-06-28 17:23:09 +00006923
cerionfb197c42005-12-24 12:32:10 +00006924 IRType ty = mode64 ? Ity_I64 : Ity_I32;
6925 IRTemp EA = newTemp(ty);
6926 IRTemp EA_align16 = newTemp(ty);
cerion2831b002005-11-30 19:55:22 +00006927
ceriona982c052005-06-28 17:23:09 +00006928 if (opc1 != 0x1F || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006929 vex_printf("dis_av_load(ppc)(instr)\n");
ceriona982c052005-06-28 17:23:09 +00006930 return False;
6931 }
6932
cerionfb197c42005-12-24 12:32:10 +00006933 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
6934 assign( EA_align16, addr_align( mkexpr(EA), 16 ) );
ceriona50fde52005-07-01 21:16:10 +00006935
ceriona982c052005-06-28 17:23:09 +00006936 switch (opc2) {
6937
cerion6f6c6a02005-09-13 18:41:09 +00006938 case 0x006: { // lvsl (Load Vector for Shift Left, AV p123)
cerionfb197c42005-12-24 12:32:10 +00006939 IRDirty* d;
sewardjd1470942005-10-22 02:01:16 +00006940 UInt vD_off = vectorGuestRegOffset(vD_addr);
6941 IRExpr** args = mkIRExprVec_3(
6942 mkU32(vD_off),
sewardje9d8a262009-07-01 08:06:34 +00006943 binop(Iop_And32, mkNarrowTo32(ty, mkexpr(EA)),
cerionfb197c42005-12-24 12:32:10 +00006944 mkU32(0xF)),
sewardjd1470942005-10-22 02:01:16 +00006945 mkU32(0)/*left*/ );
cerion5b2325f2005-12-23 00:55:09 +00006946 if (!mode64) {
cerion4c4f5ef2006-01-02 14:41:50 +00006947 d = unsafeIRDirty_0_N (
6948 0/*regparms*/,
6949 "ppc32g_dirtyhelper_LVS",
sewardjdd40fdf2006-12-24 02:20:24 +00006950 fnptr_to_fnentry(vbi, &ppc32g_dirtyhelper_LVS),
cerion4c4f5ef2006-01-02 14:41:50 +00006951 args );
cerion5b2325f2005-12-23 00:55:09 +00006952 } else {
cerion4c4f5ef2006-01-02 14:41:50 +00006953 d = unsafeIRDirty_0_N (
6954 0/*regparms*/,
6955 "ppc64g_dirtyhelper_LVS",
sewardjdd40fdf2006-12-24 02:20:24 +00006956 fnptr_to_fnentry(vbi, &ppc64g_dirtyhelper_LVS),
cerion4c4f5ef2006-01-02 14:41:50 +00006957 args );
cerion5b2325f2005-12-23 00:55:09 +00006958 }
ceriond953ebb2005-11-29 13:27:20 +00006959 DIP("lvsl v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr);
cerion6f6c6a02005-09-13 18:41:09 +00006960 /* declare guest state effects */
6961 d->needsBBP = True;
6962 d->nFxState = 1;
6963 d->fxState[0].fx = Ifx_Write;
sewardjd1470942005-10-22 02:01:16 +00006964 d->fxState[0].offset = vD_off;
cerion6f6c6a02005-09-13 18:41:09 +00006965 d->fxState[0].size = sizeof(U128);
cerion32aad402005-09-10 12:02:24 +00006966
cerion6f6c6a02005-09-13 18:41:09 +00006967 /* execute the dirty call, side-effecting guest state */
6968 stmt( IRStmt_Dirty(d) );
6969 break;
6970 }
6971 case 0x026: { // lvsr (Load Vector for Shift Right, AV p125)
cerionfb197c42005-12-24 12:32:10 +00006972 IRDirty* d;
sewardjd1470942005-10-22 02:01:16 +00006973 UInt vD_off = vectorGuestRegOffset(vD_addr);
6974 IRExpr** args = mkIRExprVec_3(
6975 mkU32(vD_off),
sewardje9d8a262009-07-01 08:06:34 +00006976 binop(Iop_And32, mkNarrowTo32(ty, mkexpr(EA)),
cerionfb197c42005-12-24 12:32:10 +00006977 mkU32(0xF)),
sewardjd1470942005-10-22 02:01:16 +00006978 mkU32(1)/*right*/ );
cerion5b2325f2005-12-23 00:55:09 +00006979 if (!mode64) {
cerion4c4f5ef2006-01-02 14:41:50 +00006980 d = unsafeIRDirty_0_N (
6981 0/*regparms*/,
6982 "ppc32g_dirtyhelper_LVS",
sewardjdd40fdf2006-12-24 02:20:24 +00006983 fnptr_to_fnentry(vbi, &ppc32g_dirtyhelper_LVS),
cerion4c4f5ef2006-01-02 14:41:50 +00006984 args );
cerion5b2325f2005-12-23 00:55:09 +00006985 } else {
cerion4c4f5ef2006-01-02 14:41:50 +00006986 d = unsafeIRDirty_0_N (
6987 0/*regparms*/,
6988 "ppc64g_dirtyhelper_LVS",
sewardjdd40fdf2006-12-24 02:20:24 +00006989 fnptr_to_fnentry(vbi, &ppc64g_dirtyhelper_LVS),
cerion4c4f5ef2006-01-02 14:41:50 +00006990 args );
cerion5b2325f2005-12-23 00:55:09 +00006991 }
ceriond953ebb2005-11-29 13:27:20 +00006992 DIP("lvsr v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr);
cerion6f6c6a02005-09-13 18:41:09 +00006993 /* declare guest state effects */
6994 d->needsBBP = True;
6995 d->nFxState = 1;
6996 d->fxState[0].fx = Ifx_Write;
sewardjd1470942005-10-22 02:01:16 +00006997 d->fxState[0].offset = vD_off;
cerion6f6c6a02005-09-13 18:41:09 +00006998 d->fxState[0].size = sizeof(U128);
cerion32aad402005-09-10 12:02:24 +00006999
cerion6f6c6a02005-09-13 18:41:09 +00007000 /* execute the dirty call, side-effecting guest state */
7001 stmt( IRStmt_Dirty(d) );
7002 break;
7003 }
cerion32aad402005-09-10 12:02:24 +00007004 case 0x007: // lvebx (Load Vector Element Byte Indexed, AV p119)
ceriond953ebb2005-11-29 13:27:20 +00007005 DIP("lvebx v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr);
cerion61c92742005-09-14 22:59:26 +00007006 /* loads addressed byte into vector[EA[0:3]
7007 since all other destination bytes are undefined,
7008 can simply load entire vector from 16-aligned EA */
cerionfb197c42005-12-24 12:32:10 +00007009 putVReg( vD_addr, loadBE(Ity_V128, mkexpr(EA_align16)) );
cerion61c92742005-09-14 22:59:26 +00007010 break;
cerion32aad402005-09-10 12:02:24 +00007011
7012 case 0x027: // lvehx (Load Vector Element Half Word Indexed, AV p121)
ceriond953ebb2005-11-29 13:27:20 +00007013 DIP("lvehx v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr);
cerion61c92742005-09-14 22:59:26 +00007014 /* see note for lvebx */
cerionfb197c42005-12-24 12:32:10 +00007015 putVReg( vD_addr, loadBE(Ity_V128, mkexpr(EA_align16)) );
cerion61c92742005-09-14 22:59:26 +00007016 break;
cerion32aad402005-09-10 12:02:24 +00007017
7018 case 0x047: // lvewx (Load Vector Element Word Indexed, AV p122)
ceriond953ebb2005-11-29 13:27:20 +00007019 DIP("lvewx v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr);
cerion61c92742005-09-14 22:59:26 +00007020 /* see note for lvebx */
cerionfb197c42005-12-24 12:32:10 +00007021 putVReg( vD_addr, loadBE(Ity_V128, mkexpr(EA_align16)) );
cerion61c92742005-09-14 22:59:26 +00007022 break;
ceriona982c052005-06-28 17:23:09 +00007023
7024 case 0x067: // lvx (Load Vector Indexed, AV p127)
ceriond953ebb2005-11-29 13:27:20 +00007025 DIP("lvx v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr);
cerionfb197c42005-12-24 12:32:10 +00007026 putVReg( vD_addr, loadBE(Ity_V128, mkexpr(EA_align16)) );
ceriona50fde52005-07-01 21:16:10 +00007027 break;
ceriona982c052005-06-28 17:23:09 +00007028
cerion32aad402005-09-10 12:02:24 +00007029 case 0x167: // lvxl (Load Vector Indexed LRU, AV p128)
ceriond953ebb2005-11-29 13:27:20 +00007030 DIP("lvxl v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr);
sewardjd2fd8642006-12-27 21:21:14 +00007031 putVReg( vD_addr, loadBE(Ity_V128, mkexpr(EA_align16)) );
7032 break;
ceriona982c052005-06-28 17:23:09 +00007033
7034 default:
cerion5b2325f2005-12-23 00:55:09 +00007035 vex_printf("dis_av_load(ppc)(opc2)\n");
ceriona982c052005-06-28 17:23:09 +00007036 return False;
7037 }
7038 return True;
7039}
7040
cerion2831b002005-11-30 19:55:22 +00007041
ceriona982c052005-06-28 17:23:09 +00007042/*
7043 AltiVec Store Instructions
7044*/
7045static Bool dis_av_store ( UInt theInstr )
7046{
cerion76de5cf2005-11-18 18:25:12 +00007047 /* X-Form */
7048 UChar opc1 = ifieldOPC(theInstr);
7049 UChar vS_addr = ifieldRegDS(theInstr);
7050 UChar rA_addr = ifieldRegA(theInstr);
7051 UChar rB_addr = ifieldRegB(theInstr);
7052 UInt opc2 = ifieldOPClo10(theInstr);
7053 UChar b0 = ifieldBIT0(theInstr);
ceriona982c052005-06-28 17:23:09 +00007054
cerion2831b002005-11-30 19:55:22 +00007055 IRType ty = mode64 ? Ity_I64 : Ity_I32;
7056 IRTemp EA = newTemp(ty);
ceriondba87e22006-01-02 15:15:45 +00007057 IRTemp addr_aligned = newTemp(ty);
cerion2831b002005-11-30 19:55:22 +00007058 IRTemp vS = newTemp(Ity_V128);
7059 IRTemp eb = newTemp(Ity_I8);
7060 IRTemp idx = newTemp(Ity_I8);
ceriona982c052005-06-28 17:23:09 +00007061
7062 if (opc1 != 0x1F || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00007063 vex_printf("dis_av_store(ppc)(instr)\n");
ceriona982c052005-06-28 17:23:09 +00007064 return False;
7065 }
7066
ceriond953ebb2005-11-29 13:27:20 +00007067 assign( vS, getVReg(vS_addr));
cerion2831b002005-11-30 19:55:22 +00007068 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
ceriond953ebb2005-11-29 13:27:20 +00007069
ceriona982c052005-06-28 17:23:09 +00007070 switch (opc2) {
cerion61c92742005-09-14 22:59:26 +00007071 case 0x087: { // stvebx (Store Vector Byte Indexed, AV p131)
ceriond953ebb2005-11-29 13:27:20 +00007072 DIP("stvebx v%d,r%u,r%u\n", vS_addr, rA_addr, rB_addr);
cerion61c92742005-09-14 22:59:26 +00007073 assign( eb, binop(Iop_And8, mkU8(0xF),
cerion2831b002005-11-30 19:55:22 +00007074 unop(Iop_32to8,
sewardje9d8a262009-07-01 08:06:34 +00007075 mkNarrowTo32(ty, mkexpr(EA)) )) );
cerion5b2325f2005-12-23 00:55:09 +00007076 assign( idx, binop(Iop_Shl8,
7077 binop(Iop_Sub8, mkU8(15), mkexpr(eb)),
cerion61c92742005-09-14 22:59:26 +00007078 mkU8(3)) );
7079 storeBE( mkexpr(EA),
7080 unop(Iop_32to8, unop(Iop_V128to32,
7081 binop(Iop_ShrV128, mkexpr(vS), mkexpr(idx)))) );
7082 break;
7083 }
7084 case 0x0A7: { // stvehx (Store Vector Half Word Indexed, AV p132)
ceriond953ebb2005-11-29 13:27:20 +00007085 DIP("stvehx v%d,r%u,r%u\n", vS_addr, rA_addr, rB_addr);
ceriondba87e22006-01-02 15:15:45 +00007086 assign( addr_aligned, addr_align(mkexpr(EA), 2) );
cerion61c92742005-09-14 22:59:26 +00007087 assign( eb, binop(Iop_And8, mkU8(0xF),
sewardje9d8a262009-07-01 08:06:34 +00007088 mkNarrowTo8(ty, mkexpr(addr_aligned) )) );
cerion5b2325f2005-12-23 00:55:09 +00007089 assign( idx, binop(Iop_Shl8,
7090 binop(Iop_Sub8, mkU8(14), mkexpr(eb)),
cerion61c92742005-09-14 22:59:26 +00007091 mkU8(3)) );
ceriond953ebb2005-11-29 13:27:20 +00007092 storeBE( mkexpr(addr_aligned),
cerion61c92742005-09-14 22:59:26 +00007093 unop(Iop_32to16, unop(Iop_V128to32,
7094 binop(Iop_ShrV128, mkexpr(vS), mkexpr(idx)))) );
7095 break;
7096 }
7097 case 0x0C7: { // stvewx (Store Vector Word Indexed, AV p133)
ceriond953ebb2005-11-29 13:27:20 +00007098 DIP("stvewx v%d,r%u,r%u\n", vS_addr, rA_addr, rB_addr);
ceriondba87e22006-01-02 15:15:45 +00007099 assign( addr_aligned, addr_align(mkexpr(EA), 4) );
cerion61c92742005-09-14 22:59:26 +00007100 assign( eb, binop(Iop_And8, mkU8(0xF),
sewardje9d8a262009-07-01 08:06:34 +00007101 mkNarrowTo8(ty, mkexpr(addr_aligned) )) );
cerion5b2325f2005-12-23 00:55:09 +00007102 assign( idx, binop(Iop_Shl8,
7103 binop(Iop_Sub8, mkU8(12), mkexpr(eb)),
cerion61c92742005-09-14 22:59:26 +00007104 mkU8(3)) );
ceriond953ebb2005-11-29 13:27:20 +00007105 storeBE( mkexpr(addr_aligned),
cerion61c92742005-09-14 22:59:26 +00007106 unop(Iop_V128to32,
7107 binop(Iop_ShrV128, mkexpr(vS), mkexpr(idx))) );
7108 break;
7109 }
cerion32aad402005-09-10 12:02:24 +00007110
ceriona982c052005-06-28 17:23:09 +00007111 case 0x0E7: // stvx (Store Vector Indexed, AV p134)
ceriond953ebb2005-11-29 13:27:20 +00007112 DIP("stvx v%d,r%u,r%u\n", vS_addr, rA_addr, rB_addr);
7113 storeBE( addr_align( mkexpr(EA), 16 ), mkexpr(vS) );
ceriona982c052005-06-28 17:23:09 +00007114 break;
7115
cerion32aad402005-09-10 12:02:24 +00007116 case 0x1E7: // stvxl (Store Vector Indexed LRU, AV p135)
ceriond953ebb2005-11-29 13:27:20 +00007117 DIP("stvxl v%d,r%u,r%u\n", vS_addr, rA_addr, rB_addr);
sewardjd2fd8642006-12-27 21:21:14 +00007118 storeBE( addr_align( mkexpr(EA), 16 ), mkexpr(vS) );
7119 break;
ceriona982c052005-06-28 17:23:09 +00007120
7121 default:
cerion5b2325f2005-12-23 00:55:09 +00007122 vex_printf("dis_av_store(ppc)(opc2)\n");
ceriona982c052005-06-28 17:23:09 +00007123 return False;
7124 }
7125 return True;
7126}
7127
cerion32aad402005-09-10 12:02:24 +00007128/*
7129 AltiVec Arithmetic Instructions
7130*/
7131static Bool dis_av_arith ( UInt theInstr )
7132{
cerion76de5cf2005-11-18 18:25:12 +00007133 /* VX-Form */
7134 UChar opc1 = ifieldOPC(theInstr);
7135 UChar vD_addr = ifieldRegDS(theInstr);
7136 UChar vA_addr = ifieldRegA(theInstr);
7137 UChar vB_addr = ifieldRegB(theInstr);
7138 UInt opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00007139
ceriond3e52412005-09-14 21:15:40 +00007140 IRTemp vA = newTemp(Ity_V128);
7141 IRTemp vB = newTemp(Ity_V128);
cerion4a49b032005-11-08 16:23:07 +00007142 IRTemp z3 = newTemp(Ity_I64);
7143 IRTemp z2 = newTemp(Ity_I64);
7144 IRTemp z1 = newTemp(Ity_I64);
7145 IRTemp z0 = newTemp(Ity_I64);
7146 IRTemp aEvn, aOdd;
7147 IRTemp a15, a14, a13, a12, a11, a10, a9, a8;
7148 IRTemp a7, a6, a5, a4, a3, a2, a1, a0;
7149 IRTemp b3, b2, b1, b0;
7150
7151 aEvn = aOdd = IRTemp_INVALID;
7152 a15 = a14 = a13 = a12 = a11 = a10 = a9 = a8 = IRTemp_INVALID;
7153 a7 = a6 = a5 = a4 = a3 = a2 = a1 = a0 = IRTemp_INVALID;
7154 b3 = b2 = b1 = b0 = IRTemp_INVALID;
7155
ceriond3e52412005-09-14 21:15:40 +00007156 assign( vA, getVReg(vA_addr));
7157 assign( vB, getVReg(vB_addr));
7158
cerion32aad402005-09-10 12:02:24 +00007159 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00007160 vex_printf("dis_av_arith(ppc)(opc1 != 0x4)\n");
cerion32aad402005-09-10 12:02:24 +00007161 return False;
7162 }
7163
7164 switch (opc2) {
7165 /* Add */
ceriond3e52412005-09-14 21:15:40 +00007166 case 0x180: { // vaddcuw (Add Carryout Unsigned Word, AV p136)
cerion32aad402005-09-10 12:02:24 +00007167 DIP("vaddcuw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007168 /* unsigned_ov(x+y) = (y >u not(x)) */
ceriond3e52412005-09-14 21:15:40 +00007169 putVReg( vD_addr, binop(Iop_ShrN32x4,
cerion36991ef2005-09-15 12:42:16 +00007170 binop(Iop_CmpGT32Ux4, mkexpr(vB),
7171 unop(Iop_NotV128, mkexpr(vA))),
ceriond3e52412005-09-14 21:15:40 +00007172 mkU8(31)) );
7173 break;
7174 }
cerion32aad402005-09-10 12:02:24 +00007175 case 0x000: // vaddubm (Add Unsigned Byte Modulo, AV p141)
7176 DIP("vaddubm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007177 putVReg( vD_addr, binop(Iop_Add8x16, mkexpr(vA), mkexpr(vB)) );
7178 break;
7179
cerion32aad402005-09-10 12:02:24 +00007180 case 0x040: // vadduhm (Add Unsigned Half Word Modulo, AV p143)
7181 DIP("vadduhm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007182 putVReg( vD_addr, binop(Iop_Add16x8, mkexpr(vA), mkexpr(vB)) );
7183 break;
7184
cerion32aad402005-09-10 12:02:24 +00007185 case 0x080: // vadduwm (Add Unsigned Word Modulo, AV p145)
7186 DIP("vadduwm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007187 putVReg( vD_addr, binop(Iop_Add32x4, mkexpr(vA), mkexpr(vB)) );
7188 break;
7189
cerion32aad402005-09-10 12:02:24 +00007190 case 0x200: // vaddubs (Add Unsigned Byte Saturate, AV p142)
7191 DIP("vaddubs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007192 putVReg( vD_addr, binop(Iop_QAdd8Ux16, mkexpr(vA), mkexpr(vB)) );
7193 // TODO: set VSCR[SAT], perhaps via new primop: Iop_SatOfQAdd8Ux16
7194 break;
7195
cerion32aad402005-09-10 12:02:24 +00007196 case 0x240: // vadduhs (Add Unsigned Half Word Saturate, AV p144)
7197 DIP("vadduhs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007198 putVReg( vD_addr, binop(Iop_QAdd16Ux8, mkexpr(vA), mkexpr(vB)) );
7199 // TODO: set VSCR[SAT]
7200 break;
7201
cerion32aad402005-09-10 12:02:24 +00007202 case 0x280: // vadduws (Add Unsigned Word Saturate, AV p146)
7203 DIP("vadduws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007204 putVReg( vD_addr, binop(Iop_QAdd32Ux4, mkexpr(vA), mkexpr(vB)) );
7205 // TODO: set VSCR[SAT]
7206 break;
7207
cerion32aad402005-09-10 12:02:24 +00007208 case 0x300: // vaddsbs (Add Signed Byte Saturate, AV p138)
7209 DIP("vaddsbs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007210 putVReg( vD_addr, binop(Iop_QAdd8Sx16, mkexpr(vA), mkexpr(vB)) );
7211 // TODO: set VSCR[SAT]
7212 break;
7213
cerion32aad402005-09-10 12:02:24 +00007214 case 0x340: // vaddshs (Add Signed Half Word Saturate, AV p139)
7215 DIP("vaddshs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007216 putVReg( vD_addr, binop(Iop_QAdd16Sx8, mkexpr(vA), mkexpr(vB)) );
7217 // TODO: set VSCR[SAT]
7218 break;
7219
cerion32aad402005-09-10 12:02:24 +00007220 case 0x380: // vaddsws (Add Signed Word Saturate, AV p140)
7221 DIP("vaddsws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007222 putVReg( vD_addr, binop(Iop_QAdd32Sx4, mkexpr(vA), mkexpr(vB)) );
7223 // TODO: set VSCR[SAT]
7224 break;
7225
7226
cerion32aad402005-09-10 12:02:24 +00007227 /* Subtract */
cerion36991ef2005-09-15 12:42:16 +00007228 case 0x580: { // vsubcuw (Subtract Carryout Unsigned Word, AV p260)
cerion32aad402005-09-10 12:02:24 +00007229 DIP("vsubcuw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007230 /* unsigned_ov(x-y) = (y >u x) */
7231 putVReg( vD_addr, binop(Iop_ShrN32x4,
7232 unop(Iop_NotV128,
7233 binop(Iop_CmpGT32Ux4, mkexpr(vB),
7234 mkexpr(vA))),
7235 mkU8(31)) );
7236 break;
7237 }
cerion32aad402005-09-10 12:02:24 +00007238 case 0x400: // vsububm (Subtract Unsigned Byte Modulo, AV p265)
7239 DIP("vsububm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007240 putVReg( vD_addr, binop(Iop_Sub8x16, mkexpr(vA), mkexpr(vB)) );
7241 break;
7242
cerion32aad402005-09-10 12:02:24 +00007243 case 0x440: // vsubuhm (Subtract Unsigned Half Word Modulo, AV p267)
7244 DIP("vsubuhm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007245 putVReg( vD_addr, binop(Iop_Sub16x8, mkexpr(vA), mkexpr(vB)) );
7246 break;
7247
cerion32aad402005-09-10 12:02:24 +00007248 case 0x480: // vsubuwm (Subtract Unsigned Word Modulo, AV p269)
7249 DIP("vsubuwm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007250 putVReg( vD_addr, binop(Iop_Sub32x4, mkexpr(vA), mkexpr(vB)) );
7251 break;
7252
cerion32aad402005-09-10 12:02:24 +00007253 case 0x600: // vsububs (Subtract Unsigned Byte Saturate, AV p266)
7254 DIP("vsububs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007255 putVReg( vD_addr, binop(Iop_QSub8Ux16, mkexpr(vA), mkexpr(vB)) );
7256 // TODO: set VSCR[SAT]
7257 break;
7258
cerion5b2325f2005-12-23 00:55:09 +00007259 case 0x640: // vsubuhs (Subtract Unsigned HWord Saturate, AV p268)
cerion32aad402005-09-10 12:02:24 +00007260 DIP("vsubuhs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007261 putVReg( vD_addr, binop(Iop_QSub16Ux8, mkexpr(vA), mkexpr(vB)) );
7262 // TODO: set VSCR[SAT]
7263 break;
7264
cerion32aad402005-09-10 12:02:24 +00007265 case 0x680: // vsubuws (Subtract Unsigned Word Saturate, AV p270)
7266 DIP("vsubuws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007267 putVReg( vD_addr, binop(Iop_QSub32Ux4, mkexpr(vA), mkexpr(vB)) );
7268 // TODO: set VSCR[SAT]
7269 break;
7270
cerion32aad402005-09-10 12:02:24 +00007271 case 0x700: // vsubsbs (Subtract Signed Byte Saturate, AV p262)
7272 DIP("vsubsbs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007273 putVReg( vD_addr, binop(Iop_QSub8Sx16, mkexpr(vA), mkexpr(vB)) );
7274 // TODO: set VSCR[SAT]
7275 break;
7276
cerion32aad402005-09-10 12:02:24 +00007277 case 0x740: // vsubshs (Subtract Signed Half Word Saturate, AV p263)
7278 DIP("vsubshs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007279 putVReg( vD_addr, binop(Iop_QSub16Sx8, mkexpr(vA), mkexpr(vB)) );
7280 // TODO: set VSCR[SAT]
7281 break;
7282
cerion32aad402005-09-10 12:02:24 +00007283 case 0x780: // vsubsws (Subtract Signed Word Saturate, AV p264)
7284 DIP("vsubsws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007285 putVReg( vD_addr, binop(Iop_QSub32Sx4, mkexpr(vA), mkexpr(vB)) );
7286 // TODO: set VSCR[SAT]
7287 break;
cerion32aad402005-09-10 12:02:24 +00007288
7289
7290 /* Maximum */
7291 case 0x002: // vmaxub (Maximum Unsigned Byte, AV p182)
7292 DIP("vmaxub v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007293 putVReg( vD_addr, binop(Iop_Max8Ux16, mkexpr(vA), mkexpr(vB)) );
7294 break;
cerion32aad402005-09-10 12:02:24 +00007295
7296 case 0x042: // vmaxuh (Maximum Unsigned Half Word, AV p183)
7297 DIP("vmaxuh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007298 putVReg( vD_addr, binop(Iop_Max16Ux8, mkexpr(vA), mkexpr(vB)) );
7299 break;
cerion32aad402005-09-10 12:02:24 +00007300
7301 case 0x082: // vmaxuw (Maximum Unsigned Word, AV p184)
7302 DIP("vmaxuw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007303 putVReg( vD_addr, binop(Iop_Max32Ux4, mkexpr(vA), mkexpr(vB)) );
7304 break;
cerion32aad402005-09-10 12:02:24 +00007305
7306 case 0x102: // vmaxsb (Maximum Signed Byte, AV p179)
7307 DIP("vmaxsb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007308 putVReg( vD_addr, binop(Iop_Max8Sx16, mkexpr(vA), mkexpr(vB)) );
7309 break;
cerion32aad402005-09-10 12:02:24 +00007310
7311 case 0x142: // vmaxsh (Maximum Signed Half Word, AV p180)
7312 DIP("vmaxsh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007313 putVReg( vD_addr, binop(Iop_Max16Sx8, mkexpr(vA), mkexpr(vB)) );
7314 break;
cerion32aad402005-09-10 12:02:24 +00007315
7316 case 0x182: // vmaxsw (Maximum Signed Word, AV p181)
7317 DIP("vmaxsw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007318 putVReg( vD_addr, binop(Iop_Max32Sx4, mkexpr(vA), mkexpr(vB)) );
7319 break;
cerion32aad402005-09-10 12:02:24 +00007320
7321
7322 /* Minimum */
7323 case 0x202: // vminub (Minimum Unsigned Byte, AV p191)
7324 DIP("vminub v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007325 putVReg( vD_addr, binop(Iop_Min8Ux16, mkexpr(vA), mkexpr(vB)) );
7326 break;
cerion32aad402005-09-10 12:02:24 +00007327
7328 case 0x242: // vminuh (Minimum Unsigned Half Word, AV p192)
7329 DIP("vminuh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007330 putVReg( vD_addr, binop(Iop_Min16Ux8, mkexpr(vA), mkexpr(vB)) );
7331 break;
cerion32aad402005-09-10 12:02:24 +00007332
7333 case 0x282: // vminuw (Minimum Unsigned Word, AV p193)
7334 DIP("vminuw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007335 putVReg( vD_addr, binop(Iop_Min32Ux4, mkexpr(vA), mkexpr(vB)) );
7336 break;
cerion32aad402005-09-10 12:02:24 +00007337
7338 case 0x302: // vminsb (Minimum Signed Byte, AV p188)
7339 DIP("vminsb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007340 putVReg( vD_addr, binop(Iop_Min8Sx16, mkexpr(vA), mkexpr(vB)) );
7341 break;
cerion32aad402005-09-10 12:02:24 +00007342
7343 case 0x342: // vminsh (Minimum Signed Half Word, AV p189)
7344 DIP("vminsh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007345 putVReg( vD_addr, binop(Iop_Min16Sx8, mkexpr(vA), mkexpr(vB)) );
7346 break;
cerion32aad402005-09-10 12:02:24 +00007347
7348 case 0x382: // vminsw (Minimum Signed Word, AV p190)
7349 DIP("vminsw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007350 putVReg( vD_addr, binop(Iop_Min32Sx4, mkexpr(vA), mkexpr(vB)) );
7351 break;
7352
cerion32aad402005-09-10 12:02:24 +00007353
7354 /* Average */
7355 case 0x402: // vavgub (Average Unsigned Byte, AV p152)
7356 DIP("vavgub v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007357 putVReg( vD_addr, binop(Iop_Avg8Ux16, mkexpr(vA), mkexpr(vB)) );
7358 break;
cerion32aad402005-09-10 12:02:24 +00007359
7360 case 0x442: // vavguh (Average Unsigned Half Word, AV p153)
7361 DIP("vavguh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007362 putVReg( vD_addr, binop(Iop_Avg16Ux8, mkexpr(vA), mkexpr(vB)) );
7363 break;
cerion32aad402005-09-10 12:02:24 +00007364
7365 case 0x482: // vavguw (Average Unsigned Word, AV p154)
7366 DIP("vavguw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007367 putVReg( vD_addr, binop(Iop_Avg32Ux4, mkexpr(vA), mkexpr(vB)) );
7368 break;
cerion32aad402005-09-10 12:02:24 +00007369
7370 case 0x502: // vavgsb (Average Signed Byte, AV p149)
7371 DIP("vavgsb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007372 putVReg( vD_addr, binop(Iop_Avg8Sx16, mkexpr(vA), mkexpr(vB)) );
7373 break;
cerion32aad402005-09-10 12:02:24 +00007374
7375 case 0x542: // vavgsh (Average Signed Half Word, AV p150)
7376 DIP("vavgsh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007377 putVReg( vD_addr, binop(Iop_Avg16Sx8, mkexpr(vA), mkexpr(vB)) );
7378 break;
cerion32aad402005-09-10 12:02:24 +00007379
7380 case 0x582: // vavgsw (Average Signed Word, AV p151)
7381 DIP("vavgsw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007382 putVReg( vD_addr, binop(Iop_Avg32Sx4, mkexpr(vA), mkexpr(vB)) );
7383 break;
cerion32aad402005-09-10 12:02:24 +00007384
7385
7386 /* Multiply */
7387 case 0x008: // vmuloub (Multiply Odd Unsigned Byte, AV p213)
7388 DIP("vmuloub v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00007389 putVReg( vD_addr,
7390 binop(Iop_MullEven8Ux16, mkexpr(vA), mkexpr(vB)));
cerion36991ef2005-09-15 12:42:16 +00007391 break;
cerion32aad402005-09-10 12:02:24 +00007392
7393 case 0x048: // vmulouh (Multiply Odd Unsigned Half Word, AV p214)
7394 DIP("vmulouh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00007395 putVReg( vD_addr,
7396 binop(Iop_MullEven16Ux8, mkexpr(vA), mkexpr(vB)));
cerion36991ef2005-09-15 12:42:16 +00007397 break;
cerion32aad402005-09-10 12:02:24 +00007398
7399 case 0x108: // vmulosb (Multiply Odd Signed Byte, AV p211)
7400 DIP("vmulosb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00007401 putVReg( vD_addr,
7402 binop(Iop_MullEven8Sx16, mkexpr(vA), mkexpr(vB)));
cerion36991ef2005-09-15 12:42:16 +00007403 break;
cerion32aad402005-09-10 12:02:24 +00007404
7405 case 0x148: // vmulosh (Multiply Odd Signed Half Word, AV p212)
7406 DIP("vmulosh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00007407 putVReg( vD_addr,
7408 binop(Iop_MullEven16Sx8, mkexpr(vA), mkexpr(vB)));
cerion36991ef2005-09-15 12:42:16 +00007409 break;
cerion32aad402005-09-10 12:02:24 +00007410
7411 case 0x208: // vmuleub (Multiply Even Unsigned Byte, AV p209)
7412 DIP("vmuleub v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion24d06f12005-11-09 21:34:20 +00007413 putVReg( vD_addr, MK_Iop_MullOdd8Ux16( mkexpr(vA), mkexpr(vB) ));
cerion36991ef2005-09-15 12:42:16 +00007414 break;
cerion32aad402005-09-10 12:02:24 +00007415
7416 case 0x248: // vmuleuh (Multiply Even Unsigned Half Word, AV p210)
7417 DIP("vmuleuh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion24d06f12005-11-09 21:34:20 +00007418 putVReg( vD_addr, MK_Iop_MullOdd16Ux8( mkexpr(vA), mkexpr(vB) ));
cerion36991ef2005-09-15 12:42:16 +00007419 break;
cerion32aad402005-09-10 12:02:24 +00007420
7421 case 0x308: // vmulesb (Multiply Even Signed Byte, AV p207)
7422 DIP("vmulesb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion24d06f12005-11-09 21:34:20 +00007423 putVReg( vD_addr, MK_Iop_MullOdd8Sx16( mkexpr(vA), mkexpr(vB) ));
cerion36991ef2005-09-15 12:42:16 +00007424 break;
cerion32aad402005-09-10 12:02:24 +00007425
7426 case 0x348: // vmulesh (Multiply Even Signed Half Word, AV p208)
7427 DIP("vmulesh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion24d06f12005-11-09 21:34:20 +00007428 putVReg( vD_addr, MK_Iop_MullOdd16Sx8( mkexpr(vA), mkexpr(vB) ));
cerion36991ef2005-09-15 12:42:16 +00007429 break;
cerion32aad402005-09-10 12:02:24 +00007430
7431
7432 /* Sum Across Partial */
cerion4a49b032005-11-08 16:23:07 +00007433 case 0x608: { // vsum4ubs (Sum Partial (1/4) UB Saturate, AV p275)
7434 IRTemp aEE, aEO, aOE, aOO;
7435 aEE = aEO = aOE = aOO = IRTemp_INVALID;
cerion32aad402005-09-10 12:02:24 +00007436 DIP("vsum4ubs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion32aad402005-09-10 12:02:24 +00007437
cerion4a49b032005-11-08 16:23:07 +00007438 /* vA: V128_8Ux16 -> 4 x V128_32Ux4, sign-extended */
7439 expand8Ux16( mkexpr(vA), &aEvn, &aOdd ); // (15,13...),(14,12...)
7440 expand16Ux8( mkexpr(aEvn), &aEE, &aEO ); // (15,11...),(13, 9...)
7441 expand16Ux8( mkexpr(aOdd), &aOE, &aOO ); // (14,10...),(12, 8...)
7442
7443 /* break V128 to 4xI32's, zero-extending to I64's */
7444 breakV128to4x64U( mkexpr(aEE), &a15, &a11, &a7, &a3 );
7445 breakV128to4x64U( mkexpr(aOE), &a14, &a10, &a6, &a2 );
7446 breakV128to4x64U( mkexpr(aEO), &a13, &a9, &a5, &a1 );
7447 breakV128to4x64U( mkexpr(aOO), &a12, &a8, &a4, &a0 );
7448 breakV128to4x64U( mkexpr(vB), &b3, &b2, &b1, &b0 );
7449
7450 /* add lanes */
7451 assign( z3, binop(Iop_Add64, mkexpr(b3),
cerion5b2325f2005-12-23 00:55:09 +00007452 binop(Iop_Add64,
7453 binop(Iop_Add64, mkexpr(a15), mkexpr(a14)),
7454 binop(Iop_Add64, mkexpr(a13), mkexpr(a12)))) );
cerion4a49b032005-11-08 16:23:07 +00007455 assign( z2, binop(Iop_Add64, mkexpr(b2),
cerion5b2325f2005-12-23 00:55:09 +00007456 binop(Iop_Add64,
7457 binop(Iop_Add64, mkexpr(a11), mkexpr(a10)),
7458 binop(Iop_Add64, mkexpr(a9), mkexpr(a8)))) );
cerion4a49b032005-11-08 16:23:07 +00007459 assign( z1, binop(Iop_Add64, mkexpr(b1),
cerion5b2325f2005-12-23 00:55:09 +00007460 binop(Iop_Add64,
7461 binop(Iop_Add64, mkexpr(a7), mkexpr(a6)),
7462 binop(Iop_Add64, mkexpr(a5), mkexpr(a4)))) );
cerion4a49b032005-11-08 16:23:07 +00007463 assign( z0, binop(Iop_Add64, mkexpr(b0),
cerion5b2325f2005-12-23 00:55:09 +00007464 binop(Iop_Add64,
7465 binop(Iop_Add64, mkexpr(a3), mkexpr(a2)),
7466 binop(Iop_Add64, mkexpr(a1), mkexpr(a0)))) );
cerion4a49b032005-11-08 16:23:07 +00007467
7468 /* saturate-narrow to 32bit, and combine to V128 */
7469 putVReg( vD_addr, mkV128from4x64U( mkexpr(z3), mkexpr(z2),
7470 mkexpr(z1), mkexpr(z0)) );
7471 break;
7472 }
7473 case 0x708: { // vsum4sbs (Sum Partial (1/4) SB Saturate, AV p273)
7474 IRTemp aEE, aEO, aOE, aOO;
7475 aEE = aEO = aOE = aOO = IRTemp_INVALID;
cerion32aad402005-09-10 12:02:24 +00007476 DIP("vsum4sbs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion32aad402005-09-10 12:02:24 +00007477
cerion4a49b032005-11-08 16:23:07 +00007478 /* vA: V128_8Sx16 -> 4 x V128_32Sx4, sign-extended */
7479 expand8Sx16( mkexpr(vA), &aEvn, &aOdd ); // (15,13...),(14,12...)
7480 expand16Sx8( mkexpr(aEvn), &aEE, &aEO ); // (15,11...),(13, 9...)
7481 expand16Sx8( mkexpr(aOdd), &aOE, &aOO ); // (14,10...),(12, 8...)
7482
7483 /* break V128 to 4xI32's, sign-extending to I64's */
7484 breakV128to4x64S( mkexpr(aEE), &a15, &a11, &a7, &a3 );
7485 breakV128to4x64S( mkexpr(aOE), &a14, &a10, &a6, &a2 );
7486 breakV128to4x64S( mkexpr(aEO), &a13, &a9, &a5, &a1 );
7487 breakV128to4x64S( mkexpr(aOO), &a12, &a8, &a4, &a0 );
7488 breakV128to4x64S( mkexpr(vB), &b3, &b2, &b1, &b0 );
7489
7490 /* add lanes */
7491 assign( z3, binop(Iop_Add64, mkexpr(b3),
cerion5b2325f2005-12-23 00:55:09 +00007492 binop(Iop_Add64,
7493 binop(Iop_Add64, mkexpr(a15), mkexpr(a14)),
7494 binop(Iop_Add64, mkexpr(a13), mkexpr(a12)))) );
cerion4a49b032005-11-08 16:23:07 +00007495 assign( z2, binop(Iop_Add64, mkexpr(b2),
cerion5b2325f2005-12-23 00:55:09 +00007496 binop(Iop_Add64,
7497 binop(Iop_Add64, mkexpr(a11), mkexpr(a10)),
7498 binop(Iop_Add64, mkexpr(a9), mkexpr(a8)))) );
cerion4a49b032005-11-08 16:23:07 +00007499 assign( z1, binop(Iop_Add64, mkexpr(b1),
cerion5b2325f2005-12-23 00:55:09 +00007500 binop(Iop_Add64,
7501 binop(Iop_Add64, mkexpr(a7), mkexpr(a6)),
7502 binop(Iop_Add64, mkexpr(a5), mkexpr(a4)))) );
cerion4a49b032005-11-08 16:23:07 +00007503 assign( z0, binop(Iop_Add64, mkexpr(b0),
cerion5b2325f2005-12-23 00:55:09 +00007504 binop(Iop_Add64,
7505 binop(Iop_Add64, mkexpr(a3), mkexpr(a2)),
7506 binop(Iop_Add64, mkexpr(a1), mkexpr(a0)))) );
cerion4a49b032005-11-08 16:23:07 +00007507
7508 /* saturate-narrow to 32bit, and combine to V128 */
7509 putVReg( vD_addr, mkV128from4x64S( mkexpr(z3), mkexpr(z2),
7510 mkexpr(z1), mkexpr(z0)) );
7511 break;
7512 }
7513 case 0x648: { // vsum4shs (Sum Partial (1/4) SHW Saturate, AV p274)
cerion32aad402005-09-10 12:02:24 +00007514 DIP("vsum4shs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion32aad402005-09-10 12:02:24 +00007515
cerion4a49b032005-11-08 16:23:07 +00007516 /* vA: V128_16Sx8 -> 2 x V128_32Sx4, sign-extended */
7517 expand16Sx8( mkexpr(vA), &aEvn, &aOdd ); // (7,5...),(6,4...)
7518
7519 /* break V128 to 4xI32's, sign-extending to I64's */
7520 breakV128to4x64S( mkexpr(aEvn), &a7, &a5, &a3, &a1 );
7521 breakV128to4x64S( mkexpr(aOdd), &a6, &a4, &a2, &a0 );
7522 breakV128to4x64S( mkexpr(vB), &b3, &b2, &b1, &b0 );
7523
7524 /* add lanes */
7525 assign( z3, binop(Iop_Add64, mkexpr(b3),
7526 binop(Iop_Add64, mkexpr(a7), mkexpr(a6))));
7527 assign( z2, binop(Iop_Add64, mkexpr(b2),
7528 binop(Iop_Add64, mkexpr(a5), mkexpr(a4))));
7529 assign( z1, binop(Iop_Add64, mkexpr(b1),
7530 binop(Iop_Add64, mkexpr(a3), mkexpr(a2))));
7531 assign( z0, binop(Iop_Add64, mkexpr(b0),
7532 binop(Iop_Add64, mkexpr(a1), mkexpr(a0))));
7533
7534 /* saturate-narrow to 32bit, and combine to V128 */
7535 putVReg( vD_addr, mkV128from4x64S( mkexpr(z3), mkexpr(z2),
7536 mkexpr(z1), mkexpr(z0)) );
7537 break;
7538 }
7539 case 0x688: { // vsum2sws (Sum Partial (1/2) SW Saturate, AV p272)
cerion32aad402005-09-10 12:02:24 +00007540 DIP("vsum2sws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion32aad402005-09-10 12:02:24 +00007541
cerion4a49b032005-11-08 16:23:07 +00007542 /* break V128 to 4xI32's, sign-extending to I64's */
7543 breakV128to4x64S( mkexpr(vA), &a3, &a2, &a1, &a0 );
7544 breakV128to4x64S( mkexpr(vB), &b3, &b2, &b1, &b0 );
7545
7546 /* add lanes */
7547 assign( z2, binop(Iop_Add64, mkexpr(b2),
7548 binop(Iop_Add64, mkexpr(a3), mkexpr(a2))) );
7549 assign( z0, binop(Iop_Add64, mkexpr(b0),
7550 binop(Iop_Add64, mkexpr(a1), mkexpr(a0))) );
7551
7552 /* saturate-narrow to 32bit, and combine to V128 */
7553 putVReg( vD_addr, mkV128from4x64S( mkU64(0), mkexpr(z2),
7554 mkU64(0), mkexpr(z0)) );
7555 break;
7556 }
7557 case 0x788: { // vsumsws (Sum SW Saturate, AV p271)
cerion32aad402005-09-10 12:02:24 +00007558 DIP("vsumsws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion32aad402005-09-10 12:02:24 +00007559
cerion4a49b032005-11-08 16:23:07 +00007560 /* break V128 to 4xI32's, sign-extending to I64's */
7561 breakV128to4x64S( mkexpr(vA), &a3, &a2, &a1, &a0 );
7562 breakV128to4x64S( mkexpr(vB), &b3, &b2, &b1, &b0 );
7563
7564 /* add lanes */
7565 assign( z0, binop(Iop_Add64, mkexpr(b0),
cerion5b2325f2005-12-23 00:55:09 +00007566 binop(Iop_Add64,
7567 binop(Iop_Add64, mkexpr(a3), mkexpr(a2)),
7568 binop(Iop_Add64, mkexpr(a1), mkexpr(a0)))) );
cerion4a49b032005-11-08 16:23:07 +00007569
7570 /* saturate-narrow to 32bit, and combine to V128 */
7571 putVReg( vD_addr, mkV128from4x64S( mkU64(0), mkU64(0),
7572 mkU64(0), mkexpr(z0)) );
7573 break;
7574 }
cerion32aad402005-09-10 12:02:24 +00007575 default:
cerion5b2325f2005-12-23 00:55:09 +00007576 vex_printf("dis_av_arith(ppc)(opc2=0x%x)\n", opc2);
cerion32aad402005-09-10 12:02:24 +00007577 return False;
7578 }
7579 return True;
7580}
7581
7582/*
7583 AltiVec Logic Instructions
7584*/
7585static Bool dis_av_logic ( UInt theInstr )
7586{
cerion76de5cf2005-11-18 18:25:12 +00007587 /* VX-Form */
7588 UChar opc1 = ifieldOPC(theInstr);
7589 UChar vD_addr = ifieldRegDS(theInstr);
7590 UChar vA_addr = ifieldRegA(theInstr);
7591 UChar vB_addr = ifieldRegB(theInstr);
7592 UInt opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00007593
cerion225a0342005-09-12 20:49:09 +00007594 IRTemp vA = newTemp(Ity_V128);
7595 IRTemp vB = newTemp(Ity_V128);
7596 assign( vA, getVReg(vA_addr));
7597 assign( vB, getVReg(vB_addr));
7598
cerion32aad402005-09-10 12:02:24 +00007599 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00007600 vex_printf("dis_av_logic(ppc)(opc1 != 0x4)\n");
cerion32aad402005-09-10 12:02:24 +00007601 return False;
7602 }
7603
7604 switch (opc2) {
7605 case 0x404: // vand (And, AV p147)
7606 DIP("vand v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion225a0342005-09-12 20:49:09 +00007607 putVReg( vD_addr, binop(Iop_AndV128, mkexpr(vA), mkexpr(vB)) );
7608 break;
cerion32aad402005-09-10 12:02:24 +00007609
7610 case 0x444: // vandc (And, AV p148)
7611 DIP("vandc v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion6e7a0ea2005-09-13 13:34:09 +00007612 putVReg( vD_addr, binop(Iop_AndV128, mkexpr(vA),
cerion76de5cf2005-11-18 18:25:12 +00007613 unop(Iop_NotV128, mkexpr(vB))) );
cerion6e7a0ea2005-09-13 13:34:09 +00007614 break;
cerion32aad402005-09-10 12:02:24 +00007615
7616 case 0x484: // vor (Or, AV p217)
7617 DIP("vor v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion225a0342005-09-12 20:49:09 +00007618 putVReg( vD_addr, binop(Iop_OrV128, mkexpr(vA), mkexpr(vB)) );
7619 break;
cerion32aad402005-09-10 12:02:24 +00007620
7621 case 0x4C4: // vxor (Xor, AV p282)
7622 DIP("vxor v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion225a0342005-09-12 20:49:09 +00007623 putVReg( vD_addr, binop(Iop_XorV128, mkexpr(vA), mkexpr(vB)) );
7624 break;
cerion32aad402005-09-10 12:02:24 +00007625
7626 case 0x504: // vnor (Nor, AV p216)
7627 DIP("vnor v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion6e7a0ea2005-09-13 13:34:09 +00007628 putVReg( vD_addr,
7629 unop(Iop_NotV128, binop(Iop_OrV128, mkexpr(vA), mkexpr(vB))) );
7630 break;
cerion32aad402005-09-10 12:02:24 +00007631
7632 default:
cerion5b2325f2005-12-23 00:55:09 +00007633 vex_printf("dis_av_logic(ppc)(opc2=0x%x)\n", opc2);
cerion32aad402005-09-10 12:02:24 +00007634 return False;
7635 }
7636 return True;
7637}
7638
7639/*
7640 AltiVec Compare Instructions
7641*/
7642static Bool dis_av_cmp ( UInt theInstr )
7643{
cerion76de5cf2005-11-18 18:25:12 +00007644 /* VXR-Form */
7645 UChar opc1 = ifieldOPC(theInstr);
7646 UChar vD_addr = ifieldRegDS(theInstr);
7647 UChar vA_addr = ifieldRegA(theInstr);
7648 UChar vB_addr = ifieldRegB(theInstr);
7649 UChar flag_rC = ifieldBIT10(theInstr);
7650 UInt opc2 = IFIELD( theInstr, 0, 10 );
cerion32aad402005-09-10 12:02:24 +00007651
cerion0c439222005-09-15 14:22:58 +00007652 IRTemp vA = newTemp(Ity_V128);
7653 IRTemp vB = newTemp(Ity_V128);
7654 IRTemp vD = newTemp(Ity_V128);
7655 assign( vA, getVReg(vA_addr));
7656 assign( vB, getVReg(vB_addr));
7657
cerion32aad402005-09-10 12:02:24 +00007658 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00007659 vex_printf("dis_av_cmp(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00007660 return False;
7661 }
7662
7663 switch (opc2) {
7664 case 0x006: // vcmpequb (Compare Equal-to Unsigned B, AV p160)
cerion5b2325f2005-12-23 00:55:09 +00007665 DIP("vcmpequb%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7666 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007667 assign( vD, binop(Iop_CmpEQ8x16, mkexpr(vA), mkexpr(vB)) );
7668 break;
cerion32aad402005-09-10 12:02:24 +00007669
7670 case 0x046: // vcmpequh (Compare Equal-to Unsigned HW, AV p161)
cerion5b2325f2005-12-23 00:55:09 +00007671 DIP("vcmpequh%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7672 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007673 assign( vD, binop(Iop_CmpEQ16x8, mkexpr(vA), mkexpr(vB)) );
7674 break;
cerion32aad402005-09-10 12:02:24 +00007675
7676 case 0x086: // vcmpequw (Compare Equal-to Unsigned W, AV p162)
cerion5b2325f2005-12-23 00:55:09 +00007677 DIP("vcmpequw%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7678 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007679 assign( vD, binop(Iop_CmpEQ32x4, mkexpr(vA), mkexpr(vB)) );
7680 break;
cerion32aad402005-09-10 12:02:24 +00007681
7682 case 0x206: // vcmpgtub (Compare Greater-than Unsigned B, AV p168)
cerion5b2325f2005-12-23 00:55:09 +00007683 DIP("vcmpgtub%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7684 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007685 assign( vD, binop(Iop_CmpGT8Ux16, mkexpr(vA), mkexpr(vB)) );
7686 break;
cerion32aad402005-09-10 12:02:24 +00007687
7688 case 0x246: // vcmpgtuh (Compare Greater-than Unsigned HW, AV p169)
cerion5b2325f2005-12-23 00:55:09 +00007689 DIP("vcmpgtuh%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7690 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007691 assign( vD, binop(Iop_CmpGT16Ux8, mkexpr(vA), mkexpr(vB)) );
7692 break;
cerion32aad402005-09-10 12:02:24 +00007693
7694 case 0x286: // vcmpgtuw (Compare Greater-than Unsigned W, AV p170)
cerion5b2325f2005-12-23 00:55:09 +00007695 DIP("vcmpgtuw%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7696 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007697 assign( vD, binop(Iop_CmpGT32Ux4, mkexpr(vA), mkexpr(vB)) );
7698 break;
cerion32aad402005-09-10 12:02:24 +00007699
7700 case 0x306: // vcmpgtsb (Compare Greater-than Signed B, AV p165)
cerion5b2325f2005-12-23 00:55:09 +00007701 DIP("vcmpgtsb%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7702 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007703 assign( vD, binop(Iop_CmpGT8Sx16, mkexpr(vA), mkexpr(vB)) );
7704 break;
cerion32aad402005-09-10 12:02:24 +00007705
7706 case 0x346: // vcmpgtsh (Compare Greater-than Signed HW, AV p166)
cerion5b2325f2005-12-23 00:55:09 +00007707 DIP("vcmpgtsh%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7708 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007709 assign( vD, binop(Iop_CmpGT16Sx8, mkexpr(vA), mkexpr(vB)) );
7710 break;
cerion32aad402005-09-10 12:02:24 +00007711
7712 case 0x386: // vcmpgtsw (Compare Greater-than Signed W, AV p167)
cerion5b2325f2005-12-23 00:55:09 +00007713 DIP("vcmpgtsw%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7714 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007715 assign( vD, binop(Iop_CmpGT32Sx4, mkexpr(vA), mkexpr(vB)) );
7716 break;
cerion32aad402005-09-10 12:02:24 +00007717
7718 default:
cerion5b2325f2005-12-23 00:55:09 +00007719 vex_printf("dis_av_cmp(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00007720 return False;
7721 }
cerion0c439222005-09-15 14:22:58 +00007722
7723 putVReg( vD_addr, mkexpr(vD) );
7724
cerion76de5cf2005-11-18 18:25:12 +00007725 if (flag_rC) {
cerion8ea0d3e2005-11-14 00:44:47 +00007726 set_AV_CR6( mkexpr(vD), True );
cerion0c439222005-09-15 14:22:58 +00007727 }
cerion32aad402005-09-10 12:02:24 +00007728 return True;
7729}
7730
7731/*
7732 AltiVec Multiply-Sum Instructions
7733*/
7734static Bool dis_av_multarith ( UInt theInstr )
7735{
cerion76de5cf2005-11-18 18:25:12 +00007736 /* VA-Form */
7737 UChar opc1 = ifieldOPC(theInstr);
7738 UChar vD_addr = ifieldRegDS(theInstr);
7739 UChar vA_addr = ifieldRegA(theInstr);
7740 UChar vB_addr = ifieldRegB(theInstr);
7741 UChar vC_addr = ifieldRegC(theInstr);
7742 UChar opc2 = toUChar( IFIELD( theInstr, 0, 6 ) );
cerion32aad402005-09-10 12:02:24 +00007743
cerion4a49b032005-11-08 16:23:07 +00007744 IRTemp vA = newTemp(Ity_V128);
7745 IRTemp vB = newTemp(Ity_V128);
7746 IRTemp vC = newTemp(Ity_V128);
sewardj197bd172005-10-12 11:34:33 +00007747 IRTemp zeros = newTemp(Ity_V128);
cerion4a49b032005-11-08 16:23:07 +00007748 IRTemp aLo = newTemp(Ity_V128);
7749 IRTemp bLo = newTemp(Ity_V128);
7750 IRTemp cLo = newTemp(Ity_V128);
7751 IRTemp zLo = newTemp(Ity_V128);
7752 IRTemp aHi = newTemp(Ity_V128);
7753 IRTemp bHi = newTemp(Ity_V128);
7754 IRTemp cHi = newTemp(Ity_V128);
7755 IRTemp zHi = newTemp(Ity_V128);
7756 IRTemp abEvn = newTemp(Ity_V128);
7757 IRTemp abOdd = newTemp(Ity_V128);
7758 IRTemp z3 = newTemp(Ity_I64);
7759 IRTemp z2 = newTemp(Ity_I64);
7760 IRTemp z1 = newTemp(Ity_I64);
7761 IRTemp z0 = newTemp(Ity_I64);
7762 IRTemp ab7, ab6, ab5, ab4, ab3, ab2, ab1, ab0;
7763 IRTemp c3, c2, c1, c0;
7764
7765 ab7 = ab6 = ab5 = ab4 = ab3 = ab2 = ab1 = ab0 = IRTemp_INVALID;
7766 c3 = c2 = c1 = c0 = IRTemp_INVALID;
7767
cerion6f1cc0f2005-09-16 16:02:11 +00007768 assign( vA, getVReg(vA_addr));
7769 assign( vB, getVReg(vB_addr));
7770 assign( vC, getVReg(vC_addr));
cerion4a49b032005-11-08 16:23:07 +00007771 assign( zeros, unop(Iop_Dup32x4, mkU32(0)) );
cerion6f1cc0f2005-09-16 16:02:11 +00007772
cerion32aad402005-09-10 12:02:24 +00007773 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00007774 vex_printf("dis_av_multarith(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00007775 return False;
7776 }
7777
7778 switch (opc2) {
cerion32aad402005-09-10 12:02:24 +00007779 /* Multiply-Add */
cerion5b2325f2005-12-23 00:55:09 +00007780 case 0x20: { // vmhaddshs (Mult Hi, Add Signed HW Saturate, AV p185)
cerion6f1cc0f2005-09-16 16:02:11 +00007781 IRTemp cSigns = newTemp(Ity_V128);
cerion5b2325f2005-12-23 00:55:09 +00007782 DIP("vmhaddshs v%d,v%d,v%d,v%d\n",
7783 vD_addr, vA_addr, vB_addr, vC_addr);
7784 assign(cSigns, binop(Iop_CmpGT16Sx8, mkexpr(zeros), mkexpr(vC)));
7785 assign(aLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vA)));
7786 assign(bLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vB)));
7787 assign(cLo, binop(Iop_InterleaveLO16x8, mkexpr(cSigns),mkexpr(vC)));
7788 assign(aHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vA)));
7789 assign(bHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vB)));
7790 assign(cHi, binop(Iop_InterleaveHI16x8, mkexpr(cSigns),mkexpr(vC)));
cerion32aad402005-09-10 12:02:24 +00007791
cerion24d06f12005-11-09 21:34:20 +00007792 assign( zLo, binop(Iop_Add32x4, mkexpr(cLo),
cerion6f1cc0f2005-09-16 16:02:11 +00007793 binop(Iop_SarN32x4,
cerion1ac656a2005-11-04 19:44:48 +00007794 binop(Iop_MullEven16Sx8,
cerion24d06f12005-11-09 21:34:20 +00007795 mkexpr(aLo), mkexpr(bLo)),
7796 mkU8(15))) );
cerion6f1cc0f2005-09-16 16:02:11 +00007797
cerion24d06f12005-11-09 21:34:20 +00007798 assign( zHi, binop(Iop_Add32x4, mkexpr(cHi),
cerion6f1cc0f2005-09-16 16:02:11 +00007799 binop(Iop_SarN32x4,
cerion1ac656a2005-11-04 19:44:48 +00007800 binop(Iop_MullEven16Sx8,
cerion24d06f12005-11-09 21:34:20 +00007801 mkexpr(aHi), mkexpr(bHi)),
7802 mkU8(15))) );
cerion6f1cc0f2005-09-16 16:02:11 +00007803
cerion5b2325f2005-12-23 00:55:09 +00007804 putVReg( vD_addr,
7805 binop(Iop_QNarrow32Sx4, mkexpr(zHi), mkexpr(zLo)) );
cerion6f1cc0f2005-09-16 16:02:11 +00007806 break;
7807 }
cerion5b2325f2005-12-23 00:55:09 +00007808 case 0x21: { // vmhraddshs (Mult High Round, Add Signed HW Saturate, AV p186)
cerion6f1cc0f2005-09-16 16:02:11 +00007809 IRTemp zKonst = newTemp(Ity_V128);
cerion6f1cc0f2005-09-16 16:02:11 +00007810 IRTemp cSigns = newTemp(Ity_V128);
cerion5b2325f2005-12-23 00:55:09 +00007811 DIP("vmhraddshs v%d,v%d,v%d,v%d\n",
7812 vD_addr, vA_addr, vB_addr, vC_addr);
7813 assign(cSigns, binop(Iop_CmpGT16Sx8, mkexpr(zeros), mkexpr(vC)) );
7814 assign(aLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vA)));
7815 assign(bLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vB)));
7816 assign(cLo, binop(Iop_InterleaveLO16x8, mkexpr(cSigns),mkexpr(vC)));
7817 assign(aHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vA)));
7818 assign(bHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vB)));
7819 assign(cHi, binop(Iop_InterleaveHI16x8, mkexpr(cSigns),mkexpr(vC)));
cerion32aad402005-09-10 12:02:24 +00007820
cerion6f1cc0f2005-09-16 16:02:11 +00007821 /* shifting our const avoids store/load version of Dup */
cerion4a49b032005-11-08 16:23:07 +00007822 assign( zKonst, binop(Iop_ShlN32x4, unop(Iop_Dup32x4, mkU32(0x1)),
7823 mkU8(14)) );
cerion6f1cc0f2005-09-16 16:02:11 +00007824
cerion24d06f12005-11-09 21:34:20 +00007825 assign( zLo, binop(Iop_Add32x4, mkexpr(cLo),
cerion6f1cc0f2005-09-16 16:02:11 +00007826 binop(Iop_SarN32x4,
7827 binop(Iop_Add32x4, mkexpr(zKonst),
cerion1ac656a2005-11-04 19:44:48 +00007828 binop(Iop_MullEven16Sx8,
cerion24d06f12005-11-09 21:34:20 +00007829 mkexpr(aLo), mkexpr(bLo))),
7830 mkU8(15))) );
cerion6f1cc0f2005-09-16 16:02:11 +00007831
cerion24d06f12005-11-09 21:34:20 +00007832 assign( zHi, binop(Iop_Add32x4, mkexpr(cHi),
cerion6f1cc0f2005-09-16 16:02:11 +00007833 binop(Iop_SarN32x4,
7834 binop(Iop_Add32x4, mkexpr(zKonst),
cerion1ac656a2005-11-04 19:44:48 +00007835 binop(Iop_MullEven16Sx8,
cerion24d06f12005-11-09 21:34:20 +00007836 mkexpr(aHi), mkexpr(bHi))),
7837 mkU8(15))) );
cerion6f1cc0f2005-09-16 16:02:11 +00007838
7839 putVReg( vD_addr, binop(Iop_QNarrow32Sx4, mkexpr(zHi), mkexpr(zLo)) );
7840 break;
7841 }
cerion5b2325f2005-12-23 00:55:09 +00007842 case 0x22: { // vmladduhm (Mult Low, Add Unsigned HW Modulo, AV p194)
7843 DIP("vmladduhm v%d,v%d,v%d,v%d\n",
7844 vD_addr, vA_addr, vB_addr, vC_addr);
7845 assign(aLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vA)));
7846 assign(bLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vB)));
7847 assign(cLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vC)));
7848 assign(aHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vA)));
7849 assign(bHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vB)));
7850 assign(cHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vC)));
7851 assign(zLo, binop(Iop_Add32x4,
7852 binop(Iop_MullEven16Ux8, mkexpr(aLo), mkexpr(bLo)),
7853 mkexpr(cLo)) );
7854 assign(zHi, binop(Iop_Add32x4,
7855 binop(Iop_MullEven16Ux8, mkexpr(aHi), mkexpr(bHi)),
7856 mkexpr(cHi)));
7857 putVReg(vD_addr, binop(Iop_Narrow32x4, mkexpr(zHi), mkexpr(zLo)));
cerion6f1cc0f2005-09-16 16:02:11 +00007858 break;
7859 }
cerion32aad402005-09-10 12:02:24 +00007860
7861
7862 /* Multiply-Sum */
cerion6f1cc0f2005-09-16 16:02:11 +00007863 case 0x24: { // vmsumubm (Multiply Sum Unsigned B Modulo, AV p204)
cerion4a49b032005-11-08 16:23:07 +00007864 IRTemp abEE, abEO, abOE, abOO;
7865 abEE = abEO = abOE = abOO = IRTemp_INVALID;
cerion5b2325f2005-12-23 00:55:09 +00007866 DIP("vmsumubm v%d,v%d,v%d,v%d\n",
7867 vD_addr, vA_addr, vB_addr, vC_addr);
cerion32aad402005-09-10 12:02:24 +00007868
cerion4a49b032005-11-08 16:23:07 +00007869 /* multiply vA,vB (unsigned, widening) */
cerion24d06f12005-11-09 21:34:20 +00007870 assign( abEvn, MK_Iop_MullOdd8Ux16( mkexpr(vA), mkexpr(vB) ));
7871 assign( abOdd, binop(Iop_MullEven8Ux16, mkexpr(vA), mkexpr(vB)) );
cerion4a49b032005-11-08 16:23:07 +00007872
7873 /* evn,odd: V128_16Ux8 -> 2 x V128_32Ux4, zero-extended */
7874 expand16Ux8( mkexpr(abEvn), &abEE, &abEO );
7875 expand16Ux8( mkexpr(abOdd), &abOE, &abOO );
7876
cerion6f1cc0f2005-09-16 16:02:11 +00007877 putVReg( vD_addr,
cerion5b2325f2005-12-23 00:55:09 +00007878 binop(Iop_Add32x4, mkexpr(vC),
7879 binop(Iop_Add32x4,
7880 binop(Iop_Add32x4, mkexpr(abEE), mkexpr(abEO)),
7881 binop(Iop_Add32x4, mkexpr(abOE), mkexpr(abOO)))) );
cerion6f1cc0f2005-09-16 16:02:11 +00007882 break;
7883 }
cerion4a49b032005-11-08 16:23:07 +00007884 case 0x25: { // vmsummbm (Multiply Sum Mixed-Sign B Modulo, AV p201)
7885 IRTemp aEvn, aOdd, bEvn, bOdd;
7886 IRTemp abEE = newTemp(Ity_V128);
7887 IRTemp abEO = newTemp(Ity_V128);
7888 IRTemp abOE = newTemp(Ity_V128);
7889 IRTemp abOO = newTemp(Ity_V128);
7890 aEvn = aOdd = bEvn = bOdd = IRTemp_INVALID;
cerion5b2325f2005-12-23 00:55:09 +00007891 DIP("vmsummbm v%d,v%d,v%d,v%d\n",
7892 vD_addr, vA_addr, vB_addr, vC_addr);
cerion32aad402005-09-10 12:02:24 +00007893
cerion4a49b032005-11-08 16:23:07 +00007894 /* sign-extend vA, zero-extend vB, for mixed-sign multiply
7895 (separating out adjacent lanes to different vectors) */
7896 expand8Sx16( mkexpr(vA), &aEvn, &aOdd );
7897 expand8Ux16( mkexpr(vB), &bEvn, &bOdd );
7898
7899 /* multiply vA, vB, again separating adjacent lanes */
cerion24d06f12005-11-09 21:34:20 +00007900 assign( abEE, MK_Iop_MullOdd16Sx8( mkexpr(aEvn), mkexpr(bEvn) ));
7901 assign( abEO, binop(Iop_MullEven16Sx8, mkexpr(aEvn), mkexpr(bEvn)) );
7902 assign( abOE, MK_Iop_MullOdd16Sx8( mkexpr(aOdd), mkexpr(bOdd) ));
7903 assign( abOO, binop(Iop_MullEven16Sx8, mkexpr(aOdd), mkexpr(bOdd)) );
cerion4a49b032005-11-08 16:23:07 +00007904
7905 /* add results together, + vC */
7906 putVReg( vD_addr,
cerion5b2325f2005-12-23 00:55:09 +00007907 binop(Iop_QAdd32Sx4, mkexpr(vC),
7908 binop(Iop_QAdd32Sx4,
7909 binop(Iop_QAdd32Sx4, mkexpr(abEE), mkexpr(abEO)),
7910 binop(Iop_QAdd32Sx4, mkexpr(abOE), mkexpr(abOO)))) );
cerion4a49b032005-11-08 16:23:07 +00007911 break;
7912 }
cerion6f1cc0f2005-09-16 16:02:11 +00007913 case 0x26: { // vmsumuhm (Multiply Sum Unsigned HW Modulo, AV p205)
cerion5b2325f2005-12-23 00:55:09 +00007914 DIP("vmsumuhm v%d,v%d,v%d,v%d\n",
7915 vD_addr, vA_addr, vB_addr, vC_addr);
cerion24d06f12005-11-09 21:34:20 +00007916 assign( abEvn, MK_Iop_MullOdd16Ux8( mkexpr(vA), mkexpr(vB) ));
7917 assign( abOdd, binop(Iop_MullEven16Ux8, mkexpr(vA), mkexpr(vB)) );
cerion6f1cc0f2005-09-16 16:02:11 +00007918 putVReg( vD_addr,
cerion5b2325f2005-12-23 00:55:09 +00007919 binop(Iop_Add32x4, mkexpr(vC),
7920 binop(Iop_Add32x4, mkexpr(abEvn), mkexpr(abOdd))) );
cerion6f1cc0f2005-09-16 16:02:11 +00007921 break;
7922 }
cerion4a49b032005-11-08 16:23:07 +00007923 case 0x27: { // vmsumuhs (Multiply Sum Unsigned HW Saturate, AV p206)
cerion5b2325f2005-12-23 00:55:09 +00007924 DIP("vmsumuhs v%d,v%d,v%d,v%d\n",
7925 vD_addr, vA_addr, vB_addr, vC_addr);
cerion4a49b032005-11-08 16:23:07 +00007926 /* widening multiply, separating lanes */
cerion24d06f12005-11-09 21:34:20 +00007927 assign( abEvn, MK_Iop_MullOdd16Ux8(mkexpr(vA), mkexpr(vB) ));
7928 assign( abOdd, binop(Iop_MullEven16Ux8, mkexpr(vA), mkexpr(vB)) );
cerion32aad402005-09-10 12:02:24 +00007929
cerion4a49b032005-11-08 16:23:07 +00007930 /* break V128 to 4xI32's, zero-extending to I64's */
7931 breakV128to4x64U( mkexpr(abEvn), &ab7, &ab5, &ab3, &ab1 );
7932 breakV128to4x64U( mkexpr(abOdd), &ab6, &ab4, &ab2, &ab0 );
7933 breakV128to4x64U( mkexpr(vC), &c3, &c2, &c1, &c0 );
7934
7935 /* add lanes */
7936 assign( z3, binop(Iop_Add64, mkexpr(c3),
7937 binop(Iop_Add64, mkexpr(ab7), mkexpr(ab6))));
7938 assign( z2, binop(Iop_Add64, mkexpr(c2),
7939 binop(Iop_Add64, mkexpr(ab5), mkexpr(ab4))));
7940 assign( z1, binop(Iop_Add64, mkexpr(c1),
7941 binop(Iop_Add64, mkexpr(ab3), mkexpr(ab2))));
7942 assign( z0, binop(Iop_Add64, mkexpr(c0),
7943 binop(Iop_Add64, mkexpr(ab1), mkexpr(ab0))));
7944
7945 /* saturate-narrow to 32bit, and combine to V128 */
7946 putVReg( vD_addr, mkV128from4x64U( mkexpr(z3), mkexpr(z2),
7947 mkexpr(z1), mkexpr(z0)) );
7948
cerion6f1cc0f2005-09-16 16:02:11 +00007949 break;
7950 }
cerion4a49b032005-11-08 16:23:07 +00007951 case 0x28: { // vmsumshm (Multiply Sum Signed HW Modulo, AV p202)
cerion5b2325f2005-12-23 00:55:09 +00007952 DIP("vmsumshm v%d,v%d,v%d,v%d\n",
7953 vD_addr, vA_addr, vB_addr, vC_addr);
cerion24d06f12005-11-09 21:34:20 +00007954 assign( abEvn, MK_Iop_MullOdd16Sx8( mkexpr(vA), mkexpr(vB) ));
7955 assign( abOdd, binop(Iop_MullEven16Sx8, mkexpr(vA), mkexpr(vB)) );
cerion4a49b032005-11-08 16:23:07 +00007956 putVReg( vD_addr,
cerion5b2325f2005-12-23 00:55:09 +00007957 binop(Iop_Add32x4, mkexpr(vC),
7958 binop(Iop_Add32x4, mkexpr(abOdd), mkexpr(abEvn))) );
cerion4a49b032005-11-08 16:23:07 +00007959 break;
7960 }
7961 case 0x29: { // vmsumshs (Multiply Sum Signed HW Saturate, AV p203)
cerion5b2325f2005-12-23 00:55:09 +00007962 DIP("vmsumshs v%d,v%d,v%d,v%d\n",
7963 vD_addr, vA_addr, vB_addr, vC_addr);
cerion4a49b032005-11-08 16:23:07 +00007964 /* widening multiply, separating lanes */
cerion24d06f12005-11-09 21:34:20 +00007965 assign( abEvn, MK_Iop_MullOdd16Sx8( mkexpr(vA), mkexpr(vB) ));
7966 assign( abOdd, binop(Iop_MullEven16Sx8, mkexpr(vA), mkexpr(vB)) );
cerion32aad402005-09-10 12:02:24 +00007967
cerion4a49b032005-11-08 16:23:07 +00007968 /* break V128 to 4xI32's, sign-extending to I64's */
7969 breakV128to4x64S( mkexpr(abEvn), &ab7, &ab5, &ab3, &ab1 );
7970 breakV128to4x64S( mkexpr(abOdd), &ab6, &ab4, &ab2, &ab0 );
7971 breakV128to4x64S( mkexpr(vC), &c3, &c2, &c1, &c0 );
7972
7973 /* add lanes */
7974 assign( z3, binop(Iop_Add64, mkexpr(c3),
7975 binop(Iop_Add64, mkexpr(ab7), mkexpr(ab6))));
7976 assign( z2, binop(Iop_Add64, mkexpr(c2),
7977 binop(Iop_Add64, mkexpr(ab5), mkexpr(ab4))));
7978 assign( z1, binop(Iop_Add64, mkexpr(c1),
7979 binop(Iop_Add64, mkexpr(ab3), mkexpr(ab2))));
7980 assign( z0, binop(Iop_Add64, mkexpr(c0),
7981 binop(Iop_Add64, mkexpr(ab1), mkexpr(ab0))));
7982
7983 /* saturate-narrow to 32bit, and combine to V128 */
7984 putVReg( vD_addr, mkV128from4x64S( mkexpr(z3), mkexpr(z2),
7985 mkexpr(z1), mkexpr(z0)) );
7986 break;
7987 }
cerion32aad402005-09-10 12:02:24 +00007988 default:
cerion5b2325f2005-12-23 00:55:09 +00007989 vex_printf("dis_av_multarith(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00007990 return False;
7991 }
7992 return True;
7993}
7994
7995/*
7996 AltiVec Shift/Rotate Instructions
7997*/
7998static Bool dis_av_shift ( UInt theInstr )
7999{
cerion76de5cf2005-11-18 18:25:12 +00008000 /* VX-Form */
8001 UChar opc1 = ifieldOPC(theInstr);
8002 UChar vD_addr = ifieldRegDS(theInstr);
8003 UChar vA_addr = ifieldRegA(theInstr);
8004 UChar vB_addr = ifieldRegB(theInstr);
8005 UInt opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00008006
cerion27b3d7e2005-09-14 20:35:47 +00008007 IRTemp vA = newTemp(Ity_V128);
8008 IRTemp vB = newTemp(Ity_V128);
8009 assign( vA, getVReg(vA_addr));
8010 assign( vB, getVReg(vB_addr));
8011
cerion32aad402005-09-10 12:02:24 +00008012 if (opc1 != 0x4){
cerion5b2325f2005-12-23 00:55:09 +00008013 vex_printf("dis_av_shift(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00008014 return False;
8015 }
8016
8017 switch (opc2) {
8018 /* Rotate */
8019 case 0x004: // vrlb (Rotate Left Integer B, AV p234)
8020 DIP("vrlb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
sewardj1bee5612005-11-10 18:10:58 +00008021 putVReg( vD_addr, binop(Iop_Rol8x16, mkexpr(vA), mkexpr(vB)) );
cerion0a7b4f42005-09-16 07:54:40 +00008022 break;
cerion32aad402005-09-10 12:02:24 +00008023
8024 case 0x044: // vrlh (Rotate Left Integer HW, AV p235)
8025 DIP("vrlh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
sewardj1bee5612005-11-10 18:10:58 +00008026 putVReg( vD_addr, binop(Iop_Rol16x8, mkexpr(vA), mkexpr(vB)) );
cerion0a7b4f42005-09-16 07:54:40 +00008027 break;
cerion32aad402005-09-10 12:02:24 +00008028
8029 case 0x084: // vrlw (Rotate Left Integer W, AV p236)
8030 DIP("vrlw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
sewardj1bee5612005-11-10 18:10:58 +00008031 putVReg( vD_addr, binop(Iop_Rol32x4, mkexpr(vA), mkexpr(vB)) );
cerion0a7b4f42005-09-16 07:54:40 +00008032 break;
cerion32aad402005-09-10 12:02:24 +00008033
8034
8035 /* Shift Left */
8036 case 0x104: // vslb (Shift Left Integer B, AV p240)
8037 DIP("vslb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00008038 putVReg( vD_addr, binop(Iop_Shl8x16, mkexpr(vA), mkexpr(vB)) );
8039 break;
cerion32aad402005-09-10 12:02:24 +00008040
8041 case 0x144: // vslh (Shift Left Integer HW, AV p242)
8042 DIP("vslh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00008043 putVReg( vD_addr, binop(Iop_Shl16x8, mkexpr(vA), mkexpr(vB)) );
8044 break;
cerion32aad402005-09-10 12:02:24 +00008045
8046 case 0x184: // vslw (Shift Left Integer W, AV p244)
8047 DIP("vslw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00008048 putVReg( vD_addr, binop(Iop_Shl32x4, mkexpr(vA), mkexpr(vB)) );
8049 break;
cerion32aad402005-09-10 12:02:24 +00008050
cerion0a7b4f42005-09-16 07:54:40 +00008051 case 0x1C4: { // vsl (Shift Left, AV p239)
cerion0a7b4f42005-09-16 07:54:40 +00008052 IRTemp sh = newTemp(Ity_I8);
sewardj197bd172005-10-12 11:34:33 +00008053 DIP("vsl v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00008054 assign( sh, binop(Iop_And8, mkU8(0x7),
8055 unop(Iop_32to8,
8056 unop(Iop_V128to32, mkexpr(vB)))) );
8057 putVReg( vD_addr,
8058 binop(Iop_ShlV128, mkexpr(vA), mkexpr(sh)) );
8059 break;
8060 }
8061 case 0x40C: { // vslo (Shift Left by Octet, AV p243)
cerion0a7b4f42005-09-16 07:54:40 +00008062 IRTemp sh = newTemp(Ity_I8);
sewardj197bd172005-10-12 11:34:33 +00008063 DIP("vslo v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00008064 assign( sh, binop(Iop_And8, mkU8(0x78),
8065 unop(Iop_32to8,
8066 unop(Iop_V128to32, mkexpr(vB)))) );
8067 putVReg( vD_addr,
8068 binop(Iop_ShlV128, mkexpr(vA), mkexpr(sh)) );
8069 break;
8070 }
8071
cerion32aad402005-09-10 12:02:24 +00008072
8073 /* Shift Right */
8074 case 0x204: // vsrb (Shift Right B, AV p256)
8075 DIP("vsrb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00008076 putVReg( vD_addr, binop(Iop_Shr8x16, mkexpr(vA), mkexpr(vB)) );
8077 break;
cerion32aad402005-09-10 12:02:24 +00008078
8079 case 0x244: // vsrh (Shift Right HW, AV p257)
8080 DIP("vsrh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00008081 putVReg( vD_addr, binop(Iop_Shr16x8, mkexpr(vA), mkexpr(vB)) );
8082 break;
cerion32aad402005-09-10 12:02:24 +00008083
8084 case 0x284: // vsrw (Shift Right W, AV p259)
8085 DIP("vsrw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00008086 putVReg( vD_addr, binop(Iop_Shr32x4, mkexpr(vA), mkexpr(vB)) );
8087 break;
cerion32aad402005-09-10 12:02:24 +00008088
cerion27b3d7e2005-09-14 20:35:47 +00008089 case 0x2C4: { // vsr (Shift Right, AV p251)
cerion27b3d7e2005-09-14 20:35:47 +00008090 IRTemp sh = newTemp(Ity_I8);
sewardj197bd172005-10-12 11:34:33 +00008091 DIP("vsr v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion27b3d7e2005-09-14 20:35:47 +00008092 assign( sh, binop(Iop_And8, mkU8(0x7),
8093 unop(Iop_32to8,
8094 unop(Iop_V128to32, mkexpr(vB)))) );
8095 putVReg( vD_addr,
8096 binop(Iop_ShrV128, mkexpr(vA), mkexpr(sh)) );
8097 break;
8098 }
cerion5b2325f2005-12-23 00:55:09 +00008099 case 0x304: // vsrab (Shift Right Alg B, AV p253)
cerion32aad402005-09-10 12:02:24 +00008100 DIP("vsrab v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00008101 putVReg( vD_addr, binop(Iop_Sar8x16, mkexpr(vA), mkexpr(vB)) );
8102 break;
cerion32aad402005-09-10 12:02:24 +00008103
cerion5b2325f2005-12-23 00:55:09 +00008104 case 0x344: // vsrah (Shift Right Alg HW, AV p254)
cerion32aad402005-09-10 12:02:24 +00008105 DIP("vsrah v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00008106 putVReg( vD_addr, binop(Iop_Sar16x8, mkexpr(vA), mkexpr(vB)) );
8107 break;
cerion32aad402005-09-10 12:02:24 +00008108
cerion5b2325f2005-12-23 00:55:09 +00008109 case 0x384: // vsraw (Shift Right Alg W, AV p255)
cerion32aad402005-09-10 12:02:24 +00008110 DIP("vsraw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00008111 putVReg( vD_addr, binop(Iop_Sar32x4, mkexpr(vA), mkexpr(vB)) );
8112 break;
cerion32aad402005-09-10 12:02:24 +00008113
cerion0a7b4f42005-09-16 07:54:40 +00008114 case 0x44C: { // vsro (Shift Right by Octet, AV p258)
cerion0a7b4f42005-09-16 07:54:40 +00008115 IRTemp sh = newTemp(Ity_I8);
sewardj197bd172005-10-12 11:34:33 +00008116 DIP("vsro v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00008117 assign( sh, binop(Iop_And8, mkU8(0x78),
8118 unop(Iop_32to8,
8119 unop(Iop_V128to32, mkexpr(vB)))) );
8120 putVReg( vD_addr,
8121 binop(Iop_ShrV128, mkexpr(vA), mkexpr(sh)) );
8122 break;
8123 }
cerion32aad402005-09-10 12:02:24 +00008124
8125 default:
cerion5b2325f2005-12-23 00:55:09 +00008126 vex_printf("dis_av_shift(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00008127 return False;
8128 }
8129 return True;
8130}
8131
8132/*
8133 AltiVec Permute Instructions
8134*/
8135static Bool dis_av_permute ( UInt theInstr )
8136{
cerion76de5cf2005-11-18 18:25:12 +00008137 /* VA-Form, VX-Form */
8138 UChar opc1 = ifieldOPC(theInstr);
8139 UChar vD_addr = ifieldRegDS(theInstr);
8140 UChar vA_addr = ifieldRegA(theInstr);
8141 UChar UIMM_5 = vA_addr;
8142 UChar vB_addr = ifieldRegB(theInstr);
8143 UChar vC_addr = ifieldRegC(theInstr);
8144 UChar b10 = ifieldBIT10(theInstr);
8145 UChar SHB_uimm4 = toUChar( IFIELD( theInstr, 6, 4 ) );
8146 UInt opc2 = toUChar( IFIELD( theInstr, 0, 6 ) );
cerion32aad402005-09-10 12:02:24 +00008147
cerion76de5cf2005-11-18 18:25:12 +00008148 UChar SIMM_8 = extend_s_5to8(UIMM_5);
cerion32aad402005-09-10 12:02:24 +00008149
cerion6e7a0ea2005-09-13 13:34:09 +00008150 IRTemp vA = newTemp(Ity_V128);
8151 IRTemp vB = newTemp(Ity_V128);
8152 IRTemp vC = newTemp(Ity_V128);
8153 assign( vA, getVReg(vA_addr));
8154 assign( vB, getVReg(vB_addr));
8155 assign( vC, getVReg(vC_addr));
8156
cerion32aad402005-09-10 12:02:24 +00008157 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00008158 vex_printf("dis_av_permute(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00008159 return False;
8160 }
8161
8162 switch (opc2) {
8163 case 0x2A: // vsel (Conditional Select, AV p238)
8164 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 +00008165 /* vD = (vA & ~vC) | (vB & vC) */
8166 putVReg( vD_addr, binop(Iop_OrV128,
8167 binop(Iop_AndV128, mkexpr(vA), unop(Iop_NotV128, mkexpr(vC))),
8168 binop(Iop_AndV128, mkexpr(vB), mkexpr(vC))) );
8169 return True;
cerion32aad402005-09-10 12:02:24 +00008170
cerion92d9d872005-09-15 21:58:50 +00008171 case 0x2B: { // vperm (Permute, AV p218)
cerion92d9d872005-09-15 21:58:50 +00008172 /* limited to two args for IR, so have to play games... */
sewardjdc1f9132005-10-22 12:49:49 +00008173 IRTemp a_perm = newTemp(Ity_V128);
8174 IRTemp b_perm = newTemp(Ity_V128);
8175 IRTemp mask = newTemp(Ity_V128);
8176 IRTemp vC_andF = newTemp(Ity_V128);
cerion5b2325f2005-12-23 00:55:09 +00008177 DIP("vperm v%d,v%d,v%d,v%d\n",
8178 vD_addr, vA_addr, vB_addr, vC_addr);
sewardjdc1f9132005-10-22 12:49:49 +00008179 /* Limit the Perm8x16 steering values to 0 .. 15 as that is what
8180 IR specifies, and also to hide irrelevant bits from
8181 memcheck */
cerion5b2325f2005-12-23 00:55:09 +00008182 assign( vC_andF,
8183 binop(Iop_AndV128, mkexpr(vC),
8184 unop(Iop_Dup8x16, mkU8(0xF))) );
8185 assign( a_perm,
8186 binop(Iop_Perm8x16, mkexpr(vA), mkexpr(vC_andF)) );
8187 assign( b_perm,
8188 binop(Iop_Perm8x16, mkexpr(vB), mkexpr(vC_andF)) );
cerion92d9d872005-09-15 21:58:50 +00008189 // mask[i8] = (vC[i8]_4 == 1) ? 0xFF : 0x0
8190 assign( mask, binop(Iop_SarN8x16,
8191 binop(Iop_ShlN8x16, mkexpr(vC), mkU8(3)),
8192 mkU8(7)) );
8193 // dst = (a & ~mask) | (b & mask)
8194 putVReg( vD_addr, binop(Iop_OrV128,
8195 binop(Iop_AndV128, mkexpr(a_perm),
8196 unop(Iop_NotV128, mkexpr(mask))),
8197 binop(Iop_AndV128, mkexpr(b_perm),
8198 mkexpr(mask))) );
8199 return True;
8200 }
cerion32aad402005-09-10 12:02:24 +00008201 case 0x2C: // vsldoi (Shift Left Double by Octet Imm, AV p241)
8202 if (b10 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00008203 vex_printf("dis_av_permute(ppc)(vsldoi)\n");
cerion32aad402005-09-10 12:02:24 +00008204 return False;
8205 }
cerion5b2325f2005-12-23 00:55:09 +00008206 DIP("vsldoi v%d,v%d,v%d,%d\n",
8207 vD_addr, vA_addr, vB_addr, SHB_uimm4);
cerion92d9d872005-09-15 21:58:50 +00008208 if (SHB_uimm4 == 0)
8209 putVReg( vD_addr, mkexpr(vA) );
8210 else
8211 putVReg( vD_addr,
cerion5b2325f2005-12-23 00:55:09 +00008212 binop(Iop_OrV128,
8213 binop(Iop_ShlV128, mkexpr(vA), mkU8(SHB_uimm4*8)),
8214 binop(Iop_ShrV128, mkexpr(vB), mkU8((16-SHB_uimm4)*8))) );
cerion92d9d872005-09-15 21:58:50 +00008215 return True;
cerion32aad402005-09-10 12:02:24 +00008216
8217 default:
8218 break; // Fall through...
8219 }
8220
cerion76de5cf2005-11-18 18:25:12 +00008221 opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00008222 switch (opc2) {
8223
8224 /* Merge */
8225 case 0x00C: // vmrghb (Merge High B, AV p195)
8226 DIP("vmrghb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion92d9d872005-09-15 21:58:50 +00008227 putVReg( vD_addr,
8228 binop(Iop_InterleaveHI8x16, mkexpr(vA), mkexpr(vB)) );
8229 break;
cerion32aad402005-09-10 12:02:24 +00008230
8231 case 0x04C: // vmrghh (Merge High HW, AV p196)
8232 DIP("vmrghh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion92d9d872005-09-15 21:58:50 +00008233 putVReg( vD_addr,
8234 binop(Iop_InterleaveHI16x8, mkexpr(vA), mkexpr(vB)) );
8235 break;
cerion32aad402005-09-10 12:02:24 +00008236
8237 case 0x08C: // vmrghw (Merge High W, AV p197)
8238 DIP("vmrghw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion92d9d872005-09-15 21:58:50 +00008239 putVReg( vD_addr,
8240 binop(Iop_InterleaveHI32x4, mkexpr(vA), mkexpr(vB)) );
8241 break;
cerion32aad402005-09-10 12:02:24 +00008242
8243 case 0x10C: // vmrglb (Merge Low B, AV p198)
8244 DIP("vmrglb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion92d9d872005-09-15 21:58:50 +00008245 putVReg( vD_addr,
8246 binop(Iop_InterleaveLO8x16, mkexpr(vA), mkexpr(vB)) );
8247 break;
cerion32aad402005-09-10 12:02:24 +00008248
8249 case 0x14C: // vmrglh (Merge Low HW, AV p199)
8250 DIP("vmrglh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion92d9d872005-09-15 21:58:50 +00008251 putVReg( vD_addr,
8252 binop(Iop_InterleaveLO16x8, mkexpr(vA), mkexpr(vB)) );
8253 break;
cerion32aad402005-09-10 12:02:24 +00008254
8255 case 0x18C: // vmrglw (Merge Low W, AV p200)
8256 DIP("vmrglw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion92d9d872005-09-15 21:58:50 +00008257 putVReg( vD_addr,
8258 binop(Iop_InterleaveLO32x4, mkexpr(vA), mkexpr(vB)) );
8259 break;
8260
cerion32aad402005-09-10 12:02:24 +00008261
8262 /* Splat */
cerion92d9d872005-09-15 21:58:50 +00008263 case 0x20C: { // vspltb (Splat Byte, AV p245)
cerion92d9d872005-09-15 21:58:50 +00008264 /* vD = Dup8x16( vB[UIMM_5] ) */
sewardjd1470942005-10-22 02:01:16 +00008265 UChar sh_uimm = (15 - (UIMM_5 & 15)) * 8;
sewardj197bd172005-10-12 11:34:33 +00008266 DIP("vspltb v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
cerion92d9d872005-09-15 21:58:50 +00008267 putVReg( vD_addr, unop(Iop_Dup8x16,
8268 unop(Iop_32to8, unop(Iop_V128to32,
8269 binop(Iop_ShrV128, mkexpr(vB), mkU8(sh_uimm))))) );
8270 break;
8271 }
8272 case 0x24C: { // vsplth (Splat Half Word, AV p246)
sewardjd1470942005-10-22 02:01:16 +00008273 UChar sh_uimm = (7 - (UIMM_5 & 7)) * 16;
sewardj197bd172005-10-12 11:34:33 +00008274 DIP("vsplth v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
cerion92d9d872005-09-15 21:58:50 +00008275 putVReg( vD_addr, unop(Iop_Dup16x8,
8276 unop(Iop_32to16, unop(Iop_V128to32,
8277 binop(Iop_ShrV128, mkexpr(vB), mkU8(sh_uimm))))) );
8278 break;
8279 }
cerion27b3d7e2005-09-14 20:35:47 +00008280 case 0x28C: { // vspltw (Splat Word, AV p250)
cerion27b3d7e2005-09-14 20:35:47 +00008281 /* vD = Dup32x4( vB[UIMM_5] ) */
sewardjd1470942005-10-22 02:01:16 +00008282 UChar sh_uimm = (3 - (UIMM_5 & 3)) * 32;
sewardj197bd172005-10-12 11:34:33 +00008283 DIP("vspltw v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
cerion27b3d7e2005-09-14 20:35:47 +00008284 putVReg( vD_addr, unop(Iop_Dup32x4,
8285 unop(Iop_V128to32,
8286 binop(Iop_ShrV128, mkexpr(vB), mkU8(sh_uimm)))) );
8287 break;
8288 }
cerion32aad402005-09-10 12:02:24 +00008289 case 0x30C: // vspltisb (Splat Immediate Signed B, AV p247)
8290 DIP("vspltisb v%d,%d\n", vD_addr, (Char)SIMM_8);
cerion92d9d872005-09-15 21:58:50 +00008291 putVReg( vD_addr, unop(Iop_Dup8x16, mkU8(SIMM_8)) );
8292 break;
cerion32aad402005-09-10 12:02:24 +00008293
8294 case 0x34C: // vspltish (Splat Immediate Signed HW, AV p248)
8295 DIP("vspltish v%d,%d\n", vD_addr, (Char)SIMM_8);
cerion5b2325f2005-12-23 00:55:09 +00008296 putVReg( vD_addr,
8297 unop(Iop_Dup16x8, mkU16(extend_s_8to32(SIMM_8))) );
cerion92d9d872005-09-15 21:58:50 +00008298 break;
cerion32aad402005-09-10 12:02:24 +00008299
8300 case 0x38C: // vspltisw (Splat Immediate Signed W, AV p249)
8301 DIP("vspltisw v%d,%d\n", vD_addr, (Char)SIMM_8);
cerion5b2325f2005-12-23 00:55:09 +00008302 putVReg( vD_addr,
8303 unop(Iop_Dup32x4, mkU32(extend_s_8to32(SIMM_8))) );
cerion92d9d872005-09-15 21:58:50 +00008304 break;
cerion32aad402005-09-10 12:02:24 +00008305
8306 default:
cerion5b2325f2005-12-23 00:55:09 +00008307 vex_printf("dis_av_permute(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00008308 return False;
8309 }
8310 return True;
8311}
8312
8313/*
8314 AltiVec Pack/Unpack Instructions
8315*/
8316static Bool dis_av_pack ( UInt theInstr )
8317{
cerion76de5cf2005-11-18 18:25:12 +00008318 /* VX-Form */
8319 UChar opc1 = ifieldOPC(theInstr);
8320 UChar vD_addr = ifieldRegDS(theInstr);
8321 UChar vA_addr = ifieldRegA(theInstr);
8322 UChar vB_addr = ifieldRegB(theInstr);
8323 UInt opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00008324
sewardj197bd172005-10-12 11:34:33 +00008325 IRTemp signs = IRTemp_INVALID;
8326 IRTemp zeros = IRTemp_INVALID;
cerion76de5cf2005-11-18 18:25:12 +00008327 IRTemp vA = newTemp(Ity_V128);
8328 IRTemp vB = newTemp(Ity_V128);
cerion3c052792005-09-16 07:13:44 +00008329 assign( vA, getVReg(vA_addr));
8330 assign( vB, getVReg(vB_addr));
8331
cerion32aad402005-09-10 12:02:24 +00008332 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00008333 vex_printf("dis_av_pack(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00008334 return False;
8335 }
8336
8337 switch (opc2) {
8338 /* Packing */
8339 case 0x00E: // vpkuhum (Pack Unsigned HW Unsigned Modulo, AV p224)
8340 DIP("vpkuhum v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
sewardj1bee5612005-11-10 18:10:58 +00008341 putVReg( vD_addr, binop(Iop_Narrow16x8, mkexpr(vA), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008342 return True;
cerion32aad402005-09-10 12:02:24 +00008343
8344 case 0x04E: // vpkuwum (Pack Unsigned W Unsigned Modulo, AV p226)
8345 DIP("vpkuwum v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
sewardj1bee5612005-11-10 18:10:58 +00008346 putVReg( vD_addr, binop(Iop_Narrow32x4, mkexpr(vA), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008347 return True;
cerion32aad402005-09-10 12:02:24 +00008348
8349 case 0x08E: // vpkuhus (Pack Unsigned HW Unsigned Saturate, AV p225)
8350 DIP("vpkuhus v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00008351 putVReg( vD_addr,
8352 binop(Iop_QNarrow16Ux8, mkexpr(vA), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008353 // TODO: set VSCR[SAT]
8354 return True;
cerion32aad402005-09-10 12:02:24 +00008355
8356 case 0x0CE: // vpkuwus (Pack Unsigned W Unsigned Saturate, AV p227)
8357 DIP("vpkuwus v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00008358 putVReg( vD_addr,
8359 binop(Iop_QNarrow32Ux4, mkexpr(vA), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008360 // TODO: set VSCR[SAT]
8361 return True;
cerion32aad402005-09-10 12:02:24 +00008362
cerion3c052792005-09-16 07:13:44 +00008363 case 0x10E: { // vpkshus (Pack Signed HW Unsigned Saturate, AV p221)
cerion3c052792005-09-16 07:13:44 +00008364 // This insn does a signed->unsigned saturating conversion.
8365 // Conversion done here, then uses unsigned->unsigned vpk insn:
8366 // => UnsignedSaturatingNarrow( x & ~ (x >>s 15) )
8367 IRTemp vA_tmp = newTemp(Ity_V128);
8368 IRTemp vB_tmp = newTemp(Ity_V128);
sewardj197bd172005-10-12 11:34:33 +00008369 DIP("vpkshus v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00008370 assign( vA_tmp, binop(Iop_AndV128, mkexpr(vA),
8371 unop(Iop_NotV128,
8372 binop(Iop_SarN16x8,
8373 mkexpr(vA), mkU8(15)))) );
8374 assign( vB_tmp, binop(Iop_AndV128, mkexpr(vB),
8375 unop(Iop_NotV128,
8376 binop(Iop_SarN16x8,
8377 mkexpr(vB), mkU8(15)))) );
8378 putVReg( vD_addr, binop(Iop_QNarrow16Ux8,
8379 mkexpr(vA_tmp), mkexpr(vB_tmp)) );
8380 // TODO: set VSCR[SAT]
8381 return True;
8382 }
8383 case 0x14E: { // vpkswus (Pack Signed W Unsigned Saturate, AV p223)
cerion3c052792005-09-16 07:13:44 +00008384 // This insn does a signed->unsigned saturating conversion.
8385 // Conversion done here, then uses unsigned->unsigned vpk insn:
8386 // => UnsignedSaturatingNarrow( x & ~ (x >>s 31) )
8387 IRTemp vA_tmp = newTemp(Ity_V128);
8388 IRTemp vB_tmp = newTemp(Ity_V128);
sewardj197bd172005-10-12 11:34:33 +00008389 DIP("vpkswus v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00008390 assign( vA_tmp, binop(Iop_AndV128, mkexpr(vA),
8391 unop(Iop_NotV128,
8392 binop(Iop_SarN32x4,
8393 mkexpr(vA), mkU8(31)))) );
8394 assign( vB_tmp, binop(Iop_AndV128, mkexpr(vB),
8395 unop(Iop_NotV128,
8396 binop(Iop_SarN32x4,
8397 mkexpr(vB), mkU8(31)))) );
8398 putVReg( vD_addr, binop(Iop_QNarrow32Ux4,
8399 mkexpr(vA_tmp), mkexpr(vB_tmp)) );
8400 // TODO: set VSCR[SAT]
8401 return True;
8402 }
cerion32aad402005-09-10 12:02:24 +00008403 case 0x18E: // vpkshss (Pack Signed HW Signed Saturate, AV p220)
8404 DIP("vpkshss v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00008405 putVReg( vD_addr,
8406 binop(Iop_QNarrow16Sx8, mkexpr(vA), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008407 // TODO: set VSCR[SAT]
8408 return True;
cerion32aad402005-09-10 12:02:24 +00008409
8410 case 0x1CE: // vpkswss (Pack Signed W Signed Saturate, AV p222)
8411 DIP("vpkswss v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00008412 putVReg( vD_addr,
8413 binop(Iop_QNarrow32Sx4, mkexpr(vA), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008414 // TODO: set VSCR[SAT]
8415 return True;
cerion32aad402005-09-10 12:02:24 +00008416
cerion3c052792005-09-16 07:13:44 +00008417 case 0x30E: { // vpkpx (Pack Pixel, AV p219)
cerion3c052792005-09-16 07:13:44 +00008418 /* CAB: Worth a new primop? */
cerion5b2325f2005-12-23 00:55:09 +00008419 /* Using shifts to compact pixel elements, then packing them */
cerion3c052792005-09-16 07:13:44 +00008420 IRTemp a1 = newTemp(Ity_V128);
8421 IRTemp a2 = newTemp(Ity_V128);
8422 IRTemp a3 = newTemp(Ity_V128);
8423 IRTemp a_tmp = newTemp(Ity_V128);
8424 IRTemp b1 = newTemp(Ity_V128);
8425 IRTemp b2 = newTemp(Ity_V128);
8426 IRTemp b3 = newTemp(Ity_V128);
8427 IRTemp b_tmp = newTemp(Ity_V128);
sewardj197bd172005-10-12 11:34:33 +00008428 DIP("vpkpx v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00008429 assign( a1, binop(Iop_ShlN16x8,
8430 binop(Iop_ShrN32x4, mkexpr(vA), mkU8(19)),
8431 mkU8(10)) );
8432 assign( a2, binop(Iop_ShlN16x8,
8433 binop(Iop_ShrN16x8, mkexpr(vA), mkU8(11)),
8434 mkU8(5)) );
8435 assign( a3, binop(Iop_ShrN16x8,
8436 binop(Iop_ShlN16x8, mkexpr(vA), mkU8(8)),
8437 mkU8(11)) );
8438 assign( a_tmp, binop(Iop_OrV128, mkexpr(a1),
8439 binop(Iop_OrV128, mkexpr(a2), mkexpr(a3))) );
8440
8441 assign( b1, binop(Iop_ShlN16x8,
8442 binop(Iop_ShrN32x4, mkexpr(vB), mkU8(19)),
8443 mkU8(10)) );
8444 assign( b2, binop(Iop_ShlN16x8,
8445 binop(Iop_ShrN16x8, mkexpr(vB), mkU8(11)),
8446 mkU8(5)) );
8447 assign( b3, binop(Iop_ShrN16x8,
8448 binop(Iop_ShlN16x8, mkexpr(vB), mkU8(8)),
8449 mkU8(11)) );
8450 assign( b_tmp, binop(Iop_OrV128, mkexpr(b1),
8451 binop(Iop_OrV128, mkexpr(b2), mkexpr(b3))) );
8452
sewardj1bee5612005-11-10 18:10:58 +00008453 putVReg( vD_addr, binop(Iop_Narrow32x4,
cerion3c052792005-09-16 07:13:44 +00008454 mkexpr(a_tmp), mkexpr(b_tmp)) );
8455 return True;
8456 }
cerion32aad402005-09-10 12:02:24 +00008457
8458 default:
8459 break; // Fall through...
8460 }
8461
8462
8463 if (vA_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00008464 vex_printf("dis_av_pack(ppc)(vA_addr)\n");
cerion32aad402005-09-10 12:02:24 +00008465 return False;
8466 }
8467
sewardj197bd172005-10-12 11:34:33 +00008468 signs = newTemp(Ity_V128);
8469 zeros = newTemp(Ity_V128);
cerion3c052792005-09-16 07:13:44 +00008470 assign( zeros, unop(Iop_Dup32x4, mkU32(0)) );
8471
cerion32aad402005-09-10 12:02:24 +00008472 switch (opc2) {
8473 /* Unpacking */
cerion3c052792005-09-16 07:13:44 +00008474 case 0x20E: { // vupkhsb (Unpack High Signed B, AV p277)
cerion32aad402005-09-10 12:02:24 +00008475 DIP("vupkhsb v%d,v%d\n", vD_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00008476 assign( signs, binop(Iop_CmpGT8Sx16, mkexpr(zeros), mkexpr(vB)) );
cerion5b2325f2005-12-23 00:55:09 +00008477 putVReg( vD_addr,
8478 binop(Iop_InterleaveHI8x16, mkexpr(signs), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008479 break;
8480 }
8481 case 0x24E: { // vupkhsh (Unpack High Signed HW, AV p278)
cerion32aad402005-09-10 12:02:24 +00008482 DIP("vupkhsh v%d,v%d\n", vD_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00008483 assign( signs, binop(Iop_CmpGT16Sx8, mkexpr(zeros), mkexpr(vB)) );
cerion5b2325f2005-12-23 00:55:09 +00008484 putVReg( vD_addr,
8485 binop(Iop_InterleaveHI16x8, mkexpr(signs), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008486 break;
8487 }
8488 case 0x28E: { // vupklsb (Unpack Low Signed B, AV p280)
cerion32aad402005-09-10 12:02:24 +00008489 DIP("vupklsb v%d,v%d\n", vD_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00008490 assign( signs, binop(Iop_CmpGT8Sx16, mkexpr(zeros), mkexpr(vB)) );
cerion5b2325f2005-12-23 00:55:09 +00008491 putVReg( vD_addr,
8492 binop(Iop_InterleaveLO8x16, mkexpr(signs), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008493 break;
8494 }
8495 case 0x2CE: { // vupklsh (Unpack Low Signed HW, AV p281)
cerion32aad402005-09-10 12:02:24 +00008496 DIP("vupklsh v%d,v%d\n", vD_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00008497 assign( signs, binop(Iop_CmpGT16Sx8, mkexpr(zeros), mkexpr(vB)) );
cerion5b2325f2005-12-23 00:55:09 +00008498 putVReg( vD_addr,
8499 binop(Iop_InterleaveLO16x8, mkexpr(signs), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008500 break;
8501 }
8502 case 0x34E: { // vupkhpx (Unpack High Pixel16, AV p276)
cerion3c052792005-09-16 07:13:44 +00008503 /* CAB: Worth a new primop? */
8504 /* Using shifts to isolate pixel elements, then expanding them */
8505 IRTemp z0 = newTemp(Ity_V128);
8506 IRTemp z1 = newTemp(Ity_V128);
8507 IRTemp z01 = newTemp(Ity_V128);
8508 IRTemp z2 = newTemp(Ity_V128);
8509 IRTemp z3 = newTemp(Ity_V128);
8510 IRTemp z23 = newTemp(Ity_V128);
sewardj197bd172005-10-12 11:34:33 +00008511 DIP("vupkhpx v%d,v%d\n", vD_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00008512 assign( z0, binop(Iop_ShlN16x8,
8513 binop(Iop_SarN16x8, mkexpr(vB), mkU8(15)),
8514 mkU8(8)) );
8515 assign( z1, binop(Iop_ShrN16x8,
8516 binop(Iop_ShlN16x8, mkexpr(vB), mkU8(1)),
8517 mkU8(11)) );
8518 assign( z01, binop(Iop_InterleaveHI16x8, mkexpr(zeros),
8519 binop(Iop_OrV128, mkexpr(z0), mkexpr(z1))) );
8520 assign( z2, binop(Iop_ShrN16x8,
8521 binop(Iop_ShlN16x8,
8522 binop(Iop_ShrN16x8, mkexpr(vB), mkU8(5)),
8523 mkU8(11)),
8524 mkU8(3)) );
8525 assign( z3, binop(Iop_ShrN16x8,
8526 binop(Iop_ShlN16x8, mkexpr(vB), mkU8(11)),
8527 mkU8(11)) );
8528 assign( z23, binop(Iop_InterleaveHI16x8, mkexpr(zeros),
8529 binop(Iop_OrV128, mkexpr(z2), mkexpr(z3))) );
cerion5b2325f2005-12-23 00:55:09 +00008530 putVReg( vD_addr,
8531 binop(Iop_OrV128,
8532 binop(Iop_ShlN32x4, mkexpr(z01), mkU8(16)),
8533 mkexpr(z23)) );
cerion3c052792005-09-16 07:13:44 +00008534 break;
8535 }
8536 case 0x3CE: { // vupklpx (Unpack Low Pixel16, AV p279)
cerion3c052792005-09-16 07:13:44 +00008537 /* identical to vupkhpx, except interleaving LO */
8538 IRTemp z0 = newTemp(Ity_V128);
8539 IRTemp z1 = newTemp(Ity_V128);
8540 IRTemp z01 = newTemp(Ity_V128);
8541 IRTemp z2 = newTemp(Ity_V128);
8542 IRTemp z3 = newTemp(Ity_V128);
8543 IRTemp z23 = newTemp(Ity_V128);
sewardj197bd172005-10-12 11:34:33 +00008544 DIP("vupklpx v%d,v%d\n", vD_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00008545 assign( z0, binop(Iop_ShlN16x8,
8546 binop(Iop_SarN16x8, mkexpr(vB), mkU8(15)),
8547 mkU8(8)) );
8548 assign( z1, binop(Iop_ShrN16x8,
8549 binop(Iop_ShlN16x8, mkexpr(vB), mkU8(1)),
8550 mkU8(11)) );
8551 assign( z01, binop(Iop_InterleaveLO16x8, mkexpr(zeros),
8552 binop(Iop_OrV128, mkexpr(z0), mkexpr(z1))) );
8553 assign( z2, binop(Iop_ShrN16x8,
8554 binop(Iop_ShlN16x8,
8555 binop(Iop_ShrN16x8, mkexpr(vB), mkU8(5)),
8556 mkU8(11)),
8557 mkU8(3)) );
8558 assign( z3, binop(Iop_ShrN16x8,
8559 binop(Iop_ShlN16x8, mkexpr(vB), mkU8(11)),
8560 mkU8(11)) );
8561 assign( z23, binop(Iop_InterleaveLO16x8, mkexpr(zeros),
8562 binop(Iop_OrV128, mkexpr(z2), mkexpr(z3))) );
cerion5b2325f2005-12-23 00:55:09 +00008563 putVReg( vD_addr,
8564 binop(Iop_OrV128,
8565 binop(Iop_ShlN32x4, mkexpr(z01), mkU8(16)),
8566 mkexpr(z23)) );
cerion3c052792005-09-16 07:13:44 +00008567 break;
8568 }
cerion32aad402005-09-10 12:02:24 +00008569 default:
cerion5b2325f2005-12-23 00:55:09 +00008570 vex_printf("dis_av_pack(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00008571 return False;
8572 }
8573 return True;
8574}
8575
8576
8577/*
8578 AltiVec Floating Point Arithmetic Instructions
8579*/
8580static Bool dis_av_fp_arith ( UInt theInstr )
8581{
cerion76de5cf2005-11-18 18:25:12 +00008582 /* VA-Form */
8583 UChar opc1 = ifieldOPC(theInstr);
8584 UChar vD_addr = ifieldRegDS(theInstr);
8585 UChar vA_addr = ifieldRegA(theInstr);
8586 UChar vB_addr = ifieldRegB(theInstr);
8587 UChar vC_addr = ifieldRegC(theInstr);
cerion32aad402005-09-10 12:02:24 +00008588 UInt opc2=0;
8589
cerion8ea0d3e2005-11-14 00:44:47 +00008590 IRTemp vA = newTemp(Ity_V128);
8591 IRTemp vB = newTemp(Ity_V128);
8592 IRTemp vC = newTemp(Ity_V128);
8593 assign( vA, getVReg(vA_addr));
8594 assign( vB, getVReg(vB_addr));
8595 assign( vC, getVReg(vC_addr));
8596
cerion32aad402005-09-10 12:02:24 +00008597 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00008598 vex_printf("dis_av_fp_arith(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00008599 return False;
8600 }
8601
cerion76de5cf2005-11-18 18:25:12 +00008602 opc2 = IFIELD( theInstr, 0, 6 );
cerion32aad402005-09-10 12:02:24 +00008603 switch (opc2) {
8604 case 0x2E: // vmaddfp (Multiply Add FP, AV p177)
cerion5b2325f2005-12-23 00:55:09 +00008605 DIP("vmaddfp v%d,v%d,v%d,v%d\n",
8606 vD_addr, vA_addr, vC_addr, vB_addr);
8607 putVReg( vD_addr,
8608 binop(Iop_Add32Fx4, mkexpr(vB),
8609 binop(Iop_Mul32Fx4, mkexpr(vA), mkexpr(vC))) );
cerionf3f173c2005-11-14 02:37:44 +00008610 return True;
cerion32aad402005-09-10 12:02:24 +00008611
cerionf3f173c2005-11-14 02:37:44 +00008612 case 0x2F: { // vnmsubfp (Negative Multiply-Subtract FP, AV p215)
cerion5b2325f2005-12-23 00:55:09 +00008613 DIP("vnmsubfp v%d,v%d,v%d,v%d\n",
8614 vD_addr, vA_addr, vC_addr, vB_addr);
8615 putVReg( vD_addr,
8616 binop(Iop_Sub32Fx4,
8617 mkexpr(vB),
8618 binop(Iop_Mul32Fx4, mkexpr(vA), mkexpr(vC))) );
cerionf3f173c2005-11-14 02:37:44 +00008619 return True;
8620 }
cerion32aad402005-09-10 12:02:24 +00008621
8622 default:
8623 break; // Fall through...
8624 }
8625
cerion76de5cf2005-11-18 18:25:12 +00008626 opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00008627 switch (opc2) {
8628 case 0x00A: // vaddfp (Add FP, AV p137)
8629 DIP("vaddfp v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008630 putVReg( vD_addr, binop(Iop_Add32Fx4, mkexpr(vA), mkexpr(vB)) );
8631 return True;
cerion32aad402005-09-10 12:02:24 +00008632
8633 case 0x04A: // vsubfp (Subtract FP, AV p261)
8634 DIP("vsubfp v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008635 putVReg( vD_addr, binop(Iop_Sub32Fx4, mkexpr(vA), mkexpr(vB)) );
8636 return True;
cerion32aad402005-09-10 12:02:24 +00008637
8638 case 0x40A: // vmaxfp (Maximum FP, AV p178)
8639 DIP("vmaxfp v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008640 putVReg( vD_addr, binop(Iop_Max32Fx4, mkexpr(vA), mkexpr(vB)) );
8641 return True;
cerion32aad402005-09-10 12:02:24 +00008642
8643 case 0x44A: // vminfp (Minimum FP, AV p187)
8644 DIP("vminfp v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008645 putVReg( vD_addr, binop(Iop_Min32Fx4, mkexpr(vA), mkexpr(vB)) );
8646 return True;
cerion32aad402005-09-10 12:02:24 +00008647
8648 default:
8649 break; // Fall through...
8650 }
8651
8652
8653 if (vA_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00008654 vex_printf("dis_av_fp_arith(ppc)(vA_addr)\n");
cerion32aad402005-09-10 12:02:24 +00008655 return False;
8656 }
8657
8658 switch (opc2) {
8659 case 0x10A: // vrefp (Reciprocal Esimate FP, AV p228)
8660 DIP("vrefp v%d,v%d\n", vD_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008661 putVReg( vD_addr, unop(Iop_Recip32Fx4, mkexpr(vB)) );
8662 return True;
cerion32aad402005-09-10 12:02:24 +00008663
cerion5b2325f2005-12-23 00:55:09 +00008664 case 0x14A: // vrsqrtefp (Reciprocal Sqrt Estimate FP, AV p237)
cerion32aad402005-09-10 12:02:24 +00008665 DIP("vrsqrtefp v%d,v%d\n", vD_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008666 putVReg( vD_addr, unop(Iop_RSqrt32Fx4, mkexpr(vB)) );
8667 return True;
cerion32aad402005-09-10 12:02:24 +00008668
8669 case 0x18A: // vexptefp (2 Raised to the Exp Est FP, AV p173)
8670 DIP("vexptefp v%d,v%d\n", vD_addr, vB_addr);
8671 DIP(" => not implemented\n");
8672 return False;
8673
8674 case 0x1CA: // vlogefp (Log2 Estimate FP, AV p175)
8675 DIP("vlogefp v%d,v%d\n", vD_addr, vB_addr);
8676 DIP(" => not implemented\n");
8677 return False;
8678
8679 default:
cerion5b2325f2005-12-23 00:55:09 +00008680 vex_printf("dis_av_fp_arith(ppc)(opc2=0x%x)\n",opc2);
cerion32aad402005-09-10 12:02:24 +00008681 return False;
8682 }
8683 return True;
8684}
8685
8686/*
8687 AltiVec Floating Point Compare Instructions
8688*/
8689static Bool dis_av_fp_cmp ( UInt theInstr )
8690{
cerion76de5cf2005-11-18 18:25:12 +00008691 /* VXR-Form */
8692 UChar opc1 = ifieldOPC(theInstr);
8693 UChar vD_addr = ifieldRegDS(theInstr);
8694 UChar vA_addr = ifieldRegA(theInstr);
8695 UChar vB_addr = ifieldRegB(theInstr);
8696 UChar flag_rC = ifieldBIT10(theInstr);
8697 UInt opc2 = IFIELD( theInstr, 0, 10 );
cerion32aad402005-09-10 12:02:24 +00008698
cerion8ea0d3e2005-11-14 00:44:47 +00008699 Bool cmp_bounds = False;
8700
8701 IRTemp vA = newTemp(Ity_V128);
8702 IRTemp vB = newTemp(Ity_V128);
8703 IRTemp vD = newTemp(Ity_V128);
8704 assign( vA, getVReg(vA_addr));
8705 assign( vB, getVReg(vB_addr));
8706
cerion32aad402005-09-10 12:02:24 +00008707 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00008708 vex_printf("dis_av_fp_cmp(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00008709 return False;
8710 }
8711
8712 switch (opc2) {
8713 case 0x0C6: // vcmpeqfp (Compare Equal-to FP, AV p159)
cerion5b2325f2005-12-23 00:55:09 +00008714 DIP("vcmpeqfp%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
8715 vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008716 assign( vD, binop(Iop_CmpEQ32Fx4, mkexpr(vA), mkexpr(vB)) );
8717 break;
cerion32aad402005-09-10 12:02:24 +00008718
cerion5b2325f2005-12-23 00:55:09 +00008719 case 0x1C6: // vcmpgefp (Compare Greater-than-or-Equal-to, AV p163)
8720 DIP("vcmpgefp%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
8721 vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008722 assign( vD, binop(Iop_CmpGE32Fx4, mkexpr(vA), mkexpr(vB)) );
8723 break;
cerion32aad402005-09-10 12:02:24 +00008724
8725 case 0x2C6: // vcmpgtfp (Compare Greater-than FP, AV p164)
cerion5b2325f2005-12-23 00:55:09 +00008726 DIP("vcmpgtfp%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
8727 vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008728 assign( vD, binop(Iop_CmpGT32Fx4, mkexpr(vA), mkexpr(vB)) );
8729 break;
cerion32aad402005-09-10 12:02:24 +00008730
cerion8ea0d3e2005-11-14 00:44:47 +00008731 case 0x3C6: { // vcmpbfp (Compare Bounds FP, AV p157)
8732 IRTemp gt = newTemp(Ity_V128);
8733 IRTemp lt = newTemp(Ity_V128);
8734 IRTemp zeros = newTemp(Ity_V128);
cerion5b2325f2005-12-23 00:55:09 +00008735 DIP("vcmpbfp%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
8736 vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008737 cmp_bounds = True;
8738 assign( zeros, unop(Iop_Dup32x4, mkU32(0)) );
8739
8740 /* Note: making use of fact that the ppc backend for compare insns
cerion5b2325f2005-12-23 00:55:09 +00008741 return zero'd lanes if either of the corresponding arg lanes is
8742 a nan.
cerion8ea0d3e2005-11-14 00:44:47 +00008743
8744 Perhaps better to have an irop Iop_isNan32Fx4, but then we'd
8745 need this for the other compares too (vcmpeqfp etc)...
8746 Better still, tighten down the spec for compare irops.
8747 */
8748 assign( gt, unop(Iop_NotV128,
8749 binop(Iop_CmpLE32Fx4, mkexpr(vA), mkexpr(vB))) );
8750 assign( lt, unop(Iop_NotV128,
8751 binop(Iop_CmpGE32Fx4, mkexpr(vA),
cerion5b2325f2005-12-23 00:55:09 +00008752 binop(Iop_Sub32Fx4, mkexpr(zeros),
8753 mkexpr(vB)))) );
cerion8ea0d3e2005-11-14 00:44:47 +00008754
8755 // finally, just shift gt,lt to correct position
8756 assign( vD, binop(Iop_ShlN32x4,
8757 binop(Iop_OrV128,
8758 binop(Iop_AndV128, mkexpr(gt),
8759 unop(Iop_Dup32x4, mkU32(0x2))),
8760 binop(Iop_AndV128, mkexpr(lt),
8761 unop(Iop_Dup32x4, mkU32(0x1)))),
8762 mkU8(30)) );
8763 break;
8764 }
cerion32aad402005-09-10 12:02:24 +00008765
8766 default:
cerion5b2325f2005-12-23 00:55:09 +00008767 vex_printf("dis_av_fp_cmp(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00008768 return False;
8769 }
cerion8ea0d3e2005-11-14 00:44:47 +00008770
8771 putVReg( vD_addr, mkexpr(vD) );
8772
cerion76de5cf2005-11-18 18:25:12 +00008773 if (flag_rC) {
cerion8ea0d3e2005-11-14 00:44:47 +00008774 set_AV_CR6( mkexpr(vD), !cmp_bounds );
8775 }
cerion32aad402005-09-10 12:02:24 +00008776 return True;
8777}
8778
8779/*
8780 AltiVec Floating Point Convert/Round Instructions
8781*/
8782static Bool dis_av_fp_convert ( UInt theInstr )
8783{
cerion76de5cf2005-11-18 18:25:12 +00008784 /* VX-Form */
8785 UChar opc1 = ifieldOPC(theInstr);
8786 UChar vD_addr = ifieldRegDS(theInstr);
8787 UChar UIMM_5 = ifieldRegA(theInstr);
8788 UChar vB_addr = ifieldRegB(theInstr);
8789 UInt opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00008790
cerion76de5cf2005-11-18 18:25:12 +00008791 IRTemp vB = newTemp(Ity_V128);
8792 IRTemp vScale = newTemp(Ity_V128);
ceriond963eb42005-11-16 18:02:58 +00008793 IRTemp vInvScale = newTemp(Ity_V128);
sewardj41a7b702005-11-18 22:18:23 +00008794
8795 float scale, inv_scale;
8796
ceriond963eb42005-11-16 18:02:58 +00008797 assign( vB, getVReg(vB_addr));
8798
8799 /* scale = 2^UIMM, cast to float, reinterpreted as uint */
sewardj41a7b702005-11-18 22:18:23 +00008800 scale = (float)( (unsigned int) 1<<UIMM_5 );
sewardj2ead5222005-11-23 03:53:45 +00008801 assign( vScale, unop(Iop_Dup32x4, mkU32( float_to_bits(scale) )) );
sewardj41a7b702005-11-18 22:18:23 +00008802 inv_scale = 1/scale;
cerion5b2325f2005-12-23 00:55:09 +00008803 assign( vInvScale,
8804 unop(Iop_Dup32x4, mkU32( float_to_bits(inv_scale) )) );
ceriond963eb42005-11-16 18:02:58 +00008805
cerion32aad402005-09-10 12:02:24 +00008806 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00008807 vex_printf("dis_av_fp_convert(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00008808 return False;
8809 }
8810
8811 switch (opc2) {
8812 case 0x30A: // vcfux (Convert from Unsigned Fixed-Point W, AV p156)
8813 DIP("vcfux v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
ceriond963eb42005-11-16 18:02:58 +00008814 putVReg( vD_addr, binop(Iop_Mul32Fx4,
8815 unop(Iop_I32UtoFx4, mkexpr(vB)),
8816 mkexpr(vInvScale)) );
8817 return True;
cerion32aad402005-09-10 12:02:24 +00008818
8819 case 0x34A: // vcfsx (Convert from Signed Fixed-Point W, AV p155)
8820 DIP("vcfsx v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
ceriond963eb42005-11-16 18:02:58 +00008821
8822 putVReg( vD_addr, binop(Iop_Mul32Fx4,
8823 unop(Iop_I32StoFx4, mkexpr(vB)),
8824 mkexpr(vInvScale)) );
8825 return True;
cerion32aad402005-09-10 12:02:24 +00008826
8827 case 0x38A: // vctuxs (Convert to Unsigned Fixed-Point W Saturate, AV p172)
8828 DIP("vctuxs v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
ceriond963eb42005-11-16 18:02:58 +00008829 putVReg( vD_addr,
8830 unop(Iop_QFtoI32Ux4_RZ,
8831 binop(Iop_Mul32Fx4, mkexpr(vB), mkexpr(vScale))) );
8832 return True;
cerion32aad402005-09-10 12:02:24 +00008833
8834 case 0x3CA: // vctsxs (Convert to Signed Fixed-Point W Saturate, AV p171)
8835 DIP("vctsxs v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
ceriond963eb42005-11-16 18:02:58 +00008836 putVReg( vD_addr,
8837 unop(Iop_QFtoI32Sx4_RZ,
8838 binop(Iop_Mul32Fx4, mkexpr(vB), mkexpr(vScale))) );
8839 return True;
cerion32aad402005-09-10 12:02:24 +00008840
8841 default:
8842 break; // Fall through...
8843 }
8844
8845 if (UIMM_5 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00008846 vex_printf("dis_av_fp_convert(ppc)(UIMM_5)\n");
cerion32aad402005-09-10 12:02:24 +00008847 return False;
8848 }
8849
8850 switch (opc2) {
8851 case 0x20A: // vrfin (Round to FP Integer Nearest, AV p231)
8852 DIP("vrfin v%d,v%d\n", vD_addr, vB_addr);
ceriond963eb42005-11-16 18:02:58 +00008853 putVReg( vD_addr, unop(Iop_RoundF32x4_RN, mkexpr(vB)) );
8854 break;
cerion32aad402005-09-10 12:02:24 +00008855
8856 case 0x24A: // vrfiz (Round to FP Integer toward zero, AV p233)
8857 DIP("vrfiz v%d,v%d\n", vD_addr, vB_addr);
ceriond963eb42005-11-16 18:02:58 +00008858 putVReg( vD_addr, unop(Iop_RoundF32x4_RZ, mkexpr(vB)) );
8859 break;
cerion32aad402005-09-10 12:02:24 +00008860
8861 case 0x28A: // vrfip (Round to FP Integer toward +inf, AV p232)
8862 DIP("vrfip v%d,v%d\n", vD_addr, vB_addr);
ceriond963eb42005-11-16 18:02:58 +00008863 putVReg( vD_addr, unop(Iop_RoundF32x4_RP, mkexpr(vB)) );
8864 break;
cerion32aad402005-09-10 12:02:24 +00008865
8866 case 0x2CA: // vrfim (Round to FP Integer toward -inf, AV p230)
8867 DIP("vrfim v%d,v%d\n", vD_addr, vB_addr);
ceriond963eb42005-11-16 18:02:58 +00008868 putVReg( vD_addr, unop(Iop_RoundF32x4_RM, mkexpr(vB)) );
8869 break;
cerion32aad402005-09-10 12:02:24 +00008870
8871 default:
cerion5b2325f2005-12-23 00:55:09 +00008872 vex_printf("dis_av_fp_convert(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00008873 return False;
8874 }
8875 return True;
8876}
cerion3d870a32005-03-18 12:23:33 +00008877
8878
cerion91ad5362005-01-27 23:02:41 +00008879
sewardj0f1ef862008-08-08 08:37:06 +00008880/*------------------------------------------------------------*/
8881/*--- POWER6 Instruction Translation ---*/
8882/*------------------------------------------------------------*/
8883
8884static
8885Bool dis_P6 ( UInt theInstr,
8886 Bool allow_F, Bool allow_V, Bool allow_FX, Bool allow_GX)
8887{
sewardj2b8db3e2008-08-18 21:47:52 +00008888 UInt opc, rd, ra, rb, opc2, dot;
8889
8890 /* This is a hack. We should do P6 capability checking properly.
8891 But anyway, make a guess at whether we should even try to handle
8892 this instruction. All P6 capable CPUs should be able to handle
8893 F, V, FX and GX, so that seems like a good check. */
sewardj0f1ef862008-08-08 08:37:06 +00008894 if (! (allow_F && allow_V && allow_FX && allow_GX) )
8895 return False;
8896 if (!mode64)
8897 return False; /* only support P6 in 64-bit mode for now */
8898
sewardj2b8db3e2008-08-18 21:47:52 +00008899 opc = ifieldOPC(theInstr); /* primary opcode */
8900 rd = ifieldRegDS(theInstr); /* dst reg */
8901 ra = ifieldRegA(theInstr); /* first source reg */
8902 rb = ifieldRegB(theInstr); /* second source reg */
8903 opc2 = ifieldOPClo10(theInstr); /* secondary opc, 10:1 */
8904 dot = ifieldBIT0(theInstr); /* Rc field, bit 0 */
sewardj0f1ef862008-08-08 08:37:06 +00008905
8906 if (opc == 63 && ra == 0/*presumably*/ && opc2 == 488) {
8907 /* frim (Floating Round to Integer Minus, PPC ISA 2.05 p137) */
8908 if (dot) return False;
8909 putFReg( rd, unop(Iop_RoundF64toF64_NegINF, getFReg( rb )) );
8910 DIP("frim%s fr%u,fr%u\n", dot ? "." : "", rd, rb);
8911 return True;
8912 }
8913
8914 if (opc == 63 && ra == 0/*presumably*/ && opc2 == 456) {
8915 /* frip (Floating Round to Integer Plus, PPC ISA 2.05 p137) */
8916 if (dot) return False;
8917 putFReg( rd, unop(Iop_RoundF64toF64_PosINF, getFReg( rb )) );
8918 DIP("frip%s fr%u,fr%u\n", dot ? "." : "", rd, rb);
8919 return True;
8920 }
8921
8922 if (opc == 63 && ra == 0/*presumably*/ && opc2 == 392) {
8923 /* frin (Floating Round to Integer Nearest, PPC ISA 2.05 p137) */
8924 if (dot) return False;
8925 putFReg( rd, unop(Iop_RoundF64toF64_NEAREST, getFReg( rb )) );
8926 DIP("frin%s fr%u,fr%u\n", dot ? "." : "", rd, rb);
8927 return True;
8928 }
8929
8930 if (opc == 63 && ra == 0/*presumably*/ && opc2 == 424) {
8931 /* frin (Floating Round to Integer Zero, PPC ISA 2.05 p137) */
8932 if (dot) return False;
8933 putFReg( rd, unop(Iop_RoundF64toF64_ZERO, getFReg( rb )) );
8934 DIP("friz%s fr%u,fr%u\n", dot ? "." : "", rd, rb);
8935 return True;
8936 }
8937
8938 if (0)
8939 vex_printf("dis_P6: %u %u %u %u %u %u\n", opc, rd, ra, rb, opc2, dot);
8940 return False;
8941}
cerion91ad5362005-01-27 23:02:41 +00008942
8943
8944
cerion896a1372005-01-25 12:24:25 +00008945/*------------------------------------------------------------*/
8946/*--- Disassemble a single instruction ---*/
8947/*------------------------------------------------------------*/
8948
8949/* Disassemble a single instruction into IR. The instruction
sewardj9e6491a2005-07-02 19:24:10 +00008950 is located in host memory at &guest_code[delta]. */
8951
8952static
cerion5b2325f2005-12-23 00:55:09 +00008953DisResult disInstr_PPC_WRK (
sewardj9e6491a2005-07-02 19:24:10 +00008954 Bool put_IP,
sewardjc716aea2006-01-17 01:48:46 +00008955 Bool (*resteerOkFn) ( /*opaque*/void*, Addr64 ),
8956 void* callback_opaque,
sewardj9e6491a2005-07-02 19:24:10 +00008957 Long delta64,
sewardjaca070a2006-10-17 00:28:22 +00008958 VexArchInfo* archinfo,
sewardjdd40fdf2006-12-24 02:20:24 +00008959 VexAbiInfo* abiinfo
sewardj9e6491a2005-07-02 19:24:10 +00008960 )
cerion896a1372005-01-25 12:24:25 +00008961{
sewardj9e6491a2005-07-02 19:24:10 +00008962 UChar opc1;
8963 UInt opc2;
8964 DisResult dres;
cerion896a1372005-01-25 12:24:25 +00008965 UInt theInstr;
ceriond953ebb2005-11-29 13:27:20 +00008966 IRType ty = mode64 ? Ity_I64 : Ity_I32;
sewardj5117ce12006-01-27 21:20:15 +00008967 Bool allow_F = False;
8968 Bool allow_V = False;
8969 Bool allow_FX = False;
8970 Bool allow_GX = False;
8971 UInt hwcaps = archinfo->hwcaps;
8972 Long delta;
cerion896a1372005-01-25 12:24:25 +00008973
sewardj059601a2005-11-13 00:53:05 +00008974 /* What insn variants are we supporting today? */
sewardj5117ce12006-01-27 21:20:15 +00008975 if (mode64) {
8976 allow_F = True;
8977 allow_V = (0 != (hwcaps & VEX_HWCAPS_PPC64_V));
8978 allow_FX = (0 != (hwcaps & VEX_HWCAPS_PPC64_FX));
8979 allow_GX = (0 != (hwcaps & VEX_HWCAPS_PPC64_GX));
8980 } else {
8981 allow_F = (0 != (hwcaps & VEX_HWCAPS_PPC32_F));
8982 allow_V = (0 != (hwcaps & VEX_HWCAPS_PPC32_V));
8983 allow_FX = (0 != (hwcaps & VEX_HWCAPS_PPC32_FX));
8984 allow_GX = (0 != (hwcaps & VEX_HWCAPS_PPC32_GX));
8985 }
sewardj059601a2005-11-13 00:53:05 +00008986
sewardj9e6491a2005-07-02 19:24:10 +00008987 /* The running delta */
sewardj5117ce12006-01-27 21:20:15 +00008988 delta = (Long)mkSzAddr(ty, (ULong)delta64);
sewardj9e6491a2005-07-02 19:24:10 +00008989
8990 /* Set result defaults. */
8991 dres.whatNext = Dis_Continue;
8992 dres.len = 0;
8993 dres.continueAt = 0;
cerion896a1372005-01-25 12:24:25 +00008994
cerion1515db92005-01-25 17:21:23 +00008995 /* At least this is simple on PPC32: insns are all 4 bytes long, and
cerion896a1372005-01-25 12:24:25 +00008996 4-aligned. So just fish the whole thing out of memory right now
8997 and have done. */
cerioncf004462005-01-31 15:24:55 +00008998 theInstr = getUIntBigendianly( (UChar*)(&guest_code[delta]) );
cerion896a1372005-01-25 12:24:25 +00008999
sewardj5117ce12006-01-27 21:20:15 +00009000 if (0) vex_printf("insn: 0x%x\n", theInstr);
cerionf0de28c2005-12-13 20:21:11 +00009001
sewardj1eb7e6b2006-01-12 21:13:14 +00009002 DIP("\t0x%llx: ", (ULong)guest_CIA_curr_instr);
sewardjb51f0f42005-07-18 11:38:02 +00009003
9004 /* We may be asked to update the guest CIA before going further. */
9005 if (put_IP)
cerion2831b002005-11-30 19:55:22 +00009006 putGST( PPC_GST_CIA, mkSzImm(ty, guest_CIA_curr_instr) );
cerion896a1372005-01-25 12:24:25 +00009007
sewardjce02aa72006-01-12 12:27:58 +00009008 /* Spot "Special" instructions (see comment at top of file). */
sewardj1eb7e6b2006-01-12 21:13:14 +00009009 {
sewardjce02aa72006-01-12 12:27:58 +00009010 UChar* code = (UChar*)(guest_code + delta);
sewardj1eb7e6b2006-01-12 21:13:14 +00009011 /* Spot the 16-byte preamble:
9012 32-bit mode:
9013 54001800 rlwinm 0,0,3,0,0
9014 54006800 rlwinm 0,0,13,0,0
9015 5400E800 rlwinm 0,0,29,0,0
9016 54009800 rlwinm 0,0,19,0,0
9017 64-bit mode:
9018 78001800 rotldi 0,0,3
9019 78006800 rotldi 0,0,13
9020 7800E802 rotldi 0,0,61
9021 78009802 rotldi 0,0,51
cerion896a1372005-01-25 12:24:25 +00009022 */
sewardj1eb7e6b2006-01-12 21:13:14 +00009023 UInt word1 = mode64 ? 0x78001800 : 0x54001800;
9024 UInt word2 = mode64 ? 0x78006800 : 0x54006800;
9025 UInt word3 = mode64 ? 0x7800E802 : 0x5400E800;
9026 UInt word4 = mode64 ? 0x78009802 : 0x54009800;
9027 if (getUIntBigendianly(code+ 0) == word1 &&
9028 getUIntBigendianly(code+ 4) == word2 &&
9029 getUIntBigendianly(code+ 8) == word3 &&
9030 getUIntBigendianly(code+12) == word4) {
sewardjce02aa72006-01-12 12:27:58 +00009031 /* Got a "Special" instruction preamble. Which one is it? */
9032 if (getUIntBigendianly(code+16) == 0x7C210B78 /* or 1,1,1 */) {
9033 /* %R3 = client_request ( %R4 ) */
9034 DIP("r3 = client_request ( %%r4 )\n");
9035 delta += 20;
sewardjdd40fdf2006-12-24 02:20:24 +00009036 irsb->next = mkSzImm( ty, guest_CIA_bbstart + delta );
9037 irsb->jumpkind = Ijk_ClientReq;
sewardjce02aa72006-01-12 12:27:58 +00009038 dres.whatNext = Dis_StopHere;
9039 goto decode_success;
9040 }
9041 else
9042 if (getUIntBigendianly(code+16) == 0x7C421378 /* or 2,2,2 */) {
9043 /* %R3 = guest_NRADDR */
9044 DIP("r3 = guest_NRADDR\n");
9045 delta += 20;
9046 dres.len = 20;
9047 putIReg(3, IRExpr_Get( OFFB_NRADDR, ty ));
9048 goto decode_success;
9049 }
9050 else
9051 if (getUIntBigendianly(code+16) == 0x7C631B78 /* or 3,3,3 */) {
9052 /* branch-and-link-to-noredir %R11 */
9053 DIP("branch-and-link-to-noredir r11\n");
9054 delta += 20;
sewardj1eb7e6b2006-01-12 21:13:14 +00009055 putGST( PPC_GST_LR, mkSzImm(ty, guest_CIA_bbstart + (Long)delta) );
sewardjdd40fdf2006-12-24 02:20:24 +00009056 irsb->next = getIReg(11);
9057 irsb->jumpkind = Ijk_NoRedir;
sewardjce02aa72006-01-12 12:27:58 +00009058 dres.whatNext = Dis_StopHere;
9059 goto decode_success;
9060 }
sewardj5ff11dd2006-01-20 14:19:25 +00009061 else
sewardjaca070a2006-10-17 00:28:22 +00009062 if (getUIntBigendianly(code+16) == 0x7C842378 /* or 4,4,4 */) {
sewardj5ff11dd2006-01-20 14:19:25 +00009063 /* %R3 = guest_NRADDR_GPR2 */
9064 DIP("r3 = guest_NRADDR_GPR2\n");
9065 delta += 20;
9066 dres.len = 20;
sewardjaca070a2006-10-17 00:28:22 +00009067 putIReg(3, IRExpr_Get( OFFB_NRADDR_GPR2, ty ));
sewardj5ff11dd2006-01-20 14:19:25 +00009068 goto decode_success;
9069 }
sewardjce02aa72006-01-12 12:27:58 +00009070 /* We don't know what it is. Set opc1/opc2 so decode_failure
9071 can print the insn following the Special-insn preamble. */
9072 theInstr = getUIntBigendianly(code+16);
9073 opc1 = ifieldOPC(theInstr);
9074 opc2 = ifieldOPClo10(theInstr);
9075 goto decode_failure;
9076 /*NOTREACHED*/
cerion896a1372005-01-25 12:24:25 +00009077 }
9078 }
9079
cerion76de5cf2005-11-18 18:25:12 +00009080 opc1 = ifieldOPC(theInstr);
sewardjb51f0f42005-07-18 11:38:02 +00009081 opc2 = ifieldOPClo10(theInstr);
cerion932ad942005-01-30 10:18:50 +00009082
cerion91ad5362005-01-27 23:02:41 +00009083 // Note: all 'reserved' bits must be cleared, else invalid
9084 switch (opc1) {
cerion896a1372005-01-25 12:24:25 +00009085
cerione9d361a2005-03-04 17:35:29 +00009086 /* Integer Arithmetic Instructions */
9087 case 0x0C: case 0x0D: case 0x0E: // addic, addic., addi
9088 case 0x0F: case 0x07: case 0x08: // addis, mulli, subfic
9089 if (dis_int_arith( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009090 goto decode_failure;
cerion7aa4bbc2005-01-29 09:32:07 +00009091
cerione9d361a2005-03-04 17:35:29 +00009092 /* Integer Compare Instructions */
9093 case 0x0B: case 0x0A: // cmpi, cmpli
9094 if (dis_int_cmp( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009095 goto decode_failure;
cerion7aa4bbc2005-01-29 09:32:07 +00009096
cerione9d361a2005-03-04 17:35:29 +00009097 /* Integer Logical Instructions */
9098 case 0x1C: case 0x1D: case 0x18: // andi., andis., ori
9099 case 0x19: case 0x1A: case 0x1B: // oris, xori, xoris
9100 if (dis_int_logic( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009101 goto decode_failure;
cerion7aa4bbc2005-01-29 09:32:07 +00009102
cerione9d361a2005-03-04 17:35:29 +00009103 /* Integer Rotate Instructions */
9104 case 0x14: case 0x15: case 0x17: // rlwimi, rlwinm, rlwnm
9105 if (dis_int_rot( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009106 goto decode_failure;
cerion645c9302005-01-31 10:09:59 +00009107
cerionf0de28c2005-12-13 20:21:11 +00009108 /* 64bit Integer Rotate Instructions */
9109 case 0x1E: // rldcl, rldcr, rldic, rldicl, rldicr, rldimi
9110 if (dis_int_rot( theInstr )) goto decode_success;
9111 goto decode_failure;
9112
cerione9d361a2005-03-04 17:35:29 +00009113 /* Integer Load Instructions */
9114 case 0x22: case 0x23: case 0x2A: // lbz, lbzu, lha
9115 case 0x2B: case 0x28: case 0x29: // lhau, lhz, lhzu
9116 case 0x20: case 0x21: // lwz, lwzu
9117 if (dis_int_load( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009118 goto decode_failure;
cerion645c9302005-01-31 10:09:59 +00009119
cerione9d361a2005-03-04 17:35:29 +00009120 /* Integer Store Instructions */
9121 case 0x26: case 0x27: case 0x2C: // stb, stbu, sth
9122 case 0x2D: case 0x24: case 0x25: // sthu, stw, stwu
sewardjdd40fdf2006-12-24 02:20:24 +00009123 if (dis_int_store( theInstr, abiinfo )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009124 goto decode_failure;
ceriond23be4e2005-01-31 07:23:07 +00009125
sewardj7787af42005-08-04 18:32:19 +00009126 /* Integer Load and Store Multiple Instructions */
9127 case 0x2E: case 0x2F: // lmw, stmw
9128 if (dis_int_ldst_mult( theInstr )) goto decode_success;
9129 goto decode_failure;
cerion645c9302005-01-31 10:09:59 +00009130
cerione9d361a2005-03-04 17:35:29 +00009131 /* Branch Instructions */
9132 case 0x12: case 0x10: // b, bc
sewardjdd40fdf2006-12-24 02:20:24 +00009133 if (dis_branch(theInstr, abiinfo, &dres,
sewardjaca070a2006-10-17 00:28:22 +00009134 resteerOkFn, callback_opaque))
sewardjc716aea2006-01-17 01:48:46 +00009135 goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009136 goto decode_failure;
cerion896a1372005-01-25 12:24:25 +00009137
cerione9d361a2005-03-04 17:35:29 +00009138 /* System Linkage Instructions */
cerion8c3adda2005-01-31 11:54:05 +00009139 case 0x11: // sc
sewardjdd40fdf2006-12-24 02:20:24 +00009140 if (dis_syslink(theInstr, abiinfo, &dres)) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009141 goto decode_failure;
cerion26d07b22005-02-02 17:13:28 +00009142
sewardj334870d2006-02-07 16:42:39 +00009143 /* Trap Instructions */
9144 case 0x02: case 0x03: // tdi, twi
9145 if (dis_trapi(theInstr, &dres)) goto decode_success;
9146 goto decode_failure;
cerion8c3adda2005-01-31 11:54:05 +00009147
cerion3d870a32005-03-18 12:23:33 +00009148 /* Floating Point Load Instructions */
cerion094d1392005-06-20 13:45:57 +00009149 case 0x30: case 0x31: case 0x32: // lfs, lfsu, lfd
9150 case 0x33: // lfdu
sewardj5117ce12006-01-27 21:20:15 +00009151 if (!allow_F) goto decode_noF;
cerion3d870a32005-03-18 12:23:33 +00009152 if (dis_fp_load( theInstr )) goto decode_success;
cerione9d361a2005-03-04 17:35:29 +00009153 goto decode_failure;
cerion995bc362005-02-03 11:03:31 +00009154
cerion3d870a32005-03-18 12:23:33 +00009155 /* Floating Point Store Instructions */
9156 case 0x34: case 0x35: case 0x36: // stfsx, stfsux, stfdx
9157 case 0x37: // stfdux
sewardj5117ce12006-01-27 21:20:15 +00009158 if (!allow_F) goto decode_noF;
cerion3d870a32005-03-18 12:23:33 +00009159 if (dis_fp_store( theInstr )) goto decode_success;
9160 goto decode_failure;
9161
cerionf0de28c2005-12-13 20:21:11 +00009162 /* 64bit Integer Loads */
9163 case 0x3A: // ld, ldu, lwa
9164 if (!mode64) goto decode_failure;
9165 if (dis_int_load( theInstr )) goto decode_success;
9166 goto decode_failure;
9167
sewardje14bb9f2005-07-22 09:39:02 +00009168 case 0x3B:
sewardj5117ce12006-01-27 21:20:15 +00009169 if (!allow_F) goto decode_noF;
cerion76de5cf2005-11-18 18:25:12 +00009170 opc2 = IFIELD(theInstr, 1, 5);
sewardje14bb9f2005-07-22 09:39:02 +00009171 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00009172 /* Floating Point Arith Instructions */
9173 case 0x12: case 0x14: case 0x15: // fdivs, fsubs, fadds
sewardj5117ce12006-01-27 21:20:15 +00009174 case 0x19: // fmuls
ceriond953ebb2005-11-29 13:27:20 +00009175 if (dis_fp_arith(theInstr)) goto decode_success;
9176 goto decode_failure;
sewardj5117ce12006-01-27 21:20:15 +00009177 case 0x16: // fsqrts
9178 if (!allow_FX) goto decode_noFX;
9179 if (dis_fp_arith(theInstr)) goto decode_success;
9180 goto decode_failure;
9181 case 0x18: // fres
9182 if (!allow_GX) goto decode_noGX;
9183 if (dis_fp_arith(theInstr)) goto decode_success;
9184 goto decode_failure;
9185
ceriond953ebb2005-11-29 13:27:20 +00009186 /* Floating Point Mult-Add Instructions */
9187 case 0x1C: case 0x1D: case 0x1E: // fmsubs, fmadds, fnmsubs
9188 case 0x1F: // fnmadds
9189 if (dis_fp_multadd(theInstr)) goto decode_success;
9190 goto decode_failure;
sewardj79fd33f2006-01-29 17:07:57 +00009191
9192 case 0x1A: // frsqrtes
9193 if (!allow_GX) goto decode_noGX;
9194 if (dis_fp_arith(theInstr)) goto decode_success;
9195 goto decode_failure;
ceriond953ebb2005-11-29 13:27:20 +00009196
9197 default:
9198 goto decode_failure;
sewardje14bb9f2005-07-22 09:39:02 +00009199 }
9200 break;
cerion3d870a32005-03-18 12:23:33 +00009201
cerionf0de28c2005-12-13 20:21:11 +00009202 /* 64bit Integer Stores */
9203 case 0x3E: // std, stdu
9204 if (!mode64) goto decode_failure;
sewardjdd40fdf2006-12-24 02:20:24 +00009205 if (dis_int_store( theInstr, abiinfo )) goto decode_success;
cerionf0de28c2005-12-13 20:21:11 +00009206 goto decode_failure;
9207
cerion3d870a32005-03-18 12:23:33 +00009208 case 0x3F:
sewardj5117ce12006-01-27 21:20:15 +00009209 if (!allow_F) goto decode_noF;
cerion5b2325f2005-12-23 00:55:09 +00009210 /* Instrs using opc[1:5] never overlap instrs using opc[1:10],
cerion3d870a32005-03-18 12:23:33 +00009211 so we can simply fall through the first switch statement */
9212
cerion76de5cf2005-11-18 18:25:12 +00009213 opc2 = IFIELD(theInstr, 1, 5);
cerion3d870a32005-03-18 12:23:33 +00009214 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00009215 /* Floating Point Arith Instructions */
sewardj5117ce12006-01-27 21:20:15 +00009216 case 0x12: case 0x14: case 0x15: // fdiv, fsub, fadd
9217 case 0x19: // fmul
9218 if (dis_fp_arith(theInstr)) goto decode_success;
9219 goto decode_failure;
9220 case 0x16: // fsqrt
9221 if (!allow_FX) goto decode_noFX;
9222 if (dis_fp_arith(theInstr)) goto decode_success;
9223 goto decode_failure;
9224 case 0x17: case 0x1A: // fsel, frsqrte
9225 if (!allow_GX) goto decode_noGX;
ceriond953ebb2005-11-29 13:27:20 +00009226 if (dis_fp_arith(theInstr)) goto decode_success;
9227 goto decode_failure;
sewardje14bb9f2005-07-22 09:39:02 +00009228
ceriond953ebb2005-11-29 13:27:20 +00009229 /* Floating Point Mult-Add Instructions */
9230 case 0x1C: case 0x1D: case 0x1E: // fmsub, fmadd, fnmsub
9231 case 0x1F: // fnmadd
9232 if (dis_fp_multadd(theInstr)) goto decode_success;
9233 goto decode_failure;
sewardj79fd33f2006-01-29 17:07:57 +00009234
9235 case 0x18: // fre
9236 if (!allow_GX) goto decode_noGX;
9237 if (dis_fp_arith(theInstr)) goto decode_success;
9238 goto decode_failure;
ceriond953ebb2005-11-29 13:27:20 +00009239
9240 default:
9241 break; // Fall through
cerion3d870a32005-03-18 12:23:33 +00009242 }
9243
cerion76de5cf2005-11-18 18:25:12 +00009244 opc2 = IFIELD(theInstr, 1, 10);
sewardje14bb9f2005-07-22 09:39:02 +00009245 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00009246 /* Floating Point Compare Instructions */
9247 case 0x000: // fcmpu
9248 case 0x020: // fcmpo
9249 if (dis_fp_cmp(theInstr)) goto decode_success;
9250 goto decode_failure;
cerion2831b002005-11-30 19:55:22 +00009251
ceriond953ebb2005-11-29 13:27:20 +00009252 /* Floating Point Rounding/Conversion Instructions */
9253 case 0x00C: // frsp
9254 case 0x00E: // fctiw
9255 case 0x00F: // fctiwz
sewardj6be67232006-01-24 19:00:05 +00009256 case 0x32E: // fctid
9257 case 0x32F: // fctidz
9258 case 0x34E: // fcfid
ceriond953ebb2005-11-29 13:27:20 +00009259 if (dis_fp_round(theInstr)) goto decode_success;
9260 goto decode_failure;
sewardj0f1ef862008-08-08 08:37:06 +00009261
9262 /* Power6 rounding stuff */
9263 case 0x1E8: // frim
9264 case 0x1C8: // frip
9265 case 0x188: // frin
9266 case 0x1A8: // friz
9267 if (dis_P6(theInstr, allow_F, allow_V, allow_FX, allow_GX))
9268 goto decode_success;
9269 goto decode_failure;
ceriond953ebb2005-11-29 13:27:20 +00009270
9271 /* Floating Point Move Instructions */
9272 case 0x028: // fneg
9273 case 0x048: // fmr
9274 case 0x088: // fnabs
9275 case 0x108: // fabs
9276 if (dis_fp_move( theInstr )) goto decode_success;
9277 goto decode_failure;
sewardje14bb9f2005-07-22 09:39:02 +00009278
ceriond953ebb2005-11-29 13:27:20 +00009279 /* Floating Point Status/Control Register Instructions */
cerion3ea49ee2006-01-04 10:53:00 +00009280 case 0x026: // mtfsb1
sewardj496b88f2006-10-04 17:46:11 +00009281 case 0x040: // mcrfs
ceriond953ebb2005-11-29 13:27:20 +00009282 case 0x046: // mtfsb0
9283 case 0x086: // mtfsfi
9284 case 0x247: // mffs
9285 case 0x2C7: // mtfsf
9286 if (dis_fp_scr( theInstr )) goto decode_success;
9287 goto decode_failure;
cerionf0de28c2005-12-13 20:21:11 +00009288
ceriond953ebb2005-11-29 13:27:20 +00009289 default:
9290 goto decode_failure;
sewardje14bb9f2005-07-22 09:39:02 +00009291 }
cerion3d870a32005-03-18 12:23:33 +00009292 break;
ceriond953ebb2005-11-29 13:27:20 +00009293
cerion91ad5362005-01-27 23:02:41 +00009294 case 0x13:
cerionb85e8bb2005-02-16 08:54:33 +00009295 switch (opc2) {
cerion91ad5362005-01-27 23:02:41 +00009296
ceriond953ebb2005-11-29 13:27:20 +00009297 /* Condition Register Logical Instructions */
9298 case 0x101: case 0x081: case 0x121: // crand, crandc, creqv
9299 case 0x0E1: case 0x021: case 0x1C1: // crnand, crnor, cror
9300 case 0x1A1: case 0x0C1: case 0x000: // crorc, crxor, mcrf
9301 if (dis_cond_logic( theInstr )) goto decode_success;
9302 goto decode_failure;
cerionb85e8bb2005-02-16 08:54:33 +00009303
ceriond953ebb2005-11-29 13:27:20 +00009304 /* Branch Instructions */
9305 case 0x210: case 0x010: // bcctr, bclr
sewardjdd40fdf2006-12-24 02:20:24 +00009306 if (dis_branch(theInstr, abiinfo, &dres,
sewardjaca070a2006-10-17 00:28:22 +00009307 resteerOkFn, callback_opaque))
sewardjc716aea2006-01-17 01:48:46 +00009308 goto decode_success;
ceriond953ebb2005-11-29 13:27:20 +00009309 goto decode_failure;
9310
9311 /* Memory Synchronization Instructions */
9312 case 0x096: // isync
9313 if (dis_memsync( theInstr )) goto decode_success;
9314 goto decode_failure;
9315
9316 default:
9317 goto decode_failure;
cerionb85e8bb2005-02-16 08:54:33 +00009318 }
9319 break;
cerion91ad5362005-01-27 23:02:41 +00009320
9321
cerionb85e8bb2005-02-16 08:54:33 +00009322 case 0x1F:
cerione9d361a2005-03-04 17:35:29 +00009323
9324 /* For arith instns, bit10 is the OE flag (overflow enable) */
9325
cerion76de5cf2005-11-18 18:25:12 +00009326 opc2 = IFIELD(theInstr, 1, 9);
cerionb85e8bb2005-02-16 08:54:33 +00009327 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00009328 /* Integer Arithmetic Instructions */
9329 case 0x10A: case 0x00A: case 0x08A: // add, addc, adde
9330 case 0x0EA: case 0x0CA: case 0x1EB: // addme, addze, divw
9331 case 0x1CB: case 0x04B: case 0x00B: // divwu, mulhw, mulhwu
9332 case 0x0EB: case 0x068: case 0x028: // mullw, neg, subf
9333 case 0x008: case 0x088: case 0x0E8: // subfc, subfe, subfme
9334 case 0x0C8: // subfze
9335 if (dis_int_arith( theInstr )) goto decode_success;
9336 goto decode_failure;
cerionf0de28c2005-12-13 20:21:11 +00009337
9338 /* 64bit Integer Arithmetic */
9339 case 0x009: case 0x049: case 0x0E9: // mulhdu, mulhd, mulld
9340 case 0x1C9: case 0x1E9: // divdu, divd
9341 if (!mode64) goto decode_failure;
9342 if (dis_int_arith( theInstr )) goto decode_success;
9343 goto decode_failure;
9344
ceriond953ebb2005-11-29 13:27:20 +00009345 default:
9346 break; // Fall through...
cerionb85e8bb2005-02-16 08:54:33 +00009347 }
cerion91ad5362005-01-27 23:02:41 +00009348
cerione9d361a2005-03-04 17:35:29 +00009349 /* All remaining opcodes use full 10 bits. */
9350
cerion76de5cf2005-11-18 18:25:12 +00009351 opc2 = IFIELD(theInstr, 1, 10);
cerionb85e8bb2005-02-16 08:54:33 +00009352 switch (opc2) {
cerione9d361a2005-03-04 17:35:29 +00009353 /* Integer Compare Instructions */
9354 case 0x000: case 0x020: // cmp, cmpl
9355 if (dis_int_cmp( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009356 goto decode_failure;
cerion7aa4bbc2005-01-29 09:32:07 +00009357
cerione9d361a2005-03-04 17:35:29 +00009358 /* Integer Logical Instructions */
9359 case 0x01C: case 0x03C: case 0x01A: // and, andc, cntlzw
9360 case 0x11C: case 0x3BA: case 0x39A: // eqv, extsb, extsh
9361 case 0x1DC: case 0x07C: case 0x1BC: // nand, nor, or
9362 case 0x19C: case 0x13C: // orc, xor
9363 if (dis_int_logic( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009364 goto decode_failure;
cerion932ad942005-01-30 10:18:50 +00009365
cerionf0de28c2005-12-13 20:21:11 +00009366 /* 64bit Integer Logical Instructions */
cerion07b07a92005-12-22 14:32:35 +00009367 case 0x3DA: case 0x03A: // extsw, cntlzd
cerionf0de28c2005-12-13 20:21:11 +00009368 if (!mode64) goto decode_failure;
9369 if (dis_int_logic( theInstr )) goto decode_success;
9370 goto decode_failure;
9371
cerione9d361a2005-03-04 17:35:29 +00009372 /* Integer Shift Instructions */
9373 case 0x018: case 0x318: case 0x338: // slw, sraw, srawi
9374 case 0x218: // srw
9375 if (dis_int_shift( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009376 goto decode_failure;
cerion7aa4bbc2005-01-29 09:32:07 +00009377
cerionf0de28c2005-12-13 20:21:11 +00009378 /* 64bit Integer Shift Instructions */
9379 case 0x01B: case 0x31A: // sld, srad
cerion07b07a92005-12-22 14:32:35 +00009380 case 0x33A: case 0x33B: // sradi
cerionf0de28c2005-12-13 20:21:11 +00009381 case 0x21B: // srd
9382 if (!mode64) goto decode_failure;
9383 if (dis_int_shift( theInstr )) goto decode_success;
9384 goto decode_failure;
9385
cerione9d361a2005-03-04 17:35:29 +00009386 /* Integer Load Instructions */
9387 case 0x057: case 0x077: case 0x157: // lbzx, lbzux, lhax
9388 case 0x177: case 0x117: case 0x137: // lhaux, lhzx, lhzux
9389 case 0x017: case 0x037: // lwzx, lwzux
9390 if (dis_int_load( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009391 goto decode_failure;
cerion7aa4bbc2005-01-29 09:32:07 +00009392
cerionf0de28c2005-12-13 20:21:11 +00009393 /* 64bit Integer Load Instructions */
9394 case 0x035: case 0x015: // ldux, ldx
9395 case 0x175: case 0x155: // lwaux, lwax
9396 if (!mode64) goto decode_failure;
9397 if (dis_int_load( theInstr )) goto decode_success;
9398 goto decode_failure;
9399
sewardjb51f0f42005-07-18 11:38:02 +00009400 /* Integer Store Instructions */
cerione9d361a2005-03-04 17:35:29 +00009401 case 0x0F7: case 0x0D7: case 0x1B7: // stbux, stbx, sthux
9402 case 0x197: case 0x0B7: case 0x097: // sthx, stwux, stwx
sewardjdd40fdf2006-12-24 02:20:24 +00009403 if (dis_int_store( theInstr, abiinfo )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009404 goto decode_failure;
cerion91ad5362005-01-27 23:02:41 +00009405
cerionf0de28c2005-12-13 20:21:11 +00009406 /* 64bit Integer Store Instructions */
9407 case 0x0B5: case 0x095: // stdux, stdx
9408 if (!mode64) goto decode_failure;
sewardjdd40fdf2006-12-24 02:20:24 +00009409 if (dis_int_store( theInstr, abiinfo )) goto decode_success;
cerionf0de28c2005-12-13 20:21:11 +00009410 goto decode_failure;
9411
sewardj602857d2005-09-06 09:10:09 +00009412 /* Integer Load and Store with Byte Reverse Instructions */
9413 case 0x316: case 0x216: case 0x396: // lhbrx, lwbrx, sthbrx
9414 case 0x296: // stwbrx
9415 if (dis_int_ldst_rev( theInstr )) goto decode_success;
9416 goto decode_failure;
9417
sewardj87e651f2005-09-09 08:31:18 +00009418 /* Integer Load and Store String Instructions */
9419 case 0x255: case 0x215: case 0x2D5: // lswi, lswx, stswi
9420 case 0x295: { // stswx
9421 Bool stopHere = False;
9422 Bool ok = dis_int_ldst_str( theInstr, &stopHere );
9423 if (!ok) goto decode_failure;
9424 if (stopHere) {
sewardjdd40fdf2006-12-24 02:20:24 +00009425 irsb->next = mkSzImm(ty, nextInsnAddr());
9426 irsb->jumpkind = Ijk_Boring;
ceriond953ebb2005-11-29 13:27:20 +00009427 dres.whatNext = Dis_StopHere;
sewardj87e651f2005-09-09 08:31:18 +00009428 }
9429 goto decode_success;
9430 }
cerion645c9302005-01-31 10:09:59 +00009431
cerione9d361a2005-03-04 17:35:29 +00009432 /* Memory Synchronization Instructions */
9433 case 0x356: case 0x014: case 0x096: // eieio, lwarx, stwcx.
9434 case 0x256: // sync
9435 if (dis_memsync( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009436 goto decode_failure;
9437
cerionf0de28c2005-12-13 20:21:11 +00009438 /* 64bit Memory Synchronization Instructions */
9439 case 0x054: case 0x0D6: // ldarx, stdcx.
9440 if (!mode64) goto decode_failure;
9441 if (dis_memsync( theInstr )) goto decode_success;
9442 goto decode_failure;
9443
cerione9d361a2005-03-04 17:35:29 +00009444 /* Processor Control Instructions */
9445 case 0x200: case 0x013: case 0x153: // mcrxr, mfcr, mfspr
9446 case 0x173: case 0x090: case 0x1D3: // mftb, mtcrf, mtspr
sewardjdd40fdf2006-12-24 02:20:24 +00009447 if (dis_proc_ctl( abiinfo, theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009448 goto decode_failure;
cerion645c9302005-01-31 10:09:59 +00009449
cerione9d361a2005-03-04 17:35:29 +00009450 /* Cache Management Instructions */
9451 case 0x2F6: case 0x056: case 0x036: // dcba, dcbf, dcbst
9452 case 0x116: case 0x0F6: case 0x3F6: // dcbt, dcbtst, dcbz
9453 case 0x3D6: // icbi
sewardj9e6491a2005-07-02 19:24:10 +00009454 if (dis_cache_manage( theInstr, &dres, archinfo ))
sewardjd94b73a2005-06-30 12:08:48 +00009455 goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009456 goto decode_failure;
ceriond23be4e2005-01-31 07:23:07 +00009457
sewardjb51f0f42005-07-18 11:38:02 +00009458//zz /* External Control Instructions */
9459//zz case 0x136: case 0x1B6: // eciwx, ecowx
9460//zz DIP("external control op => not implemented\n");
9461//zz goto decode_failure;
sewardj59c0d8f2007-08-28 14:48:35 +00009462
9463 /* Trap Instructions */
sewardjb4f6d6b2007-08-28 16:39:52 +00009464 case 0x004: case 0x044: // tw, td
sewardj59c0d8f2007-08-28 14:48:35 +00009465 if (dis_trap(theInstr, &dres)) goto decode_success;
9466 goto decode_failure;
sewardje14bb9f2005-07-22 09:39:02 +00009467
9468 /* Floating Point Load Instructions */
9469 case 0x217: case 0x237: case 0x257: // lfsx, lfsux, lfdx
9470 case 0x277: // lfdux
sewardj5117ce12006-01-27 21:20:15 +00009471 if (!allow_F) goto decode_noF;
sewardje14bb9f2005-07-22 09:39:02 +00009472 if (dis_fp_load( theInstr )) goto decode_success;
9473 goto decode_failure;
9474
9475 /* Floating Point Store Instructions */
9476 case 0x297: case 0x2B7: case 0x2D7: // stfs, stfsu, stfd
sewardj5117ce12006-01-27 21:20:15 +00009477 case 0x2F7: // stfdu, stfiwx
9478 if (!allow_F) goto decode_noF;
sewardje14bb9f2005-07-22 09:39:02 +00009479 if (dis_fp_store( theInstr )) goto decode_success;
9480 goto decode_failure;
sewardj5117ce12006-01-27 21:20:15 +00009481 case 0x3D7: // stfiwx
9482 if (!allow_F) goto decode_noF;
9483 if (!allow_GX) goto decode_noGX;
9484 if (dis_fp_store( theInstr )) goto decode_success;
9485 goto decode_failure;
sewardje14bb9f2005-07-22 09:39:02 +00009486
cerion32aad402005-09-10 12:02:24 +00009487 /* AltiVec instructions */
9488
9489 /* AV Cache Control - Data streams */
9490 case 0x156: case 0x176: case 0x336: // dst, dstst, dss
sewardj5117ce12006-01-27 21:20:15 +00009491 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009492 if (dis_av_datastream( theInstr )) goto decode_success;
9493 goto decode_failure;
ceriona982c052005-06-28 17:23:09 +00009494
9495 /* AV Load */
9496 case 0x006: case 0x026: // lvsl, lvsr
9497 case 0x007: case 0x027: case 0x047: // lvebx, lvehx, lvewx
9498 case 0x067: case 0x167: // lvx, lvxl
sewardj5117ce12006-01-27 21:20:15 +00009499 if (!allow_V) goto decode_noV;
sewardjdd40fdf2006-12-24 02:20:24 +00009500 if (dis_av_load( abiinfo, theInstr )) goto decode_success;
ceriona982c052005-06-28 17:23:09 +00009501 goto decode_failure;
9502
9503 /* AV Store */
9504 case 0x087: case 0x0A7: case 0x0C7: // stvebx, stvehx, stvewx
9505 case 0x0E7: case 0x1E7: // stvx, stvxl
sewardj5117ce12006-01-27 21:20:15 +00009506 if (!allow_V) goto decode_noV;
ceriona982c052005-06-28 17:23:09 +00009507 if (dis_av_store( theInstr )) goto decode_success;
9508 goto decode_failure;
9509
9510 default:
sewardjcb07be22008-11-06 09:02:34 +00009511 /* Deal with some other cases that we would otherwise have
9512 punted on. */
9513 /* --- ISEL (PowerISA_V2.05.pdf, p74) --- */
sewardj1685c282008-11-06 09:22:05 +00009514 /* only decode this insn when reserved bit 0 (31 in IBM's
9515 notation) is zero */
9516 if (IFIELD(theInstr, 0, 6) == (15<<1)) {
sewardjcb07be22008-11-06 09:02:34 +00009517 UInt rT = ifieldRegDS( theInstr );
9518 UInt rA = ifieldRegA( theInstr );
9519 UInt rB = ifieldRegB( theInstr );
9520 UInt bi = ifieldRegC( theInstr );
9521 putIReg(
9522 rT,
9523 IRExpr_Mux0X( unop(Iop_32to8,getCRbit( bi )),
9524 getIReg(rB),
9525 rA == 0 ? (mode64 ? mkU64(0) : mkU32(0))
9526 : getIReg(rA) )
9527 );
9528 DIP("isel r%u,r%u,r%u,crb%u\n", rT,rA,rB,bi);
9529 goto decode_success;
9530 }
ceriona982c052005-06-28 17:23:09 +00009531 goto decode_failure;
9532 }
9533 break;
9534
9535
cerion32aad402005-09-10 12:02:24 +00009536 case 0x04:
9537 /* AltiVec instructions */
9538
cerion76de5cf2005-11-18 18:25:12 +00009539 opc2 = IFIELD(theInstr, 0, 6);
cerion32aad402005-09-10 12:02:24 +00009540 switch (opc2) {
9541 /* AV Mult-Add, Mult-Sum */
9542 case 0x20: case 0x21: case 0x22: // vmhaddshs, vmhraddshs, vmladduhm
9543 case 0x24: case 0x25: case 0x26: // vmsumubm, vmsummbm, vmsumuhm
9544 case 0x27: case 0x28: case 0x29: // vmsumuhs, vmsumshm, vmsumshs
sewardj5117ce12006-01-27 21:20:15 +00009545 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009546 if (dis_av_multarith( theInstr )) goto decode_success;
9547 goto decode_failure;
9548
9549 /* AV Permutations */
9550 case 0x2A: // vsel
9551 case 0x2B: // vperm
cerion32aad402005-09-10 12:02:24 +00009552 case 0x2C: // vsldoi
sewardj5117ce12006-01-27 21:20:15 +00009553 if (!allow_V) goto decode_noV;
cerion92d9d872005-09-15 21:58:50 +00009554 if (dis_av_permute( theInstr )) goto decode_success;
cerion32aad402005-09-10 12:02:24 +00009555 goto decode_failure;
9556
9557 /* AV Floating Point Mult-Add/Sub */
9558 case 0x2E: case 0x2F: // vmaddfp, vnmsubfp
sewardj5117ce12006-01-27 21:20:15 +00009559 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009560 if (dis_av_fp_arith( theInstr )) goto decode_success;
9561 goto decode_failure;
9562
9563 default:
9564 break; // Fall through...
9565 }
9566
cerion76de5cf2005-11-18 18:25:12 +00009567 opc2 = IFIELD(theInstr, 0, 11);
cerion32aad402005-09-10 12:02:24 +00009568 switch (opc2) {
9569 /* AV Arithmetic */
9570 case 0x180: // vaddcuw
9571 case 0x000: case 0x040: case 0x080: // vaddubm, vadduhm, vadduwm
9572 case 0x200: case 0x240: case 0x280: // vaddubs, vadduhs, vadduws
9573 case 0x300: case 0x340: case 0x380: // vaddsbs, vaddshs, vaddsws
9574 case 0x580: // vsubcuw
9575 case 0x400: case 0x440: case 0x480: // vsububm, vsubuhm, vsubuwm
9576 case 0x600: case 0x640: case 0x680: // vsububs, vsubuhs, vsubuws
9577 case 0x700: case 0x740: case 0x780: // vsubsbs, vsubshs, vsubsws
9578 case 0x402: case 0x442: case 0x482: // vavgub, vavguh, vavguw
9579 case 0x502: case 0x542: case 0x582: // vavgsb, vavgsh, vavgsw
9580 case 0x002: case 0x042: case 0x082: // vmaxub, vmaxuh, vmaxuw
9581 case 0x102: case 0x142: case 0x182: // vmaxsb, vmaxsh, vmaxsw
9582 case 0x202: case 0x242: case 0x282: // vminub, vminuh, vminuw
9583 case 0x302: case 0x342: case 0x382: // vminsb, vminsh, vminsw
9584 case 0x008: case 0x048: // vmuloub, vmulouh
9585 case 0x108: case 0x148: // vmulosb, vmulosh
9586 case 0x208: case 0x248: // vmuleub, vmuleuh
9587 case 0x308: case 0x348: // vmulesb, vmulesh
9588 case 0x608: case 0x708: case 0x648: // vsum4ubs, vsum4sbs, vsum4shs
9589 case 0x688: case 0x788: // vsum2sws, vsumsws
sewardj5117ce12006-01-27 21:20:15 +00009590 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009591 if (dis_av_arith( theInstr )) goto decode_success;
9592 goto decode_failure;
9593
9594 /* AV Rotate, Shift */
9595 case 0x004: case 0x044: case 0x084: // vrlb, vrlh, vrlw
9596 case 0x104: case 0x144: case 0x184: // vslb, vslh, vslw
9597 case 0x204: case 0x244: case 0x284: // vsrb, vsrh, vsrw
9598 case 0x304: case 0x344: case 0x384: // vsrab, vsrah, vsraw
9599 case 0x1C4: case 0x2C4: // vsl, vsr
9600 case 0x40C: case 0x44C: // vslo, vsro
sewardj5117ce12006-01-27 21:20:15 +00009601 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009602 if (dis_av_shift( theInstr )) goto decode_success;
9603 goto decode_failure;
9604
9605 /* AV Logic */
9606 case 0x404: case 0x444: case 0x484: // vand, vandc, vor
9607 case 0x4C4: case 0x504: // vxor, vnor
sewardj5117ce12006-01-27 21:20:15 +00009608 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009609 if (dis_av_logic( theInstr )) goto decode_success;
9610 goto decode_failure;
9611
9612 /* AV Processor Control */
9613 case 0x604: case 0x644: // mfvscr, mtvscr
sewardj5117ce12006-01-27 21:20:15 +00009614 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009615 if (dis_av_procctl( theInstr )) goto decode_success;
9616 goto decode_failure;
9617
9618 /* AV Floating Point Arithmetic */
9619 case 0x00A: case 0x04A: // vaddfp, vsubfp
9620 case 0x10A: case 0x14A: case 0x18A: // vrefp, vrsqrtefp, vexptefp
9621 case 0x1CA: // vlogefp
9622 case 0x40A: case 0x44A: // vmaxfp, vminfp
sewardj5117ce12006-01-27 21:20:15 +00009623 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009624 if (dis_av_fp_arith( theInstr )) goto decode_success;
9625 goto decode_failure;
9626
9627 /* AV Floating Point Round/Convert */
9628 case 0x20A: case 0x24A: case 0x28A: // vrfin, vrfiz, vrfip
9629 case 0x2CA: // vrfim
9630 case 0x30A: case 0x34A: case 0x38A: // vcfux, vcfsx, vctuxs
9631 case 0x3CA: // vctsxs
sewardj5117ce12006-01-27 21:20:15 +00009632 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009633 if (dis_av_fp_convert( theInstr )) goto decode_success;
9634 goto decode_failure;
9635
9636 /* AV Merge, Splat */
9637 case 0x00C: case 0x04C: case 0x08C: // vmrghb, vmrghh, vmrghw
9638 case 0x10C: case 0x14C: case 0x18C: // vmrglb, vmrglh, vmrglw
9639 case 0x20C: case 0x24C: case 0x28C: // vspltb, vsplth, vspltw
9640 case 0x30C: case 0x34C: case 0x38C: // vspltisb, vspltish, vspltisw
sewardj5117ce12006-01-27 21:20:15 +00009641 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009642 if (dis_av_permute( theInstr )) goto decode_success;
9643 goto decode_failure;
9644
9645 /* AV Pack, Unpack */
9646 case 0x00E: case 0x04E: case 0x08E: // vpkuhum, vpkuwum, vpkuhus
9647 case 0x0CE: // vpkuwus
9648 case 0x10E: case 0x14E: case 0x18E: // vpkshus, vpkswus, vpkshss
9649 case 0x1CE: // vpkswss
9650 case 0x20E: case 0x24E: case 0x28E: // vupkhsb, vupkhsh, vupklsb
9651 case 0x2CE: // vupklsh
9652 case 0x30E: case 0x34E: case 0x3CE: // vpkpx, vupkhpx, vupklpx
sewardj5117ce12006-01-27 21:20:15 +00009653 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009654 if (dis_av_pack( theInstr )) goto decode_success;
9655 goto decode_failure;
9656
9657 default:
9658 break; // Fall through...
9659 }
9660
cerion76de5cf2005-11-18 18:25:12 +00009661 opc2 = IFIELD(theInstr, 0, 10);
cerion32aad402005-09-10 12:02:24 +00009662 switch (opc2) {
9663
9664 /* AV Compare */
9665 case 0x006: case 0x046: case 0x086: // vcmpequb, vcmpequh, vcmpequw
9666 case 0x206: case 0x246: case 0x286: // vcmpgtub, vcmpgtuh, vcmpgtuw
9667 case 0x306: case 0x346: case 0x386: // vcmpgtsb, vcmpgtsh, vcmpgtsw
sewardj5117ce12006-01-27 21:20:15 +00009668 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009669 if (dis_av_cmp( theInstr )) goto decode_success;
9670 goto decode_failure;
9671
9672 /* AV Floating Point Compare */
9673 case 0x0C6: case 0x1C6: case 0x2C6: // vcmpeqfp, vcmpgefp, vcmpgtfp
9674 case 0x3C6: // vcmpbfp
sewardj5117ce12006-01-27 21:20:15 +00009675 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009676 if (dis_av_fp_cmp( theInstr )) goto decode_success;
9677 goto decode_failure;
9678
9679 default:
9680 goto decode_failure;
9681 }
9682 break;
cerion7aa4bbc2005-01-29 09:32:07 +00009683
cerion896a1372005-01-25 12:24:25 +00009684 default:
cerion5b2325f2005-12-23 00:55:09 +00009685 goto decode_failure;
9686
sewardj5117ce12006-01-27 21:20:15 +00009687 decode_noF:
9688 vassert(!allow_F);
9689 vex_printf("disInstr(ppc): declined to decode an FP insn.\n");
9690 goto decode_failure;
9691 decode_noV:
9692 vassert(!allow_V);
9693 vex_printf("disInstr(ppc): declined to decode an AltiVec insn.\n");
9694 goto decode_failure;
9695 decode_noFX:
sewardj7c545862006-01-27 21:52:19 +00009696 vassert(!allow_FX);
sewardj5117ce12006-01-27 21:20:15 +00009697 vex_printf("disInstr(ppc): "
sewardjb183b852006-02-03 16:08:03 +00009698 "declined to decode a GeneralPurpose-Optional insn.\n");
sewardj5117ce12006-01-27 21:20:15 +00009699 goto decode_failure;
9700 decode_noGX:
sewardj7c545862006-01-27 21:52:19 +00009701 vassert(!allow_GX);
sewardj5117ce12006-01-27 21:20:15 +00009702 vex_printf("disInstr(ppc): "
9703 "declined to decode a Graphics-Optional insn.\n");
cerion5b2325f2005-12-23 00:55:09 +00009704 goto decode_failure;
9705
cerion896a1372005-01-25 12:24:25 +00009706 decode_failure:
9707 /* All decode failures end up here. */
cerion225a0342005-09-12 20:49:09 +00009708 opc2 = (theInstr) & 0x7FF;
cerion5b2325f2005-12-23 00:55:09 +00009709 vex_printf("disInstr(ppc): unhandled instruction: "
cerion896a1372005-01-25 12:24:25 +00009710 "0x%x\n", theInstr);
sewardjc7cd2142005-09-09 22:31:49 +00009711 vex_printf(" primary %d(0x%x), secondary %u(0x%x)\n",
sewardjb51f0f42005-07-18 11:38:02 +00009712 opc1, opc1, opc2, opc2);
cerion995bc362005-02-03 11:03:31 +00009713
sewardj01a9e802005-02-01 20:46:00 +00009714 /* Tell the dispatcher that this insn cannot be decoded, and so has
9715 not been executed, and (is currently) the next to be executed.
9716 CIA should be up-to-date since it made so at the start of each
9717 insn, but nevertheless be paranoid and update it again right
9718 now. */
cerion2831b002005-11-30 19:55:22 +00009719 putGST( PPC_GST_CIA, mkSzImm(ty, guest_CIA_curr_instr) );
sewardjdd40fdf2006-12-24 02:20:24 +00009720 irsb->next = mkSzImm(ty, guest_CIA_curr_instr);
9721 irsb->jumpkind = Ijk_NoDecode;
ceriond953ebb2005-11-29 13:27:20 +00009722 dres.whatNext = Dis_StopHere;
9723 dres.len = 0;
sewardj9e6491a2005-07-02 19:24:10 +00009724 return dres;
cerion896a1372005-01-25 12:24:25 +00009725
9726 } /* switch (opc) for the main (primary) opcode switch. */
9727
9728 decode_success:
9729 /* All decode successes end up here. */
cerion896a1372005-01-25 12:24:25 +00009730 DIP("\n");
9731
sewardjce02aa72006-01-12 12:27:58 +00009732 if (dres.len == 0) {
9733 dres.len = 4;
9734 } else {
9735 vassert(dres.len == 20);
9736 }
sewardj9e6491a2005-07-02 19:24:10 +00009737 return dres;
cerion896a1372005-01-25 12:24:25 +00009738}
9739
9740#undef DIP
9741#undef DIS
9742
sewardj9e6491a2005-07-02 19:24:10 +00009743
9744/*------------------------------------------------------------*/
9745/*--- Top-level fn ---*/
9746/*------------------------------------------------------------*/
9747
9748/* Disassemble a single instruction into IR. The instruction
9749 is located in host memory at &guest_code[delta]. */
9750
sewardjdd40fdf2006-12-24 02:20:24 +00009751DisResult disInstr_PPC ( IRSB* irsb_IN,
cerion5b2325f2005-12-23 00:55:09 +00009752 Bool put_IP,
sewardjc716aea2006-01-17 01:48:46 +00009753 Bool (*resteerOkFn) ( void*, Addr64 ),
9754 void* callback_opaque,
cerion5b2325f2005-12-23 00:55:09 +00009755 UChar* guest_code_IN,
9756 Long delta,
9757 Addr64 guest_IP,
sewardja5f55da2006-04-30 23:37:32 +00009758 VexArch guest_arch,
cerion5b2325f2005-12-23 00:55:09 +00009759 VexArchInfo* archinfo,
sewardjdd40fdf2006-12-24 02:20:24 +00009760 VexAbiInfo* abiinfo,
cerion5b2325f2005-12-23 00:55:09 +00009761 Bool host_bigendian_IN )
sewardj9e6491a2005-07-02 19:24:10 +00009762{
sewardj5df65bb2005-11-29 14:47:04 +00009763 IRType ty;
9764 DisResult dres;
sewardj5117ce12006-01-27 21:20:15 +00009765 UInt mask32, mask64;
9766 UInt hwcaps_guest = archinfo->hwcaps;
9767
sewardja5f55da2006-04-30 23:37:32 +00009768 vassert(guest_arch == VexArchPPC32 || guest_arch == VexArchPPC64);
sewardj5df65bb2005-11-29 14:47:04 +00009769
sewardja5f55da2006-04-30 23:37:32 +00009770 /* global -- ick */
9771 mode64 = guest_arch == VexArchPPC64;
9772 ty = mode64 ? Ity_I64 : Ity_I32;
9773
9774 /* do some sanity checks */
sewardj5117ce12006-01-27 21:20:15 +00009775 mask32 = VEX_HWCAPS_PPC32_F | VEX_HWCAPS_PPC32_V
9776 | VEX_HWCAPS_PPC32_FX | VEX_HWCAPS_PPC32_GX;
9777
sewardj5117ce12006-01-27 21:20:15 +00009778 mask64 = VEX_HWCAPS_PPC64_V
9779 | VEX_HWCAPS_PPC64_FX | VEX_HWCAPS_PPC64_GX;
9780
sewardja5f55da2006-04-30 23:37:32 +00009781 if (mode64) {
9782 vassert((hwcaps_guest & mask32) == 0);
9783 } else {
9784 vassert((hwcaps_guest & mask64) == 0);
9785 }
sewardj9e6491a2005-07-02 19:24:10 +00009786
9787 /* Set globals (see top of this file) */
9788 guest_code = guest_code_IN;
sewardjdd40fdf2006-12-24 02:20:24 +00009789 irsb = irsb_IN;
sewardj9e6491a2005-07-02 19:24:10 +00009790 host_is_bigendian = host_bigendian_IN;
ceriond953ebb2005-11-29 13:27:20 +00009791
cerion2831b002005-11-30 19:55:22 +00009792 guest_CIA_curr_instr = mkSzAddr(ty, guest_IP);
9793 guest_CIA_bbstart = mkSzAddr(ty, guest_IP - delta);
sewardj9e6491a2005-07-02 19:24:10 +00009794
sewardjc716aea2006-01-17 01:48:46 +00009795 dres = disInstr_PPC_WRK ( put_IP, resteerOkFn, callback_opaque,
sewardjdd40fdf2006-12-24 02:20:24 +00009796 delta, archinfo, abiinfo );
sewardj9e6491a2005-07-02 19:24:10 +00009797
9798 return dres;
9799}
9800
9801
sewardjc808ef72005-08-18 11:50:43 +00009802/*------------------------------------------------------------*/
9803/*--- Unused stuff ---*/
9804/*------------------------------------------------------------*/
9805
9806///* A potentially more memcheck-friendly implementation of Clz32, with
9807// the boundary case Clz32(0) = 32, which is what ppc requires. */
9808//
9809//static IRExpr* /* :: Ity_I32 */ verbose_Clz32 ( IRTemp arg )
9810//{
9811// /* Welcome ... to SSA R Us. */
9812// IRTemp n1 = newTemp(Ity_I32);
9813// IRTemp n2 = newTemp(Ity_I32);
9814// IRTemp n3 = newTemp(Ity_I32);
9815// IRTemp n4 = newTemp(Ity_I32);
9816// IRTemp n5 = newTemp(Ity_I32);
9817// IRTemp n6 = newTemp(Ity_I32);
9818// IRTemp n7 = newTemp(Ity_I32);
9819// IRTemp n8 = newTemp(Ity_I32);
9820// IRTemp n9 = newTemp(Ity_I32);
9821// IRTemp n10 = newTemp(Ity_I32);
9822// IRTemp n11 = newTemp(Ity_I32);
9823// IRTemp n12 = newTemp(Ity_I32);
9824//
9825// /* First, propagate the most significant 1-bit into all lower
9826// positions in the word. */
9827// /* unsigned int clz ( unsigned int n )
9828// {
9829// n |= (n >> 1);
9830// n |= (n >> 2);
9831// n |= (n >> 4);
9832// n |= (n >> 8);
9833// n |= (n >> 16);
9834// return bitcount(~n);
9835// }
9836// */
9837// assign(n1, mkexpr(arg));
9838// assign(n2, binop(Iop_Or32, mkexpr(n1), binop(Iop_Shr32, mkexpr(n1), mkU8(1))));
9839// assign(n3, binop(Iop_Or32, mkexpr(n2), binop(Iop_Shr32, mkexpr(n2), mkU8(2))));
9840// assign(n4, binop(Iop_Or32, mkexpr(n3), binop(Iop_Shr32, mkexpr(n3), mkU8(4))));
9841// assign(n5, binop(Iop_Or32, mkexpr(n4), binop(Iop_Shr32, mkexpr(n4), mkU8(8))));
9842// assign(n6, binop(Iop_Or32, mkexpr(n5), binop(Iop_Shr32, mkexpr(n5), mkU8(16))));
9843// /* This gives a word of the form 0---01---1. Now invert it, giving
9844// a word of the form 1---10---0, then do a population-count idiom
9845// (to count the 1s, which is the number of leading zeroes, or 32
9846// if the original word was 0. */
9847// assign(n7, unop(Iop_Not32, mkexpr(n6)));
9848//
9849// /* unsigned int bitcount ( unsigned int n )
9850// {
9851// n = n - ((n >> 1) & 0x55555555);
9852// n = (n & 0x33333333) + ((n >> 2) & 0x33333333);
9853// n = (n + (n >> 4)) & 0x0F0F0F0F;
9854// n = n + (n >> 8);
9855// n = (n + (n >> 16)) & 0x3F;
9856// return n;
9857// }
9858// */
9859// assign(n8,
9860// binop(Iop_Sub32,
9861// mkexpr(n7),
9862// binop(Iop_And32,
9863// binop(Iop_Shr32, mkexpr(n7), mkU8(1)),
9864// mkU32(0x55555555))));
9865// assign(n9,
9866// binop(Iop_Add32,
9867// binop(Iop_And32, mkexpr(n8), mkU32(0x33333333)),
9868// binop(Iop_And32,
9869// binop(Iop_Shr32, mkexpr(n8), mkU8(2)),
9870// mkU32(0x33333333))));
9871// assign(n10,
9872// binop(Iop_And32,
9873// binop(Iop_Add32,
9874// mkexpr(n9),
9875// binop(Iop_Shr32, mkexpr(n9), mkU8(4))),
9876// mkU32(0x0F0F0F0F)));
9877// assign(n11,
9878// binop(Iop_Add32,
9879// mkexpr(n10),
9880// binop(Iop_Shr32, mkexpr(n10), mkU8(8))));
9881// assign(n12,
9882// binop(Iop_Add32,
9883// mkexpr(n11),
9884// binop(Iop_Shr32, mkexpr(n11), mkU8(16))));
9885// return
9886// binop(Iop_And32, mkexpr(n12), mkU32(0x3F));
9887//}
9888
cerion896a1372005-01-25 12:24:25 +00009889/*--------------------------------------------------------------------*/
sewardjcef7d3e2009-07-02 12:21:59 +00009890/*--- end guest_ppc_toIR.c ---*/
cerion896a1372005-01-25 12:24:25 +00009891/*--------------------------------------------------------------------*/