putGST_masked: correctly handle the case where the mask is for
FPSCR.RN or FPSCR.DRN, but does not cover the entire field.  Then it
is important to update the exposed parts but leave the not-exposed
parts unchanged.  This is a regression relative to circa 5 years ago.



git-svn-id: svn://svn.valgrind.org/vex/trunk@2929 8f6e269a-dfd6-0310-a8e1-e2731360e62c
diff --git a/priv/guest_ppc_toIR.c b/priv/guest_ppc_toIR.c
index b561de2..fc289ed 100644
--- a/priv/guest_ppc_toIR.c
+++ b/priv/guest_ppc_toIR.c
@@ -2933,21 +2933,59 @@
    switch (reg) {
    case PPC_GST_FPSCR: {
       /* Allow writes to either binary or decimal floating point
-       * Rounding Mode
-       */
+         Rounding Mode.
+      */
+      /* If any part of |mask| covers FPSCR.RN, update the bits of
+         FPSCR.RN by copying in |src| for locations where the
+         corresponding bit in |mask| is 1, and leaving it unchanged
+         for corresponding |mask| zero bits. */
       if (mask & MASK_FPSCR_RN) {
-         stmt( IRStmt_Put( OFFB_FPROUND,
-                           unop( Iop_32to8,
-                                 binop( Iop_And32,
-                                        unop( Iop_64to32, src ),
-                                        mkU32( MASK_FPSCR_RN & mask ) ) ) ) );
-      } else if (mask & MASK_FPSCR_DRN) {
-         stmt( IRStmt_Put( OFFB_DFPROUND,
-                           unop( Iop_32to8,
-                                 binop( Iop_And32,
-                                        unop( Iop_64HIto32, src ),
-                                        mkU32( ( MASK_FPSCR_DRN & mask )
-                                                 >> 32 ) ) ) ) );
+         stmt( 
+            IRStmt_Put(
+               OFFB_FPROUND,
+               unop(
+                  Iop_32to8,
+                  binop(
+                     Iop_Or32, 
+                     binop(
+                        Iop_And32,
+                        unop(Iop_64to32, src),
+                        mkU32(MASK_FPSCR_RN & mask)
+                     ),
+                     binop(
+                        Iop_And32, 
+                        unop(Iop_8Uto32, IRExpr_Get(OFFB_FPROUND,Ity_I8)),
+                        mkU32(MASK_FPSCR_RN & ~mask)
+                     )
+                  )
+               )
+            )
+         );
+      }
+      /* Similarly, update FPSCR.DRN if any bits of |mask|
+         corresponding to FPSCR.DRN are set. */
+      if (mask & MASK_FPSCR_DRN) {
+         stmt( 
+            IRStmt_Put(
+               OFFB_DFPROUND,
+               unop(
+                  Iop_32to8,
+                  binop(
+                     Iop_Or32, 
+                     binop(
+                        Iop_And32,
+                        unop(Iop_64HIto32, src),
+                        mkU32((MASK_FPSCR_DRN & mask) >> 32)
+                     ),
+                     binop(
+                        Iop_And32, 
+                        unop(Iop_8Uto32, IRExpr_Get(OFFB_DFPROUND,Ity_I8)),
+                        mkU32((MASK_FPSCR_DRN & ~mask) >> 32)
+                     )
+                  )
+               )
+            )
+         );
       }
 
       /* Give EmNote for attempted writes to: