diff mbox

Misc debug info improvements

Message ID 20110517071514.GY17079@tyan-ft48-01.lab.bos.redhat.com
State New
Headers show

Commit Message

Jakub Jelinek May 17, 2011, 7:15 a.m. UTC
On Mon, May 16, 2011 at 02:13:22PM +0200, Jakub Jelinek wrote:
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

Actually, I've missed one regression in libjava testing on x86_64-linux,
FAIL: pr26390 -O3 compilation from source
The problem is when cselib_subst_to_values substs ENTRY_VALUE for
corresponding VALUE, we should treat ENTRY_VALUE like REG in
replace_expr_with_values, otherwise we can end up with var-tracking
noting equivalence between a VALUE and the same VALUE, which causes
ICE in set_slot_part.

Updated patch below, bootstrapped/regtested on x86_64-linux and i686-linux.
Ok?

2011-05-17  Jakub Jelinek  <jakub@redhat.com>

	* cselib.c (promote_debug_loc): Allow l->next non-NULL for
	cselib_preserve_constants.
	(cselib_lookup_1): If cselib_preserve_constants,
	a new VALUE is being created for REG and there is a VALUE for the
	same register in wider mode, add another loc with lowpart SUBREG of
	the wider VALUE.
	(cselib_subst_to_values): Handle ENTRY_VALUE.
	* var-tracking.c (vt_expand_loc, vt_expand_loc_dummy): Increase
	max_depth from 8 to 10.
	(replace_expr_with_values): Return NULL for ENTRY_VALUE too.
	* dwarf2out.c (convert_descriptor_to_signed): New function.
	(mem_loc_descriptor) <case ZERO_EXTEND>: Optimize using DW_OP_and
	instead of two shifts.
	(mem_loc_descriptor) <do_shift>: ZERO_EXTEND second argument to
	the right mode if needed.
	(mem_loc_descriptor) <case MOD>: For typed ops just use DW_OP_mod.
	(mem_loc_descriptor) <case UNSIGNED_FIX>: Use
	convert_descriptor_to_signed.
	(mem_loc_descriptor) <case UDIV, CLZ, CTZ, FFS, POPCOUNT, PARITY,
	BSWAP, ROTATE, ROTATERT>: Handle these rtls.

	* gcc.dg/guality/bswaptest.c: New test.
	* gcc.dg/guality/clztest.c: New test.
	* gcc.dg/guality/ctztest.c: New test.
	* gcc.dg/guality/rotatetest.c: New test.


	Jakub
diff mbox

Patch

--- gcc/cselib.c.jj	2011-05-02 18:39:28.000000000 +0200
+++ gcc/cselib.c	2011-05-13 17:55:24.000000000 +0200
@@ -257,7 +257,7 @@  promote_debug_loc (struct elt_loc_list *
     {
       n_debug_values--;
       l->setting_insn = cselib_current_insn;
-      gcc_assert (!l->next);
+      gcc_assert (!l->next || cselib_preserve_constants);
     }
 }
 
@@ -1719,6 +1719,12 @@  cselib_subst_to_values (rtx x, enum mach
 	}
       return e->val_rtx;
 
+    case ENTRY_VALUE:
+      e = cselib_lookup (x, GET_MODE (x), 0, memmode);
+      if (! e)
+	break;
+      return e->val_rtx;
+
     case CONST_DOUBLE:
     case CONST_VECTOR:
     case CONST_INT:
@@ -1843,6 +1849,43 @@  cselib_lookup_1 (rtx x, enum machine_mod
 	  used_regs[n_used_regs++] = i;
 	  REG_VALUES (i) = new_elt_list (REG_VALUES (i), NULL);
 	}
+      else if (cselib_preserve_constants
+	       && GET_MODE_CLASS (mode) == MODE_INT)
+	{
+	  /* During var-tracking, try harder to find equivalences
+	     for SUBREGs.  If a setter sets say a DImode register
+	     and user uses that register only in SImode, add a lowpart
+	     subreg location.  */
+	  struct elt_list *lwider = NULL;
+	  l = REG_VALUES (i);
+	  if (l && l->elt == NULL)
+	    l = l->next;
+	  for (; l; l = l->next)
+	    if (GET_MODE_CLASS (GET_MODE (l->elt->val_rtx)) == MODE_INT
+		&& GET_MODE_SIZE (GET_MODE (l->elt->val_rtx))
+		   > GET_MODE_SIZE (mode)
+		&& (lwider == NULL
+		    || GET_MODE_SIZE (GET_MODE (l->elt->val_rtx))
+		       < GET_MODE_SIZE (GET_MODE (lwider->elt->val_rtx))))
+	      {
+		struct elt_loc_list *el;
+		if (i < FIRST_PSEUDO_REGISTER
+		    && hard_regno_nregs[i][GET_MODE (l->elt->val_rtx)] != 1)
+		  continue;
+		for (el = l->elt->locs; el; el = el->next)
+		  if (!REG_P (el->loc))
+		    break;
+		if (el)
+		  lwider = l;
+	      }
+	  if (lwider)
+	    {
+	      rtx sub = lowpart_subreg (mode, lwider->elt->val_rtx,
+					GET_MODE (lwider->elt->val_rtx));
+	      if (sub)
+		e->locs->next = new_elt_loc_list (e->locs->next, sub);
+	    }
+	}
       REG_VALUES (i)->next = new_elt_list (REG_VALUES (i)->next, e);
       slot = cselib_find_slot (x, e->hash, INSERT, memmode);
       *slot = e;
--- gcc/var-tracking.c.jj	2011-05-11 19:51:48.000000000 +0200
+++ gcc/var-tracking.c	2011-05-13 10:52:25.000000000 +0200
@@ -4836,7 +4836,7 @@  get_address_mode (rtx mem)
 static rtx
 replace_expr_with_values (rtx loc)
 {
-  if (REG_P (loc))
+  if (REG_P (loc) || GET_CODE (loc) == ENTRY_VALUE)
     return NULL;
   else if (MEM_P (loc))
     {
@@ -7415,7 +7415,7 @@  vt_expand_loc (rtx loc, htab_t vars, boo
   data.dummy = false;
   data.cur_loc_changed = false;
   data.ignore_cur_loc = ignore_cur_loc;
-  loc = cselib_expand_value_rtx_cb (loc, scratch_regs, 8,
+  loc = cselib_expand_value_rtx_cb (loc, scratch_regs, 10,
 				    vt_expand_loc_callback, &data);
 
   if (loc && MEM_P (loc))
@@ -7437,7 +7437,7 @@  vt_expand_loc_dummy (rtx loc, htab_t var
   data.dummy = true;
   data.cur_loc_changed = false;
   data.ignore_cur_loc = false;
-  ret = cselib_dummy_expand_value_rtx_cb (loc, scratch_regs, 8,
+  ret = cselib_dummy_expand_value_rtx_cb (loc, scratch_regs, 10,
 					  vt_expand_loc_callback, &data);
   *pcur_loc_changed = data.cur_loc_changed;
   return ret;
--- gcc/dwarf2out.c.jj	2011-05-11 19:39:04.000000000 +0200
+++ gcc/dwarf2out.c	2011-05-16 10:52:47.000000000 +0200
@@ -13824,6 +13824,37 @@  base_type_for_mode (enum machine_mode mo
   return type_die;
 }
 
+/* For OP descriptor assumed to be in unsigned MODE, convert it to a signed
+   type matching MODE, or, if MODE is narrower than DWARF2_ADDR_SIZE, signed
+   type matching DWARF2_ADDR_SIZE.  Return NULL if the conversion is not
+   possible.  */
+
+static dw_loc_descr_ref
+convert_descriptor_to_signed (enum machine_mode mode, dw_loc_descr_ref op)
+{
+  enum machine_mode outer_mode = mode;
+  dw_die_ref type_die;
+  dw_loc_descr_ref cvt;
+
+  if (GET_MODE_SIZE (mode) < DWARF2_ADDR_SIZE)
+    {
+      outer_mode = mode_for_size (DWARF2_ADDR_SIZE * BITS_PER_UNIT,
+				  MODE_INT, 0);
+      if (outer_mode == BLKmode
+	  || GET_MODE_SIZE (outer_mode) != DWARF2_ADDR_SIZE)
+	return NULL;
+    }
+  type_die = base_type_for_mode (outer_mode, 0);
+  if (type_die == NULL)
+    return NULL;
+  cvt = new_loc_descr (DW_OP_GNU_convert, 0, 0);
+  cvt->dw_loc_oprnd1.val_class = dw_val_class_die_ref;
+  cvt->dw_loc_oprnd1.v.val_die_ref.die = type_die;
+  cvt->dw_loc_oprnd1.v.val_die_ref.external = 0;
+  add_loc_descr (&op, cvt);
+  return op;
+}
+
 /* The following routine converts the RTL for a variable or parameter
    (resident in memory) into an equivalent Dwarf representation of a
    mechanism for getting the address of that same variable onto the top of a
@@ -13986,6 +14017,21 @@  mem_loc_descriptor (rtx rtl, enum machin
 				mem_mode, VAR_INIT_STATUS_INITIALIZED);
       if (op0 == 0)
 	break;
+      else if (GET_CODE (rtl) == ZERO_EXTEND
+	       && GET_MODE_SIZE (mode) <= DWARF2_ADDR_SIZE
+	       && GET_MODE_BITSIZE (GET_MODE (XEXP (rtl, 0)))
+		  < HOST_BITS_PER_WIDE_INT
+	       /* If DW_OP_const{1,2,4}u won't be used, it is shorter
+		  to expand zero extend as two shifts instead of
+		  masking.  */
+	       && GET_MODE_SIZE (GET_MODE (XEXP (rtl, 0))) <= 4)
+	{
+	  enum machine_mode imode = GET_MODE (XEXP (rtl, 0));
+	  mem_loc_result = op0;
+	  add_loc_descr (&mem_loc_result,
+			 int_loc_descriptor (GET_MODE_MASK (imode)));
+	  add_loc_descr (&mem_loc_result, new_loc_descr (DW_OP_and, 0, 0));
+	}
       else if (GET_MODE_SIZE (mode) <= DWARF2_ADDR_SIZE)
 	{
 	  int shift = DWARF2_ADDR_SIZE
@@ -14239,10 +14285,15 @@  mem_loc_descriptor (rtx rtl, enum machin
     do_shift:
       op0 = mem_loc_descriptor (XEXP (rtl, 0), mode, mem_mode,
 				VAR_INIT_STATUS_INITIALIZED);
-      op1 = mem_loc_descriptor (XEXP (rtl, 1),
-				GET_MODE (XEXP (rtl, 1)) == VOIDmode
-				? mode : GET_MODE (XEXP (rtl, 1)), mem_mode,
-				VAR_INIT_STATUS_INITIALIZED);
+      {
+	rtx rtlop1 = XEXP (rtl, 1);
+	if (GET_MODE (rtlop1) != VOIDmode
+	    && GET_MODE_BITSIZE (GET_MODE (rtlop1))
+	       < GET_MODE_BITSIZE (mode))
+	  rtlop1 = gen_rtx_ZERO_EXTEND (mode, rtlop1);
+	op1 = mem_loc_descriptor (rtlop1, mode, mem_mode,
+				  VAR_INIT_STATUS_INITIALIZED);
+      }
 
       if (op0 == 0 || op1 == 0)
 	break;
@@ -14279,6 +14330,16 @@  mem_loc_descriptor (rtx rtl, enum machin
       break;
 
     case MOD:
+      if (GET_MODE_SIZE (mode) > DWARF2_ADDR_SIZE && !dwarf_strict)
+	{
+	  /* If MODE is wider than DWARF2_ADDR_SIZE, mem_loc_descriptor
+	     should return signed typed values and therefore DW_OP_mod
+	     won't be unsigned as it defaults for untyped stack values,
+	     but signed.  */
+	  op = DW_OP_mod;
+	  goto do_binop;
+	}
+
       op0 = mem_loc_descriptor (XEXP (rtl, 0), mode, mem_mode,
 				VAR_INIT_STATUS_INITIALIZED);
       op1 = mem_loc_descriptor (XEXP (rtl, 1), mode, mem_mode,
@@ -14296,6 +14357,38 @@  mem_loc_descriptor (rtx rtl, enum machin
       add_loc_descr (&mem_loc_result, new_loc_descr (DW_OP_minus, 0, 0));
       break;
 
+    case UDIV:
+      if (!dwarf_strict && GET_MODE_CLASS (mode) == MODE_INT)
+	{
+	  dw_die_ref type_die;
+	  dw_loc_descr_ref cvt;
+
+	  type_die = base_type_for_mode (mode, 1);
+	  if (type_die == NULL)
+	    break;
+	  op0 = mem_loc_descriptor (XEXP (rtl, 0), mode, mem_mode,
+				    VAR_INIT_STATUS_INITIALIZED);
+	  op1 = mem_loc_descriptor (XEXP (rtl, 1), mode, mem_mode,
+				    VAR_INIT_STATUS_INITIALIZED);
+	  if (op0 == 0 || op1 == 0)
+	    break;
+	  cvt = new_loc_descr (DW_OP_GNU_convert, 0, 0);
+	  cvt->dw_loc_oprnd1.val_class = dw_val_class_die_ref;
+	  cvt->dw_loc_oprnd1.v.val_die_ref.die = type_die;
+	  cvt->dw_loc_oprnd1.v.val_die_ref.external = 0;
+	  add_loc_descr (&op0, cvt);
+	  cvt = new_loc_descr (DW_OP_GNU_convert, 0, 0);
+	  cvt->dw_loc_oprnd1.val_class = dw_val_class_die_ref;
+	  cvt->dw_loc_oprnd1.v.val_die_ref.die = type_die;
+	  cvt->dw_loc_oprnd1.v.val_die_ref.external = 0;
+	  add_loc_descr (&op1, cvt);
+	  mem_loc_result = op0;
+	  add_loc_descr (&mem_loc_result, op1);
+	  add_loc_descr (&mem_loc_result, new_loc_descr (DW_OP_div, 0, 0));
+	  mem_loc_result = convert_descriptor_to_signed (mode, mem_loc_result);
+	}
+      break;
+
     case NOT:
       op = DW_OP_not;
       goto do_unop;
@@ -14812,31 +14905,359 @@  mem_loc_descriptor (rtx rtl, enum machin
 	      && (GET_CODE (rtl) == UNSIGNED_FIX
 		  || GET_MODE_SIZE (mode) < DWARF2_ADDR_SIZE))
 	    {
-	      enum machine_mode outer_mode = mode;
-	      if (GET_MODE_SIZE (mode) < DWARF2_ADDR_SIZE)
-		{
-		  outer_mode = mode_for_size (DWARF2_ADDR_SIZE * BITS_PER_UNIT,
-					      MODE_INT, 0);
-		  if (outer_mode == BLKmode
-		      || GET_MODE_SIZE (outer_mode) != DWARF2_ADDR_SIZE)
-		    break;
-		}
-	      type_die = base_type_for_mode (outer_mode, 0);
-	      if (type_die == NULL)
+	      op0 = convert_descriptor_to_signed (mode, op0);
+	      if (op0 == NULL)
 		break;
-	      cvt = new_loc_descr (DW_OP_GNU_convert, 0, 0);
-	      cvt->dw_loc_oprnd1.val_class = dw_val_class_die_ref;
-	      cvt->dw_loc_oprnd1.v.val_die_ref.die = type_die;
-	      cvt->dw_loc_oprnd1.v.val_die_ref.external = 0;
-	      add_loc_descr (&op0, cvt);
 	    }
 	  mem_loc_result = op0;
 	}
       break;
 
-    case COMPARE:
+    case CLZ:
+    case CTZ:
+    case FFS:
+      /* CLZ (where constV is CLZ_DEFINED_VALUE_AT_ZERO computed value,
+	      const0 is DW_OP_lit0 or corresponding typed constant,
+	      const1 is DW_OP_lit1 or corresponding typed constant
+	      and constMSB is constant with just the MSB bit set
+	      for the mode):
+	   DW_OP_dup DW_OP_bra <L1> DW_OP_drop constV DW_OP_skip <L4>
+	 L1: const0 DW_OP_swap
+	 L2: DW_OP_dup constMSB DW_OP_and DW_OP_bra <L3> const1 DW_OP_shl
+	     DW_OP_swap DW_OP_plus_uconst <1> DW_OP_swap DW_OP_skip <L2>
+	 L3: DW_OP_drop
+	 L4: DW_OP_nop
+
+	 CTZ is similar:
+	   DW_OP_dup DW_OP_bra <L1> DW_OP_drop constV DW_OP_skip <L4>
+	 L1: const0 DW_OP_swap
+	 L2: DW_OP_dup const1 DW_OP_and DW_OP_bra <L3> const1 DW_OP_shr
+	     DW_OP_swap DW_OP_plus_uconst <1> DW_OP_swap DW_OP_skip <L2>
+	 L3: DW_OP_drop
+	 L4: DW_OP_nop
+
+	 FFS is similar:
+	   DW_OP_dup DW_OP_bra <L1> DW_OP_drop const0 DW_OP_skip <L4>
+	 L1: const1 DW_OP_swap
+	 L2: DW_OP_dup const1 DW_OP_and DW_OP_bra <L3> const1 DW_OP_shr
+	     DW_OP_swap DW_OP_plus_uconst <1> DW_OP_swap DW_OP_skip <L2>
+	 L3: DW_OP_drop
+	 L4: DW_OP_nop  */
+      if (GET_MODE_CLASS (mode) == MODE_INT
+	  && GET_MODE (XEXP (rtl, 0)) == mode
+	  && (GET_CODE (rtl) != CLZ
+	      || GET_MODE_BITSIZE (mode) <= 2 * HOST_BITS_PER_WIDE_INT))
+	{
+	  HOST_WIDE_INT valv;
+	  dw_loc_descr_ref l1jump, l1label;
+	  dw_loc_descr_ref l2jump, l2label;
+	  dw_loc_descr_ref l3jump, l3label;
+	  dw_loc_descr_ref l4jump, l4label;
+	  rtx msb;
+
+	  op0 = mem_loc_descriptor (XEXP (rtl, 0), mode, mem_mode,
+				    VAR_INIT_STATUS_INITIALIZED);
+	  if (op0 == NULL)
+	    break;
+	  if (GET_CODE (rtl) == CLZ)
+	    {
+	      if (!CLZ_DEFINED_VALUE_AT_ZERO (mode, valv))
+		valv = GET_MODE_BITSIZE (mode);
+	    }
+	  else if (GET_CODE (rtl) == FFS)
+	    valv = 0;
+	  else if (!CTZ_DEFINED_VALUE_AT_ZERO (mode, valv))
+	    valv = GET_MODE_BITSIZE (mode);
+	  add_loc_descr (&op0, new_loc_descr (DW_OP_dup, 0, 0));
+	  l1jump = new_loc_descr (DW_OP_bra, 0, 0);
+	  add_loc_descr (&op0, l1jump);
+	  add_loc_descr (&op0, new_loc_descr (DW_OP_drop, 0, 0));
+	  op1 = mem_loc_descriptor (GEN_INT (valv), mode, mem_mode,
+				    VAR_INIT_STATUS_INITIALIZED);
+	  if (op1 == NULL)
+	    break;
+	  add_loc_descr (&op0, op1);
+	  l4jump = new_loc_descr (DW_OP_skip, 0, 0);
+	  add_loc_descr (&op0, l4jump);
+	  l1label = mem_loc_descriptor (GET_CODE (rtl) == FFS
+					? const1_rtx : const0_rtx,
+					mode, mem_mode,
+					VAR_INIT_STATUS_INITIALIZED);
+	  if (l1label == NULL)
+	    break;
+	  add_loc_descr (&op0, l1label);
+	  add_loc_descr (&op0, new_loc_descr (DW_OP_swap, 0, 0));
+	  l2label = new_loc_descr (DW_OP_dup, 0, 0);
+	  add_loc_descr (&op0, l2label);
+	  if (GET_CODE (rtl) != CLZ)
+	    msb = const1_rtx;
+	  else if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT)
+	    msb = GEN_INT ((unsigned HOST_WIDE_INT) 1
+			   << (GET_MODE_BITSIZE (mode) - 1));
+	  else
+	    msb = immed_double_const (0, (unsigned HOST_WIDE_INT) 1
+					 << (GET_MODE_BITSIZE (mode)
+					     - HOST_BITS_PER_WIDE_INT - 1),
+				      mode);
+	  if (GET_CODE (msb) == CONST_INT && INTVAL (msb) < 0)
+	    op1 = new_loc_descr (HOST_BITS_PER_WIDE_INT == 32
+				 ? DW_OP_const4u
+				 : HOST_BITS_PER_WIDE_INT == 64
+				 ? DW_OP_const8u : DW_OP_constu,
+				 INTVAL (msb), 0);
+	  else
+	    op1 = mem_loc_descriptor (msb, mode, mem_mode,
+				      VAR_INIT_STATUS_INITIALIZED);
+	  if (op1 == NULL)
+	    break;
+	  add_loc_descr (&op0, op1);
+	  add_loc_descr (&op0, new_loc_descr (DW_OP_and, 0, 0));
+	  l3jump = new_loc_descr (DW_OP_bra, 0, 0);
+	  add_loc_descr (&op0, l3jump);
+	  op1 = mem_loc_descriptor (const1_rtx, mode, mem_mode,
+				    VAR_INIT_STATUS_INITIALIZED);
+	  if (op1 == NULL)
+	    break;
+	  add_loc_descr (&op0, op1);
+	  add_loc_descr (&op0, new_loc_descr (GET_CODE (rtl) == CLZ
+					      ? DW_OP_shl : DW_OP_shr, 0, 0));
+	  add_loc_descr (&op0, new_loc_descr (DW_OP_swap, 0, 0));
+	  add_loc_descr (&op0, new_loc_descr (DW_OP_plus_uconst, 1, 0));
+	  add_loc_descr (&op0, new_loc_descr (DW_OP_swap, 0, 0));
+	  l2jump = new_loc_descr (DW_OP_skip, 0, 0);
+	  add_loc_descr (&op0, l2jump);
+	  l3label = new_loc_descr (DW_OP_drop, 0, 0);
+	  add_loc_descr (&op0, l3label);
+	  l4label = new_loc_descr (DW_OP_nop, 0, 0);
+	  add_loc_descr (&op0, l4label);
+	  l1jump->dw_loc_oprnd1.val_class = dw_val_class_loc;
+	  l1jump->dw_loc_oprnd1.v.val_loc = l1label;
+	  l2jump->dw_loc_oprnd1.val_class = dw_val_class_loc;
+	  l2jump->dw_loc_oprnd1.v.val_loc = l2label;
+	  l3jump->dw_loc_oprnd1.val_class = dw_val_class_loc;
+	  l3jump->dw_loc_oprnd1.v.val_loc = l3label;
+	  l4jump->dw_loc_oprnd1.val_class = dw_val_class_loc;
+	  l4jump->dw_loc_oprnd1.v.val_loc = l4label;
+	  mem_loc_result = op0;
+	}
+      break;
+
+    case POPCOUNT:
+    case PARITY:
+      /* POPCOUNT (const0 is DW_OP_lit0 or corresponding typed constant,
+		   const1 is DW_OP_lit1 or corresponding typed constant):
+	     const0 DW_OP_swap
+	 L1: DW_OP_dup DW_OP_bra <L2> DW_OP_dup DW_OP_rot const1 DW_OP_and
+	     DW_OP_plus DW_OP_swap const1 DW_OP_shr DW_OP_skip <L1>
+	 L2: DW_OP_drop
+
+	 PARITY is similar:
+	 L1: DW_OP_dup DW_OP_bra <L2> DW_OP_dup DW_OP_rot const1 DW_OP_and
+	     DW_OP_xor DW_OP_swap const1 DW_OP_shr DW_OP_skip <L1>
+	 L2: DW_OP_drop  */
+      if (GET_MODE_CLASS (mode) == MODE_INT
+	  && GET_MODE (XEXP (rtl, 0)) == mode)
+	{
+	  dw_loc_descr_ref l1jump, l1label;
+	  dw_loc_descr_ref l2jump, l2label;
+
+	  op0 = mem_loc_descriptor (XEXP (rtl, 0), mode, mem_mode,
+				    VAR_INIT_STATUS_INITIALIZED);
+	  if (op0 == NULL)
+	    break;
+	  op1 = mem_loc_descriptor (const0_rtx, mode, mem_mode,
+				    VAR_INIT_STATUS_INITIALIZED);
+	  if (op1 == NULL)
+	    break;
+	  add_loc_descr (&op0, op1);
+	  add_loc_descr (&op0, new_loc_descr (DW_OP_swap, 0, 0));
+	  l1label = new_loc_descr (DW_OP_dup, 0, 0);
+	  add_loc_descr (&op0, l1label);
+	  l2jump = new_loc_descr (DW_OP_bra, 0, 0);
+	  add_loc_descr (&op0, l2jump);
+	  add_loc_descr (&op0, new_loc_descr (DW_OP_dup, 0, 0));
+	  add_loc_descr (&op0, new_loc_descr (DW_OP_rot, 0, 0));
+	  op1 = mem_loc_descriptor (const1_rtx, mode, mem_mode,
+				    VAR_INIT_STATUS_INITIALIZED);
+	  if (op1 == NULL)
+	    break;
+	  add_loc_descr (&op0, op1);
+	  add_loc_descr (&op0, new_loc_descr (DW_OP_and, 0, 0));
+	  add_loc_descr (&op0, new_loc_descr (GET_CODE (rtl) == POPCOUNT
+					      ? DW_OP_plus : DW_OP_xor, 0, 0));
+	  add_loc_descr (&op0, new_loc_descr (DW_OP_swap, 0, 0));
+	  op1 = mem_loc_descriptor (const1_rtx, mode, mem_mode,
+				    VAR_INIT_STATUS_INITIALIZED);
+	  add_loc_descr (&op0, op1);
+	  add_loc_descr (&op0, new_loc_descr (DW_OP_shr, 0, 0));
+	  l1jump = new_loc_descr (DW_OP_skip, 0, 0);
+	  add_loc_descr (&op0, l1jump);
+	  l2label = new_loc_descr (DW_OP_drop, 0, 0);
+	  add_loc_descr (&op0, l2label);
+	  l1jump->dw_loc_oprnd1.val_class = dw_val_class_loc;
+	  l1jump->dw_loc_oprnd1.v.val_loc = l1label;
+	  l2jump->dw_loc_oprnd1.val_class = dw_val_class_loc;
+	  l2jump->dw_loc_oprnd1.v.val_loc = l2label;
+	  mem_loc_result = op0;
+	}
+      break;
+
+    case BSWAP:
+      /* BSWAP (constS is initial shift count, either 56 or 24):
+	     constS const0
+	 L1: DW_OP_pick <2> constS DW_OP_pick <3> DW_OP_minus DW_OP_shr
+	     const255 DW_OP_and DW_OP_pick <2> DW_OP_shl DW_OP_or
+	     DW_OP_swap DW_OP_dup const0 DW_OP_eq DW_OP_bra <L2> const8
+	     DW_OP_minus DW_OP_swap DW_OP_skip <L1>
+	 L2: DW_OP_drop DW_OP_swap DW_OP_drop  */
+      if (GET_MODE_CLASS (mode) == MODE_INT
+	  && BITS_PER_UNIT == 8
+	  && (GET_MODE_BITSIZE (mode) == 32
+	      || GET_MODE_BITSIZE (mode) == 64))
+	{
+	  dw_loc_descr_ref l1jump, l1label;
+	  dw_loc_descr_ref l2jump, l2label;
+
+	  op0 = mem_loc_descriptor (XEXP (rtl, 0), mode, mem_mode,
+				    VAR_INIT_STATUS_INITIALIZED);
+	  if (op0 == NULL)
+	    break;
+
+	  op1 = mem_loc_descriptor (GEN_INT (GET_MODE_BITSIZE (mode) - 8),
+				    mode, mem_mode,
+				    VAR_INIT_STATUS_INITIALIZED);
+	  if (op1 == NULL)
+	    break;
+	  add_loc_descr (&op0, op1);
+	  op1 = mem_loc_descriptor (const0_rtx, mode, mem_mode,
+				    VAR_INIT_STATUS_INITIALIZED);
+	  if (op1 == NULL)
+	    break;
+	  add_loc_descr (&op0, op1);
+	  l1label = new_loc_descr (DW_OP_pick, 2, 0);
+	  add_loc_descr (&op0, l1label);
+	  op1 = mem_loc_descriptor (GEN_INT (GET_MODE_BITSIZE (mode) - 8),
+				    mode, mem_mode,
+				    VAR_INIT_STATUS_INITIALIZED);
+	  add_loc_descr (&op0, op1);
+	  add_loc_descr (&op0, new_loc_descr (DW_OP_pick, 3, 0));
+	  add_loc_descr (&op0, new_loc_descr (DW_OP_minus, 0, 0));
+	  add_loc_descr (&op0, new_loc_descr (DW_OP_shr, 0, 0));
+	  op1 = mem_loc_descriptor (GEN_INT (255), mode, mem_mode,
+				    VAR_INIT_STATUS_INITIALIZED);
+	  if (op1 == NULL)
+	    break;
+	  add_loc_descr (&op0, op1);
+	  add_loc_descr (&op0, new_loc_descr (DW_OP_and, 0, 0));
+	  add_loc_descr (&op0, new_loc_descr (DW_OP_pick, 2, 0));
+	  add_loc_descr (&op0, new_loc_descr (DW_OP_shl, 0, 0));
+	  add_loc_descr (&op0, new_loc_descr (DW_OP_or, 0, 0));
+	  add_loc_descr (&op0, new_loc_descr (DW_OP_swap, 0, 0));
+	  add_loc_descr (&op0, new_loc_descr (DW_OP_dup, 0, 0));
+	  op1 = mem_loc_descriptor (const0_rtx, mode, mem_mode,
+				    VAR_INIT_STATUS_INITIALIZED);
+	  add_loc_descr (&op0, op1);
+	  add_loc_descr (&op0, new_loc_descr (DW_OP_eq, 0, 0));
+	  l2jump = new_loc_descr (DW_OP_bra, 0, 0);
+	  add_loc_descr (&op0, l2jump);
+	  op1 = mem_loc_descriptor (GEN_INT (8), mode, mem_mode,
+				    VAR_INIT_STATUS_INITIALIZED);
+	  add_loc_descr (&op0, op1);
+	  add_loc_descr (&op0, new_loc_descr (DW_OP_minus, 0, 0));
+	  add_loc_descr (&op0, new_loc_descr (DW_OP_swap, 0, 0));
+	  l1jump = new_loc_descr (DW_OP_skip, 0, 0);
+	  add_loc_descr (&op0, l1jump);
+	  l2label = new_loc_descr (DW_OP_drop, 0, 0);
+	  add_loc_descr (&op0, l2label);
+	  add_loc_descr (&op0, new_loc_descr (DW_OP_swap, 0, 0));
+	  add_loc_descr (&op0, new_loc_descr (DW_OP_drop, 0, 0));
+	  l1jump->dw_loc_oprnd1.val_class = dw_val_class_loc;
+	  l1jump->dw_loc_oprnd1.v.val_loc = l1label;
+	  l2jump->dw_loc_oprnd1.val_class = dw_val_class_loc;
+	  l2jump->dw_loc_oprnd1.v.val_loc = l2label;
+	  mem_loc_result = op0;
+	}
+      break;
+
     case ROTATE:
     case ROTATERT:
+      /* ROTATE (constMASK is mode mask, BITSIZE is bitsize of mode):
+	     DW_OP_over DW_OP_over DW_OP_shl [ constMASK DW_OP_and ] DW_OP_rot
+	     [ DW_OP_swap constMASK DW_OP_and DW_OP_swap ] DW_OP_neg
+	     DW_OP_plus_uconst <BITSIZE> DW_OP_shr DW_OP_or
+
+	 ROTATERT is similar:
+	     DW_OP_over DW_OP_over DW_OP_neg DW_OP_plus_uconst <BITSIZE>
+	     DW_OP_shl [ constMASK DW_OP_and ] DW_OP_rot
+	     [ DW_OP_swap constMASK DW_OP_and DW_OP_swap ] DW_OP_shr DW_OP_or
+	 */
+      if (GET_MODE_CLASS (mode) == MODE_INT)
+	{
+	  rtx rtlop1 = XEXP (rtl, 1);
+	  dw_loc_descr_ref mask[2] = { NULL, NULL };
+	  int i;
+
+	  if (GET_MODE (rtlop1) != VOIDmode
+	      && GET_MODE_BITSIZE (GET_MODE (rtlop1))
+		 < GET_MODE_BITSIZE (mode))
+	    rtlop1 = gen_rtx_ZERO_EXTEND (mode, rtlop1);
+	  op0 = mem_loc_descriptor (XEXP (rtl, 0), mode, mem_mode,
+				    VAR_INIT_STATUS_INITIALIZED);
+	  op1 = mem_loc_descriptor (rtlop1, mode, mem_mode,
+				    VAR_INIT_STATUS_INITIALIZED);
+	  if (op0 == NULL || op1 == NULL)
+	    break;
+	  if (GET_MODE_SIZE (mode) < DWARF2_ADDR_SIZE)
+	    for (i = 0; i < 2; i++)
+	      {
+		if (GET_MODE_BITSIZE (mode) < HOST_BITS_PER_WIDE_INT)
+		  mask[i] = mem_loc_descriptor (GEN_INT (GET_MODE_MASK (mode)),
+						mode, mem_mode,
+						VAR_INIT_STATUS_INITIALIZED);
+		else if (GET_MODE_BITSIZE (mode) == HOST_BITS_PER_WIDE_INT)
+		  mask[i] = new_loc_descr (HOST_BITS_PER_WIDE_INT == 32
+					   ? DW_OP_const4u
+					   : HOST_BITS_PER_WIDE_INT == 64
+					   ? DW_OP_const8u : DW_OP_constu,
+					   GET_MODE_MASK (mode), 0);
+		else
+		  mask[i] = NULL;
+		if (mask[i] == NULL)
+		  return NULL;
+		add_loc_descr (&mask[i], new_loc_descr (DW_OP_and, 0, 0));
+	      }
+	  add_loc_descr (&op0, op1);
+	  add_loc_descr (&op0, new_loc_descr (DW_OP_over, 0, 0));
+	  add_loc_descr (&op0, new_loc_descr (DW_OP_over, 0, 0));
+	  if (GET_CODE (rtl) == ROTATERT)
+	    {
+	      add_loc_descr (&op0, new_loc_descr (DW_OP_neg, 0, 0));
+	      add_loc_descr (&op0, new_loc_descr (DW_OP_plus_uconst,
+						  GET_MODE_BITSIZE (mode), 0));
+	    }
+	  add_loc_descr (&op0, new_loc_descr (DW_OP_shl, 0, 0));
+	  if (mask[0] != NULL)
+	    add_loc_descr (&op0, mask[0]);
+	  add_loc_descr (&op0, new_loc_descr (DW_OP_rot, 0, 0));
+	  if (mask[1] != NULL)
+	    {
+	      add_loc_descr (&op0, new_loc_descr (DW_OP_swap, 0, 0));
+	      add_loc_descr (&op0, mask[1]);
+	      add_loc_descr (&op0, new_loc_descr (DW_OP_swap, 0, 0));
+	    }
+	  if (GET_CODE (rtl) == ROTATE)
+	    {
+	      add_loc_descr (&op0, new_loc_descr (DW_OP_neg, 0, 0));
+	      add_loc_descr (&op0, new_loc_descr (DW_OP_plus_uconst,
+						  GET_MODE_BITSIZE (mode), 0));
+	    }
+	  add_loc_descr (&op0, new_loc_descr (DW_OP_shr, 0, 0));
+	  add_loc_descr (&op0, new_loc_descr (DW_OP_or, 0, 0));
+	  mem_loc_result = op0;
+	}
+      break;
+
+    case COMPARE:
     case TRUNCATE:
       /* In theory, we could implement the above.  */
       /* DWARF cannot represent the unsigned compare operations
@@ -14856,7 +15277,6 @@  mem_loc_descriptor (rtx rtl, enum machin
     case US_ASHIFT:
     case SS_TRUNCATE:
     case US_TRUNCATE:
-    case UDIV:
     case UNORDERED:
     case ORDERED:
     case UNEQ:
@@ -14870,12 +15290,6 @@  mem_loc_descriptor (rtx rtl, enum machin
     case SAT_FRACT:
     case UNSIGNED_SAT_FRACT:
     case SQRT:
-    case BSWAP:
-    case FFS:
-    case CLZ:
-    case CTZ:
-    case POPCOUNT:
-    case PARITY:
     case ASM_OPERANDS:
     case VEC_MERGE:
     case VEC_SELECT:
--- gcc/testsuite/gcc.dg/guality/bswaptest.c.jj	2011-05-13 22:20:24.000000000 +0200
+++ gcc/testsuite/gcc.dg/guality/bswaptest.c	2011-05-13 22:20:40.000000000 +0200
@@ -0,0 +1,32 @@ 
+/* { dg-do run { target { x86_64-*-* && lp64 } } } */
+/* { dg-options "-g" } */
+
+volatile int vv;
+
+__attribute__((noclone, noinline)) long
+foo (long x)
+{
+  long f = __builtin_bswap64 (x);
+  long g = f;
+  asm volatile ("" : "+r" (f));
+  vv++;		/* { dg-final { gdb-test 12 "g" "f" } } */
+  return f;
+}
+
+__attribute__((noclone, noinline)) int
+bar (int x)
+{
+  int f = __builtin_bswap32 (x);
+  int g = f;
+  asm volatile ("" : "+r" (f));
+  vv++;		/* { dg-final { gdb-test 22 "g" "f" } } */
+  return f;
+}
+
+int
+main ()
+{
+  foo (0x123456789abcde0fUL);
+  bar (0x12345678);
+  return 0;
+}
--- gcc/testsuite/gcc.dg/guality/clztest.c.jj	2011-05-13 14:09:41.000000000 +0200
+++ gcc/testsuite/gcc.dg/guality/clztest.c	2011-05-13 14:07:02.000000000 +0200
@@ -0,0 +1,33 @@ 
+/* { dg-do run { target { x86_64-*-* && lp64 } } } */
+/* { dg-options "-g" } */
+
+volatile int vv;
+
+__attribute__((noinline, noclone)) long
+foo (long x)
+{
+  long f = __builtin_clzl (x);
+  long g = f;
+  asm volatile ("" : "+r" (f));
+  vv++;		/* { dg-final { gdb-test 12 "g" "f" } } */
+  return f;
+}
+
+__attribute__((noinline, noclone)) long
+bar (long x)
+{
+  long f = __builtin_clzl (x);
+  long g = f;
+  asm volatile ("" : "+r" (f));
+  vv++;		/* { dg-final { gdb-test 22 "g" "f" } } */
+  return f;
+}
+
+int
+main ()
+{
+  long x = vv;
+  foo (x + 0x123456UL);
+  bar (x + 0x7fffffffUL);
+  return 0;
+}
--- gcc/testsuite/gcc.dg/guality/ctztest.c.jj	2011-05-13 14:29:43.000000000 +0200
+++ gcc/testsuite/gcc.dg/guality/ctztest.c	2011-05-13 14:28:25.000000000 +0200
@@ -0,0 +1,33 @@ 
+/* { dg-do run { target { x86_64-*-* && lp64 } } } */
+/* { dg-options "-g" } */
+
+volatile int vv;
+
+__attribute__((noinline, noclone)) long
+foo (long x)
+{
+  long f = __builtin_ctzl (x);
+  long g = f;
+  asm volatile ("" : "+r" (f));
+  vv++;		/* { dg-final { gdb-test 12 "g" "f" } } */
+  return f;
+}
+
+__attribute__((noinline, noclone)) long
+bar (long x)
+{
+  long f = __builtin_ctzl (x);
+  long g = f;
+  asm volatile ("" : "+r" (f));
+  vv++;		/* { dg-final { gdb-test 22 "g" "f" } } */
+  return f;
+}
+
+int
+main ()
+{
+  long x = vv;
+  foo (x + 0x1234560UL);
+  bar (x + 0x7fff8000UL);
+  return 0;
+}
--- gcc/testsuite/gcc.dg/guality/rotatetest.c.jj	2011-05-16 10:58:45.000000000 +0200
+++ gcc/testsuite/gcc.dg/guality/rotatetest.c	2011-05-16 10:57:43.000000000 +0200
@@ -0,0 +1,76 @@ 
+/* { dg-do run { target { x86_64-*-* && lp64 } } } */
+/* { dg-options "-g" } */
+
+volatile int vv;
+
+__attribute__((noclone, noinline)) long
+f1 (unsigned long x)
+{
+  long f = (x << 12) | (x >> (64 - 12));
+  long g = f;
+  asm volatile ("" : "+r" (f));
+  vv++;		/* { dg-final { gdb-test 12 "g" "f" } } */
+  return f;
+}
+
+__attribute__((noclone, noinline)) long
+f2 (unsigned long x, int y)
+{
+  long f = (x << y) | (x >> (64 - y));
+  long g = f;
+  asm volatile ("" : "+r" (f));
+  vv++;		/* { dg-final { gdb-test 22 "g" "f" } } */
+  return f;
+}
+
+__attribute__((noclone, noinline)) long
+f3 (unsigned long x, int y)
+{
+  long f = (x >> y) | (x << (64 - y));
+  long g = f;
+  asm volatile ("" : "+r" (f));
+  vv++;		/* { dg-final { gdb-test 32 "g" "f" } } */
+  return f;
+}
+
+__attribute__((noclone, noinline)) unsigned int
+f4 (unsigned int x)
+{
+  unsigned int f = (x << 12) | (x >> (32 - 12));
+  unsigned int g = f;
+  asm volatile ("" : "+r" (f));
+  vv++;		/* { dg-final { gdb-test 42 "g" "f" } } */
+  return f;
+}
+
+__attribute__((noclone, noinline)) unsigned int
+f5 (unsigned int x, int y)
+{
+  unsigned int f = (x << y) | (x >> (64 - y));
+  unsigned int g = f;
+  asm volatile ("" : "+r" (f));
+  vv++;		/* { dg-final { gdb-test 52 "g" "f" } } */
+  return f;
+}
+
+__attribute__((noclone, noinline)) unsigned int
+f6 (unsigned int x, int y)
+{
+  unsigned int f = (x >> y) | (x << (64 - y));
+  unsigned int g = f;
+  asm volatile ("" : "+r" (f));
+  vv++;		/* { dg-final { gdb-test 62 "g" "f" } } */
+  return f;
+}
+
+int
+main ()
+{
+  f1 (0x123456789abcde0fUL);
+  f2 (0x123456789abcde0fUL, 18);
+  f3 (0x123456789abcde0fUL, 17);
+  f4 (0x12345678);
+  f5 (0x12345678, 18);
+  f6 (0x12345678, 17);
+  return 0;
+}