This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

PR 35232: Recent regression due to reload inheritance bug


fp-int-convert-double.c ICEs on mipsisa64-elf when compiled with
"-O2 -mabi=o64 -mips16".  This is a recent failure; it's a latent
reload inheritance bug exposed by:

2008-02-06  Uros Bizjak  <ubizjak@gmail.com>

        PR target/35083
        * optabs.c (expand_float): Do not check for decimal modes when
        expanding unsigned integer through signed conversion.

We have the following pre-reload insn:

(insn 1749 1748 1750 193 ../../../gcc/gcc/testsuite/gcc.dg/torture/fp-int-convert-double.c:12 (set (reg:SI 1475 [ ivout.331+-3 ])
        (zero_extend:SI (reg:QI 2209 [ ivout ]))) 147 {*zero_extendqisi2_mips16e} (expr_list:REG_DEAD (reg:QI 2209 [ ivout ])
        (nil)))

and MIPS16 zero extensions require the input operand to match the output.
The insn gets the following reload:

Reloads for insn # 1749
Reload 0: reload_in (QI) = (reg:QI 2 $2 [orig:2209 ivout ] [2209])
        reload_out (SI) = (reg:SI 1475 [ ivout.331+-3 ])
        M16_REGS, RELOAD_OTHER (opnum = 0)
        reload_in_reg: (reg:QI 2 $2 [orig:2209 ivout ] [2209])
        reload_out_reg: (reg:SI 1475 [ ivout.331+-3 ])
        reload_reg_rtx: (reg:QI 2 $2 [orig:2209 ivout ] [2209])

Note that the reload_reg_rtx is QImode.  This is valid, because
(reg:QI 2) and (reg:SI 2) occupy the same number of registers
(and the code that chose (reg:QI 2) did check this).  But we
then record inheritance information as though we had a copy
of the form:

   (reg:SI 1475) <- (reg:QI 2)

(so reg_last_reload_reg[1475] = (reg:QI 2), etc.).  The next use
of (reg:SI 1475) is in a HImode lowpart:

   (subreg:HI (reg:SI 1475) 2)

so when inheriting the previous reload of 1475, we try to reduce:

   (subreg:HI (reg:QI 2) 2)

leading to an ICE because of the bogus byte offset.

In general, if we have an in-out reload, the input side may use the
reload register in a different mode from the output side.  The problem
is that, when recording information for inheritance purposes,
we assume that the two modes are the same.

In this case, the problem is somewhat benign: we're recording the
right registers (and the right _number_ of registers), but we're doing
it in the wrong mode.  This isn't always the case though.  You can see
the same thing in a wrong-code scenario with this testcase on big-endian
32-bit MIPS systems:

--------------------------------------------------------------------
__attribute__((nomips16)) unsigned int
f1 (unsigned long long x)
{
  unsigned int r;
  asm ("# %0" : "=a" (r) : "0" (x));
  asm ("# %0" : "=h" (r) : "0" (r));
  return r;
}

int
main (void)
{
  return f1 (4) != 4;
}
--------------------------------------------------------------------

The first asm needs an in-out reload with a 64-bit input (x) and a
32-bit output (r), so the reload register itself is 64 bits wide.
emit_output_reload_insns correctly adjusts the reload register
for the output mode, creating a move from (reg:SI lo) to "r".
However, emit_reload_insns records the copy for inheritance purposes
as though it used the unadjusted reg_rtx, (reg:DI hi).  This means that,
when processing the second asm, choose_reload_regs thinks that "r" is
already in (reg:SI hi).

The fix I've gone for is to introduce two new arrays,
reload_reg_rtx_for_input and reload_reg_rtx_for_output.
do_input_reload sets reload_reg_rtx_for_input[r] to the
input form of rld[r].reg_rtx and do_output_reload sets
reload_reg_rtx_for_output[r] to the output form.
The new arrays are short-lived (like reload_inheritance_insn & co.)
so there's no need to bloat struct reload with this information.

The logic in emit_reload_insns then becomes:

  for each reload r
    if (rld[r].reg_rtx is a spill register)
      {
        clear all inheritance info for rld[r].reg_rtx;
        if (reload r is an output reload)
          record output reload involving reload_reg_rtx_for_output[r];
        else if (reload r is an input reload)
          record input reload involving reload_reg_rtx_for_input[r];
      }
    else if (rld[r].reg_rtx is not a spill register
             && reload r is an output reload)
      record output reload involving reload_reg_rtx_for_output[r];

Various other bits of code in the emit_reload_insns hierachy need
to be adjusted too:

- delete_output_reload has:

  --------------------------------------------------------------------------
  if (rld[j].out != rld[j].in
      && REG_N_DEATHS (REGNO (reg)) == 1
      && REG_N_SETS (REGNO (reg)) == 1
      && REG_BASIC_BLOCK (REGNO (reg)) >= NUM_FIXED_BLOCKS
      && find_regno_note (insn, REG_DEAD, REGNO (reg)))
    {
      ...
      /* For the debugging info, say the pseudo lives in this reload reg.  */
      reg_renumber[REGNO (reg)] = REGNO (rld[j].reg_rtx);
  --------------------------------------------------------------------------

  The caller has determined that either an input reload or an output reload
  has made a previous reload redundant.  The rld[j].reg_rtx above should
  then be the input reload register or the output reload register respectively.

  I've added a new rtx parameter to delete_output_reload, "new_reload_reg",
  to go alongside the existing "last_reload_reg".

- emit_input_reload_insns has similar code:

	      /* If these are the only uses of the pseudo reg,
		 pretend for GDB it lives in the reload reg we used.  */
	      if (REG_N_DEATHS (REGNO (old)) == 1
		  && REG_N_SETS (REGNO (old)) == 1)
		{
		  reg_renumber[REGNO (old)] = REGNO (rl->reg_rtx);
		  alter_reg (REGNO (old), -1);
		}

  Again, this should use the input reload register instead of
  the unadjusted rl->reg_rtx.

- emit_output_reload_insns looks through the output reload insns to
  look for stores of the reload register.  This search should use
  the output reload register rather than the unadjusted register.

- do_input_reload should use the input reload register (rather than
  the unadjusted register) when deciding whether the reload would be
  a no-op.

- do_input_reload should use the input reload register when deciding
  whether the reload makes a previous output reload unnecessary.

- do_output_reload should use the output reload register rather than
  the unadjusted reload register in REG_UNUSED notes.

Some of these changes are for correctness.  The others effectively
just fix missed optimisation opportunities, but I've included them for
consistency.  The idea is that emit_reload_insns should always use the
relevant input or output reload register rather than the unadjusted
form.  It seems better to make the change across the board than to
try to justify on a case-by-case whether using the wrong register
would actually lead to wrong code, rather than simply being
conceptually wrong.

Code outside of emit_reload_insns also treats reg_rtx as suitable for
both the input and output, but this is usually for dependency analysis,
where (unlike here) the assumption is conservatively correct.  reg_rtx is
wide enough for reloads in both directions.

Code like find_dummy_reload tends to be mindful of mode differences,
doing nothing if the input reload occupies a different number of words
from the output reload register.

A few other notes:

- I've moved the mode and register calculations from
  emit_{input,output}_reload_insns to do_{input,output}_reload as-is.
  Some of the commentary may or may not be out of date, it may or may
  not be safe to use gen_lowpart_common these days, and the "output is
  VOIDmode" code may or may not be handled in a more elegant way these
  days.  I think any changes in those areas should be handled
  separately from this patch.

- Changing the handling of spill registers in emit_reload_insns meant
  conflating two loops to clear reg_reloaded_valid.  One of these loops
  also clobbered reg_reloaded_call_part_clobbered while the other didn't,
  and there was no explanation why.  I had to decide what the new loop
  should do.

  Logically, reg_reloaded_call_part_clobbered should only be meaningful
  for members of reg_reloaded_valid, and it is easy to see that this is
  indeed the case.  I therefore moved the code to clear
  reg_reloaded_call_part_clobbered to the code that sets
  reg_reloaded_valid; each such piece of code had a statement
  of the form:

     if (HARD_REGNO_CALL_PART_CLOBBERED (r, m))
       SET_HARD_REG_BIT (reg_reloaded_call_part_clobbered, r);

  which I turned into:

     if (HARD_REGNO_CALL_PART_CLOBBERED (r, m))
       SET_HARD_REG_BIT (reg_reloaded_call_part_clobbered, r);
     else
       CLEAR_HARD_REG_BIT (reg_reloaded_call_part_clobbered, r);

  (these being the only bits of code to _set_ bits in the variable).
  I tweaked the comment above reg_reloaded_call_part_clobbered as well.

- It seems more robust to set reload_reg_rtx_for_{input,output}
  unconditionally -- setting them to null if no reload is needed --
  than to try to be selective.  For example, inc/dec reloads have an
  output reload, but that reload is handled by emit_input_reload_insns
  rather than emit_output_reload_insns.  We should nevertheless set
  reload_reg_rtx_for_output to the output register used.

Er... well, that wasn't short.  I hope it doesn't put anyone off.
I think this is actually a relatively straight-forward change as
far as reload changes go.  I just wanted to describe it in even
more painful detail than usual because of the late stage we're at.

That said, I understand that making reload changes so close to a release
may not seem like a good idea.  If you don't want to apply a patch like
this now, what should we do with the PR?  On the one hand, I think
everyone agrees Uros's fix is correct in itself.  On the other hand,
PR 35083 is "only" for soft-float x86, and the ICE itself was fixed by:

2008-02-05  Uros Bizjak  <ubizjak@gmail.com>

	PR target/35083
	* config/i386/i386.md (floatunsisf2): Enable for TARGET_SSE_MATH only.
	Call ix86_expand_convert_uns_sisf_sse for TARGET_SSE2.

The 2008-02-06 part is a follow-on to fix a code-size regression from 4.2.
Also, mipsisa64-elf is a primary target, and I've been trying hard to get
us down to zero failures on MIPS targets.  (I've been rotating through
quite a few different configurations -- so many to test! -- which is why
I only picked this up a week after Uros's patch.)

If you're uncomfortable with my patch, perhaps one option would be to
revert Uros's optabs patch for now, on the understanding that both his
patch and whatever patch we pick for this PR can go in before 4.3.1.
I'd obviously prefer to fix the underlying bug (and keep Uros's patch)
if we can though.

Anyway, bootstrapped & regression-tested on x86_64-linux-gnu.
Also regression-tested on mipsisa64-elfoabi.  I compared the
output of gcc.c-torture, gcc.dg and g++.dg on mipsisa64-elfoabi
before and after the patch at -O2 and -O2 -mips16.  The script
I use for this is supposed to be an "any target" thing and doesn't
work for tests that include standard headers, including
fp-int-convert-double.c.  Still, a lot of tests don't use standard
headers, and there were no changes in the assembly output for them.
So, maybe a weak test, but not completely useless...

Richard


gcc/
	PR rtl-optimization/35232
	* reload1.c (reg_reloaded_call_part_clobbered): Clarify comment.
	(forget_old_reloads_1, forget_marked_reloads): Don't clear
	reg_reloaded_call_part_clobbered here.
	(reload_regs_reach_end_p): New function.
	(reload_reg_rtx_for_input): New variable.
	(reload_reg_rtx_for_output): Likewise.
	(emit_input_reload_insns): Use reloadreg rather than rl->reg_rtx
	when reassigning a pseudo register.  Load reloadreg from 
	reload_reg_rtx_for_input, moving the mode and register
	calculation to...
	(do_input_reload): ...here.  Use the mode-adjusted reg_rtx
	instead of the original when deciding whether an input reload
	would be a no-op or whether an output reload can be deleted.
	(emit_output_reload_insns): Use the mode-adjusted reg_rtx
	when setting up new_spill_reg_store.  Load it from
	reload_reg_rtx_for_output, moving the mode and register
	calculation to...
	(do_output_reload): ...here.  Use the mode-adjusted reg_rtx
	instead of the original when deciding whether an output reload
	would be a no-op.  Do the same when modifying insn notes.
	(inherit_piecemeal_p): Take a mode and two register numbers
	as argument.
	(emit_reload_insns): Clear new_spill_reg_store for every hard
	register in the reload register.  Remove spill registers
	from reg_reloaded_valid before considering whether to record
	inheritance information for them.  Use reload_reg_rtx_for_output
	instead of reg_rtx when recording output reloads.  Use
	reload_reg_rtx_for_input instead of reg_rtx when recording
	input reloads.  Set or clear reg_reloaded_call_part_clobbered
	at the same time as setting reg_reloaded_valid.
	(delete_output_reload): Add a new_reload_reg parameter and use it
	instead of rld[j].reg_rtx.
	(emit_input_reload_insns, do_input_reload, do_output_reload): Adjust
	calls accordingly.

gcc/testsuite/
	PR rtl-optimization/35232
	* gcc.target/mips/pr35232.c: New test.

Index: gcc/reload1.c
===================================================================
--- gcc/reload1.c	2008-02-17 22:30:14.000000000 +0000
+++ gcc/reload1.c	2008-02-18 17:40:51.000000000 +0000
@@ -158,7 +158,7 @@ VEC(rtx,gc) *reg_equiv_memory_loc_vec;
 
 /* Indicate whether the register's current value is one that is not
    safe to retain across a call, even for registers that are normally
-   call-saved.  */
+   call-saved.  This is only meaningful for members of reg_reloaded_valid.  */
 static HARD_REG_SET reg_reloaded_call_part_clobbered;
 
 /* Number of spill-regs so far; number of valid elements of spill_regs.  */
@@ -434,9 +434,8 @@ static void emit_output_reload_insns (st
 				      int);
 static void do_input_reload (struct insn_chain *, struct reload *, int);
 static void do_output_reload (struct insn_chain *, struct reload *, int);
-static bool inherit_piecemeal_p (int, int);
 static void emit_reload_insns (struct insn_chain *);
-static void delete_output_reload (rtx, int, int);
+static void delete_output_reload (rtx, int, int, rtx);
 static void delete_address_reloads (rtx, rtx);
 static void delete_address_reloads_1 (rtx, rtx, rtx);
 static rtx inc_for_reload (rtx, rtx, rtx, int);
@@ -4371,7 +4370,6 @@ forget_old_reloads_1 (rtx x, const_rtx i
 	      || ! TEST_HARD_REG_BIT (reg_is_output_reload, regno + i))
 	    {
 	      CLEAR_HARD_REG_BIT (reg_reloaded_valid, regno + i);
-	      CLEAR_HARD_REG_BIT (reg_reloaded_call_part_clobbered, regno + i);
 	      spill_reg_store[regno + i] = 0;
 	    }
     }
@@ -4408,7 +4406,6 @@ forget_marked_reloads (regset regs)
 	      || ! TEST_HARD_REG_BIT (reg_is_output_reload, reg)))
 	  {
 	    CLEAR_HARD_REG_BIT (reg_reloaded_valid, reg);
-	    CLEAR_HARD_REG_BIT (reg_reloaded_call_part_clobbered, reg);
 	    spill_reg_store[reg] = 0;
 	  }
       if (n_reloads == 0
@@ -4922,6 +4919,21 @@ reload_reg_reaches_end_p (unsigned int r
       gcc_unreachable ();
     }
 }
+
+/* Like reload_reg_reaches_end_p, but check that the condition holds for
+   every register in the range [REGNO, REGNO + NR).  */
+
+static bool
+reload_regs_reach_end_p (unsigned int regno, int nr,
+			 int opnum, enum reload_type type)
+{
+  int i;
+
+  for (i = 0; i < nr; i++)
+    if (!reload_reg_reaches_end_p (regno + i, opnum, type))
+      return false;
+  return true;
+}
 
 
 /*  Returns whether R1 and R2 are uniquely chained: the value of one
@@ -5061,6 +5073,12 @@ reloads_conflict (int r1, int r2)
    or -1 if we did not need a register for this reload.  */
 static int reload_spill_index[MAX_RELOADS];
 
+/* Index X is the value of rld[X].reg_rtx, adjusted for the input mode.  */
+static rtx reload_reg_rtx_for_input[MAX_RELOADS];
+
+/* Index X is the value of rld[X].reg_rtx, adjusted for the output mode.  */
+static rtx reload_reg_rtx_for_output[MAX_RELOADS];
+
 /* Subroutine of free_for_value_p, used to check a single register.
    START_REGNO is the starting regno of the full reload register
    (possibly comprising multiple hard registers) that we are considering.  */
@@ -6559,42 +6577,6 @@ emit_input_reload_insns (struct insn_cha
   enum machine_mode mode;
   rtx *where;
 
-  /* Determine the mode to reload in.
-     This is very tricky because we have three to choose from.
-     There is the mode the insn operand wants (rl->inmode).
-     There is the mode of the reload register RELOADREG.
-     There is the intrinsic mode of the operand, which we could find
-     by stripping some SUBREGs.
-     It turns out that RELOADREG's mode is irrelevant:
-     we can change that arbitrarily.
-
-     Consider (SUBREG:SI foo:QI) as an operand that must be SImode;
-     then the reload reg may not support QImode moves, so use SImode.
-     If foo is in memory due to spilling a pseudo reg, this is safe,
-     because the QImode value is in the least significant part of a
-     slot big enough for a SImode.  If foo is some other sort of
-     memory reference, then it is impossible to reload this case,
-     so previous passes had better make sure this never happens.
-
-     Then consider a one-word union which has SImode and one of its
-     members is a float, being fetched as (SUBREG:SF union:SI).
-     We must fetch that as SFmode because we could be loading into
-     a float-only register.  In this case OLD's mode is correct.
-
-     Consider an immediate integer: it has VOIDmode.  Here we need
-     to get a mode from something else.
-
-     In some cases, there is a fourth mode, the operand's
-     containing mode.  If the insn specifies a containing mode for
-     this operand, it overrides all others.
-
-     I am not sure whether the algorithm here is always right,
-     but it does the right things in those cases.  */
-
-  mode = GET_MODE (old);
-  if (mode == VOIDmode)
-    mode = rl->inmode;
-
   /* delete_output_reload is only invoked properly if old contains
      the original pseudo register.  Since this is replaced with a
      hard reg when RELOAD_OVERRIDE_IN is set, see if we can
@@ -6612,6 +6594,9 @@ emit_input_reload_insns (struct insn_cha
   else if (GET_CODE (oldequiv) == SUBREG)
     oldequiv_reg = SUBREG_REG (oldequiv);
 
+  reloadreg = reload_reg_rtx_for_input[j];
+  mode = GET_MODE (reloadreg);
+
   /* If we are reloading from a register that was recently stored in
      with an output-reload, see if we can prove there was
      actually no need to store the old value in it.  */
@@ -6623,16 +6608,11 @@ emit_input_reload_insns (struct insn_cha
       && (dead_or_set_p (insn, spill_reg_stored_to[REGNO (oldequiv)])
 	  || rtx_equal_p (spill_reg_stored_to[REGNO (oldequiv)],
 			  rl->out_reg)))
-    delete_output_reload (insn, j, REGNO (oldequiv));
+    delete_output_reload (insn, j, REGNO (oldequiv), reloadreg);
 
-  /* Encapsulate both RELOADREG and OLDEQUIV into that mode,
-     then load RELOADREG from OLDEQUIV.  Note that we cannot use
-     gen_lowpart_common since it can do the wrong thing when
-     RELOADREG has a multi-word mode.  Note that RELOADREG
-     must always be a REG here.  */
+  /* Encapsulate OLDEQUIV into the reload mode, then load RELOADREG from
+     OLDEQUIV.  */
 
-  if (GET_MODE (reloadreg) != mode)
-    reloadreg = reload_adjust_reg_for_mode (reloadreg, mode);
   while (GET_CODE (oldequiv) == SUBREG && GET_MODE (oldequiv) != mode)
     oldequiv = SUBREG_REG (oldequiv);
   if (GET_MODE (oldequiv) != VOIDmode
@@ -6696,7 +6676,7 @@ emit_input_reload_insns (struct insn_cha
 			     spill_reg_stored_to[REGNO (oldequiv)])
 	      || rtx_equal_p (spill_reg_stored_to[REGNO (oldequiv)],
 			      old)))
-	delete_output_reload (insn, j, REGNO (oldequiv));
+	delete_output_reload (insn, j, REGNO (oldequiv), reloadreg);
 
       /* Prevent normal processing of this reload.  */
       special = 1;
@@ -6756,7 +6736,7 @@ emit_input_reload_insns (struct insn_cha
 	      if (REG_N_DEATHS (REGNO (old)) == 1
 		  && REG_N_SETS (REGNO (old)) == 1)
 		{
-		  reg_renumber[REGNO (old)] = REGNO (rl->reg_rtx);
+		  reg_renumber[REGNO (old)] = REGNO (reloadreg);
 		  alter_reg (REGNO (old), -1);
 		}
 	      special = 1;
@@ -7015,35 +6995,23 @@ emit_input_reload_insns (struct insn_cha
 emit_output_reload_insns (struct insn_chain *chain, struct reload *rl,
 			  int j)
 {
-  rtx reloadreg = rl->reg_rtx;
+  rtx reloadreg;
   rtx insn = chain->insn;
   int special = 0;
   rtx old = rl->out;
-  enum machine_mode mode = GET_MODE (old);
+  enum machine_mode mode;
   rtx p;
+  rtx rl_reg_rtx;
 
   if (rl->when_needed == RELOAD_OTHER)
     start_sequence ();
   else
     push_to_sequence (output_reload_insns[rl->opnum]);
 
-  /* Determine the mode to reload in.
-     See comments above (for input reloading).  */
-
-  if (mode == VOIDmode)
-    {
-      /* VOIDmode should never happen for an output.  */
-      if (asm_noperands (PATTERN (insn)) < 0)
-	/* It's the compiler's fault.  */
-	fatal_insn ("VOIDmode on an output", insn);
-      error_for_asm (insn, "output operand is constant in %<asm%>");
-      /* Prevent crash--use something we know is valid.  */
-      mode = word_mode;
-      old = gen_rtx_REG (mode, REGNO (reloadreg));
-    }
+  rl_reg_rtx = reload_reg_rtx_for_output[j];
+  mode = GET_MODE (rl_reg_rtx);
 
-  if (GET_MODE (reloadreg) != mode)
-    reloadreg = reload_adjust_reg_for_mode (reloadreg, mode);
+  reloadreg = rl_reg_rtx;
 
   /* If we need two reload regs, set RELOADREG to the intermediate
      one, since it will be stored into OLD.  We might need a secondary
@@ -7167,12 +7135,12 @@ emit_output_reload_insns (struct insn_ch
 	   reg_has_output_reload will make this do nothing.  */
 	note_stores (pat, forget_old_reloads_1, NULL);
 
-	if (reg_mentioned_p (rl->reg_rtx, pat))
+	if (reg_mentioned_p (rl_reg_rtx, pat))
 	  {
 	    rtx set = single_set (insn);
 	    if (reload_spill_index[j] < 0
 		&& set
-		&& SET_SRC (set) == rl->reg_rtx)
+		&& SET_SRC (set) == rl_reg_rtx)
 	      {
 		int src = REGNO (SET_SRC (set));
 
@@ -7181,7 +7149,7 @@ emit_output_reload_insns (struct insn_ch
 		if (find_regno_note (insn, REG_DEAD, src))
 		  SET_HARD_REG_BIT (reg_reloaded_died, src);
 	      }
-	    if (REGNO (rl->reg_rtx) < FIRST_PSEUDO_REGISTER)
+	    if (HARD_REGISTER_P (rl_reg_rtx))
 	      {
 		int s = rl->secondary_out_reload;
 		set = single_set (p);
@@ -7194,7 +7162,7 @@ emit_output_reload_insns (struct insn_ch
 		     made; leave new_spill_reg_store alone.  */
 		  ;
 		else if (s >= 0
-			 && SET_SRC (set) == rl->reg_rtx
+			 && SET_SRC (set) == rl_reg_rtx
 			 && SET_DEST (set) == rld[s].reg_rtx)
 		  {
 		    /* Usually the next instruction will be the
@@ -7215,7 +7183,7 @@ emit_output_reload_insns (struct insn_ch
 		      }
 		  }
 		else
-		  new_spill_reg_store[REGNO (rl->reg_rtx)] = p;
+		  new_spill_reg_store[REGNO (rl_reg_rtx)] = p;
 	      }
 	  }
       }
@@ -7242,13 +7210,62 @@ do_input_reload (struct insn_chain *chai
   rtx insn = chain->insn;
   rtx old = (rl->in && MEM_P (rl->in)
 	     ? rl->in_reg : rl->in);
+  rtx reg_rtx = rl->reg_rtx;
+
+  if (old && reg_rtx)
+    {
+      enum machine_mode mode;
+
+      /* Determine the mode to reload in.
+	 This is very tricky because we have three to choose from.
+	 There is the mode the insn operand wants (rl->inmode).
+	 There is the mode of the reload register RELOADREG.
+	 There is the intrinsic mode of the operand, which we could find
+	 by stripping some SUBREGs.
+	 It turns out that RELOADREG's mode is irrelevant:
+	 we can change that arbitrarily.
+
+	 Consider (SUBREG:SI foo:QI) as an operand that must be SImode;
+	 then the reload reg may not support QImode moves, so use SImode.
+	 If foo is in memory due to spilling a pseudo reg, this is safe,
+	 because the QImode value is in the least significant part of a
+	 slot big enough for a SImode.  If foo is some other sort of
+	 memory reference, then it is impossible to reload this case,
+	 so previous passes had better make sure this never happens.
+
+	 Then consider a one-word union which has SImode and one of its
+	 members is a float, being fetched as (SUBREG:SF union:SI).
+	 We must fetch that as SFmode because we could be loading into
+	 a float-only register.  In this case OLD's mode is correct.
+
+	 Consider an immediate integer: it has VOIDmode.  Here we need
+	 to get a mode from something else.
+
+	 In some cases, there is a fourth mode, the operand's
+	 containing mode.  If the insn specifies a containing mode for
+	 this operand, it overrides all others.
+
+	 I am not sure whether the algorithm here is always right,
+	 but it does the right things in those cases.  */
+
+      mode = GET_MODE (old);
+      if (mode == VOIDmode)
+	mode = rl->inmode;
+
+      /* We cannot use gen_lowpart_common since it can do the wrong thing
+	 when REG_RTX has a multi-word mode.  Note that REG_RTX must
+	 always be a REG here.  */
+      if (GET_MODE (reg_rtx) != mode)
+	reg_rtx = reload_adjust_reg_for_mode (reg_rtx, mode);
+    }
+  reload_reg_rtx_for_input[j] = reg_rtx;
 
   if (old != 0
       /* AUTO_INC reloads need to be handled even if inherited.  We got an
 	 AUTO_INC reload if reload_out is set but reload_out_reg isn't.  */
       && (! reload_inherited[j] || (rl->out && ! rl->out_reg))
-      && ! rtx_equal_p (rl->reg_rtx, old)
-      && rl->reg_rtx != 0)
+      && ! rtx_equal_p (reg_rtx, old)
+      && reg_rtx != 0)
     emit_input_reload_insns (chain, rld + j, old, j);
 
   /* When inheriting a wider reload, we have a MEM in rl->in,
@@ -7267,24 +7284,21 @@ do_input_reload (struct insn_chain *chai
 
   if (optimize
       && (reload_inherited[j] || reload_override_in[j])
-      && rl->reg_rtx
-      && REG_P (rl->reg_rtx)
-      && spill_reg_store[REGNO (rl->reg_rtx)] != 0
+      && reg_rtx
+      && REG_P (reg_rtx)
+      && spill_reg_store[REGNO (reg_rtx)] != 0
 #if 0
       /* There doesn't seem to be any reason to restrict this to pseudos
 	 and doing so loses in the case where we are copying from a
 	 register of the wrong class.  */
-      && (REGNO (spill_reg_stored_to[REGNO (rl->reg_rtx)])
-	  >= FIRST_PSEUDO_REGISTER)
+      && !HARD_REGISTER_P (spill_reg_stored_to[REGNO (reg_rtx)])
 #endif
       /* The insn might have already some references to stackslots
 	 replaced by MEMs, while reload_out_reg still names the
 	 original pseudo.  */
-      && (dead_or_set_p (insn,
-			 spill_reg_stored_to[REGNO (rl->reg_rtx)])
-	  || rtx_equal_p (spill_reg_stored_to[REGNO (rl->reg_rtx)],
-			  rl->out_reg)))
-    delete_output_reload (insn, j, REGNO (rl->reg_rtx));
+      && (dead_or_set_p (insn, spill_reg_stored_to[REGNO (reg_rtx)])
+	  || rtx_equal_p (spill_reg_stored_to[REGNO (reg_rtx)], rl->out_reg)))
+    delete_output_reload (insn, j, REGNO (reg_rtx), reg_rtx);
 }
 
 /* Do output reloading for reload RL, which is for the insn described by
@@ -7300,6 +7314,30 @@ do_output_reload (struct insn_chain *cha
      not loaded in this same reload, see if we can eliminate a previous
      store.  */
   rtx pseudo = rl->out_reg;
+  rtx reg_rtx = rl->reg_rtx;
+
+  if (rl->out && reg_rtx)
+    {
+      enum machine_mode mode;
+
+      /* Determine the mode to reload in.
+	 See comments above (for input reloading).  */
+      mode = GET_MODE (rl->out);
+      if (mode == VOIDmode)
+	{
+	  /* VOIDmode should never happen for an output.  */
+	  if (asm_noperands (PATTERN (insn)) < 0)
+	    /* It's the compiler's fault.  */
+	    fatal_insn ("VOIDmode on an output", insn);
+	  error_for_asm (insn, "output operand is constant in %<asm%>");
+	  /* Prevent crash--use something we know is valid.  */
+	  mode = word_mode;
+	  rl->out = gen_rtx_REG (mode, REGNO (reg_rtx));
+	}
+      if (GET_MODE (reg_rtx) != mode)
+	reg_rtx = reload_adjust_reg_for_mode (reg_rtx, mode);
+    }
+  reload_reg_rtx_for_output[j] = reg_rtx;
 
   if (pseudo
       && optimize
@@ -7318,13 +7356,13 @@ do_output_reload (struct insn_chain *cha
 	  && reg_reloaded_contents[last_regno] == pseudo_no
 	  && spill_reg_store[last_regno]
 	  && rtx_equal_p (pseudo, spill_reg_stored_to[last_regno]))
-	delete_output_reload (insn, j, last_regno);
+	delete_output_reload (insn, j, last_regno, reg_rtx);
     }
 
   old = rl->out_reg;
   if (old == 0
-      || rl->reg_rtx == old
-      || rl->reg_rtx == 0)
+      || reg_rtx == old
+      || reg_rtx == 0)
     return;
 
   /* An output operand that dies right away does need a reload,
@@ -7333,7 +7371,7 @@ do_output_reload (struct insn_chain *cha
   if ((REG_P (old) || GET_CODE (old) == SCRATCH)
       && (note = find_reg_note (insn, REG_UNUSED, old)) != 0)
     {
-      XEXP (note, 0) = rl->reg_rtx;
+      XEXP (note, 0) = reg_rtx;
       return;
     }
   /* Likewise for a SUBREG of an operand that dies.  */
@@ -7342,8 +7380,7 @@ do_output_reload (struct insn_chain *cha
 	   && 0 != (note = find_reg_note (insn, REG_UNUSED,
 					  SUBREG_REG (old))))
     {
-      XEXP (note, 0) = gen_lowpart_common (GET_MODE (old),
-					   rl->reg_rtx);
+      XEXP (note, 0) = gen_lowpart_common (GET_MODE (old), reg_rtx);
       return;
     }
   else if (GET_CODE (old) == SCRATCH)
@@ -7357,22 +7394,20 @@ do_output_reload (struct insn_chain *cha
   emit_output_reload_insns (chain, rld + j, j);
 }
 
-/* Reload number R reloads from or to a group of hard registers starting at
-   register REGNO.  Return true if it can be treated for inheritance purposes
-   like a group of reloads, each one reloading a single hard register.
-   The caller has already checked that the spill register and REGNO use
-   the same number of registers to store the reload value.  */
+/* A reload copies values of MODE from register SRC to register DEST.
+   Return true if it can be treated for inheritance purposes like a
+   group of reloads, each one reloading a single hard register.  The
+   caller has already checked that (reg:MODE SRC) and (reg:MODE DEST)
+   occupy the same number of hard registers.  */
 
 static bool
-inherit_piecemeal_p (int r ATTRIBUTE_UNUSED, int regno ATTRIBUTE_UNUSED)
+inherit_piecemeal_p (int dest ATTRIBUTE_UNUSED,
+		     int src ATTRIBUTE_UNUSED,
+		     enum machine_mode mode ATTRIBUTE_UNUSED)
 {
 #ifdef CANNOT_CHANGE_MODE_CLASS
-  return (!REG_CANNOT_CHANGE_MODE_P (reload_spill_index[r],
-				     GET_MODE (rld[r].reg_rtx),
-				     reg_raw_mode[reload_spill_index[r]])
-	  && !REG_CANNOT_CHANGE_MODE_P (regno,
-					GET_MODE (rld[r].reg_rtx),
-					reg_raw_mode[regno]));
+  return (!REG_CANNOT_CHANGE_MODE_P (dest, mode, reg_raw_mode[dest])
+	  && !REG_CANNOT_CHANGE_MODE_P (src, mode, reg_raw_mode[src]));
 #else
   return true;
 #endif
@@ -7414,9 +7449,13 @@ emit_reload_insns (struct insn_chain *ch
 
   for (j = 0; j < n_reloads; j++)
     {
-      if (rld[j].reg_rtx
-	  && REGNO (rld[j].reg_rtx) < FIRST_PSEUDO_REGISTER)
-	new_spill_reg_store[REGNO (rld[j].reg_rtx)] = 0;
+      if (rld[j].reg_rtx && HARD_REGISTER_P (rld[j].reg_rtx))
+	{
+	  unsigned int i;
+
+	  for (i = REGNO (rld[j].reg_rtx); i < END_REGNO (rld[j].reg_rtx); i++)
+	    new_spill_reg_store[i] = 0;
+	}
 
       do_input_reload (chain, rld + j, j);
       do_output_reload (chain, rld + j, j);
@@ -7515,40 +7554,31 @@ emit_reload_insns (struct insn_chain *ch
 	{
 	  int nr = hard_regno_nregs[i][GET_MODE (rld[r].reg_rtx)];
 	  int k;
-	  int part_reaches_end = 0;
-	  int all_reaches_end = 1;
 
 	  /* For a multi register reload, we need to check if all or part
 	     of the value lives to the end.  */
 	  for (k = 0; k < nr; k++)
-	    {
-	      if (reload_reg_reaches_end_p (i + k, rld[r].opnum,
-					    rld[r].when_needed))
-		part_reaches_end = 1;
-	      else
-		all_reaches_end = 0;
-	    }
-
-	  /* Ignore reloads that don't reach the end of the insn in
-	     entirety.  */
-	  if (all_reaches_end)
-	    {
-	      /* First, clear out memory of what used to be in this spill reg.
-		 If consecutive registers are used, clear them all.  */
-
-	      for (k = 0; k < nr; k++)
-  	        {
-		CLEAR_HARD_REG_BIT (reg_reloaded_valid, i + k);
-  		  CLEAR_HARD_REG_BIT (reg_reloaded_call_part_clobbered, i + k);
-  		}
-
-	      /* Maybe the spill reg contains a copy of reload_out.  */
-	      if (rld[r].out != 0
-		  && (REG_P (rld[r].out)
+	    if (reload_reg_reaches_end_p (i + k, rld[r].opnum,
+					  rld[r].when_needed))
+	      CLEAR_HARD_REG_BIT (reg_reloaded_valid, i + k);
+
+	  /* Maybe the spill reg contains a copy of reload_out.  */
+	  if (rld[r].out != 0
+	      && (REG_P (rld[r].out)
 #ifdef AUTO_INC_DEC
-		      || ! rld[r].out_reg
+		  || ! rld[r].out_reg
 #endif
-		      || REG_P (rld[r].out_reg)))
+		  || REG_P (rld[r].out_reg)))
+	    {
+	      rtx src;
+	      enum machine_mode mode;
+
+	      src = reload_reg_rtx_for_output[r];
+	      mode = GET_MODE (src);
+	      i = REGNO (src);
+	      nr = hard_regno_nregs[i][mode];
+	      if (reload_regs_reach_end_p (i, nr, rld[r].opnum,
+					   rld[r].when_needed))
 		{
 		  rtx out = (REG_P (rld[r].out)
 			     ? rld[r].out
@@ -7557,17 +7587,16 @@ emit_reload_insns (struct insn_chain *ch
 /* AUTO_INC */		     : XEXP (rld[r].in_reg, 0));
 		  int nregno = REGNO (out);
 		  int nnr = (nregno >= FIRST_PSEUDO_REGISTER ? 1
-			     : hard_regno_nregs[nregno]
-					       [GET_MODE (rld[r].reg_rtx)]);
+			     : hard_regno_nregs[nregno][mode]);
 		  bool piecemeal;
 
 		  spill_reg_store[i] = new_spill_reg_store[i];
 		  spill_reg_stored_to[i] = out;
-		  reg_last_reload_reg[nregno] = rld[r].reg_rtx;
+		  reg_last_reload_reg[nregno] = src;
 
 		  piecemeal = (nregno < FIRST_PSEUDO_REGISTER
 			       && nr == nnr
-			       && inherit_piecemeal_p (r, nregno));
+			       && inherit_piecemeal_p (nregno, i, mode));
 
 		  /* If NREGNO is a hard register, it may occupy more than
 		     one register.  If it does, say what is in the
@@ -7578,9 +7607,7 @@ emit_reload_insns (struct insn_chain *ch
 		  if (nregno < FIRST_PSEUDO_REGISTER)
 		    for (k = 1; k < nnr; k++)
 		      reg_last_reload_reg[nregno + k]
-			= (piecemeal
-			   ? regno_reg_rtx[REGNO (rld[r].reg_rtx) + k]
-			   : 0);
+			= (piecemeal ? regno_reg_rtx[i + k] : 0);
 
 		  /* Now do the inverse operation.  */
 		  for (k = 0; k < nr; k++)
@@ -7592,24 +7619,38 @@ emit_reload_insns (struct insn_chain *ch
 			   : nregno + k);
 		      reg_reloaded_insn[i + k] = insn;
 		      SET_HARD_REG_BIT (reg_reloaded_valid, i + k);
-		      if (HARD_REGNO_CALL_PART_CLOBBERED (i + k, GET_MODE (out)))
-			SET_HARD_REG_BIT (reg_reloaded_call_part_clobbered, i + k);
+		      if (HARD_REGNO_CALL_PART_CLOBBERED (i + k, mode))
+			SET_HARD_REG_BIT (reg_reloaded_call_part_clobbered,
+					  i + k);
+		      else
+			CLEAR_HARD_REG_BIT (reg_reloaded_call_part_clobbered,
+					    i + k);
 		    }
 		}
-
-	      /* Maybe the spill reg contains a copy of reload_in.  Only do
-		 something if there will not be an output reload for
-		 the register being reloaded.  */
-	      else if (rld[r].out_reg == 0
-		       && rld[r].in != 0
-		       && ((REG_P (rld[r].in)
-			    && REGNO (rld[r].in) >= FIRST_PSEUDO_REGISTER
-	                    && !REGNO_REG_SET_P (&reg_has_output_reload,
-			      			 REGNO (rld[r].in)))
-			   || (REG_P (rld[r].in_reg)
-			       && !REGNO_REG_SET_P (&reg_has_output_reload,
-						    REGNO (rld[r].in_reg))))
-		       && ! reg_set_p (rld[r].reg_rtx, PATTERN (insn)))
+	    }
+	  /* Maybe the spill reg contains a copy of reload_in.  Only do
+	     something if there will not be an output reload for
+	     the register being reloaded.  */
+	  else if (rld[r].out_reg == 0
+		   && rld[r].in != 0
+		   && ((REG_P (rld[r].in)
+			&& !HARD_REGISTER_P (rld[r].in)
+			&& !REGNO_REG_SET_P (&reg_has_output_reload,
+					     REGNO (rld[r].in)))
+		       || (REG_P (rld[r].in_reg)
+			   && !REGNO_REG_SET_P (&reg_has_output_reload,
+						REGNO (rld[r].in_reg))))
+		   && !reg_set_p (reload_reg_rtx_for_input[r], PATTERN (insn)))
+	    {
+	      rtx dest;
+	      enum machine_mode mode;
+
+	      dest = reload_reg_rtx_for_input[r];
+	      i = REGNO (dest);
+	      mode = GET_MODE (dest);
+	      nr = hard_regno_nregs[i][mode];
+	      if (reload_regs_reach_end_p (i, nr, rld[r].opnum,
+					   rld[r].when_needed))
 		{
 		  int nregno;
 		  int nnr;
@@ -7626,21 +7667,18 @@ emit_reload_insns (struct insn_chain *ch
 		  nregno = REGNO (in);
 
 		  nnr = (nregno >= FIRST_PSEUDO_REGISTER ? 1
-			 : hard_regno_nregs[nregno]
-					   [GET_MODE (rld[r].reg_rtx)]);
+			 : hard_regno_nregs[nregno][mode]);
 
-		  reg_last_reload_reg[nregno] = rld[r].reg_rtx;
+		  reg_last_reload_reg[nregno] = dest;
 
 		  piecemeal = (nregno < FIRST_PSEUDO_REGISTER
 			       && nr == nnr
-			       && inherit_piecemeal_p (r, nregno));
+			       && inherit_piecemeal_p (i, nregno, mode));
 
 		  if (nregno < FIRST_PSEUDO_REGISTER)
 		    for (k = 1; k < nnr; k++)
 		      reg_last_reload_reg[nregno + k]
-			= (piecemeal
-			   ? regno_reg_rtx[REGNO (rld[r].reg_rtx) + k]
-			   : 0);
+			= (piecemeal ? regno_reg_rtx[i + k] : 0);
 
 		  /* Unless we inherited this reload, show we haven't
 		     recently done a store.
@@ -7659,22 +7697,15 @@ emit_reload_insns (struct insn_chain *ch
 			   : nregno + k);
 		      reg_reloaded_insn[i + k] = insn;
 		      SET_HARD_REG_BIT (reg_reloaded_valid, i + k);
-		      if (HARD_REGNO_CALL_PART_CLOBBERED (i + k, GET_MODE (in)))
-			SET_HARD_REG_BIT (reg_reloaded_call_part_clobbered, i + k);
+		      if (HARD_REGNO_CALL_PART_CLOBBERED (i + k, mode))
+			SET_HARD_REG_BIT (reg_reloaded_call_part_clobbered,
+					  i + k);
+		      else
+			CLEAR_HARD_REG_BIT (reg_reloaded_call_part_clobbered,
+					    i + k);
 		    }
 		}
 	    }
-
-	  /* However, if part of the reload reaches the end, then we must
-	     invalidate the old info for the part that survives to the end.  */
-	  else if (part_reaches_end)
-	    {
-	      for (k = 0; k < nr; k++)
-		if (reload_reg_reaches_end_p (i + k,
-					      rld[r].opnum,
-					      rld[r].when_needed))
-		  CLEAR_HARD_REG_BIT (reg_reloaded_valid, i + k);
-	    }
 	}
 
       /* The following if-statement was #if 0'd in 1.34 (or before...).
@@ -7687,17 +7718,18 @@ emit_reload_insns (struct insn_chain *ch
 	 it thinks only about the original insn.  So invalidate it here.
 	 Also do the same thing for RELOAD_OTHER constraints where the
 	 output is discarded.  */
-      if (i < 0 
-	  && ((rld[r].out != 0
-	       && (REG_P (rld[r].out)
-		   || (MEM_P (rld[r].out)
+      else if (i < 0
+	       && ((rld[r].out != 0
+		    && (REG_P (rld[r].out)
+			|| (MEM_P (rld[r].out)
+			    && REG_P (rld[r].out_reg))))
+		   || (rld[r].out == 0 && rld[r].out_reg
 		       && REG_P (rld[r].out_reg))))
-	      || (rld[r].out == 0 && rld[r].out_reg
-		  && REG_P (rld[r].out_reg))))
 	{
 	  rtx out = ((rld[r].out && REG_P (rld[r].out))
 		     ? rld[r].out : rld[r].out_reg);
 	  int nregno = REGNO (out);
+	  enum machine_mode mode = GET_MODE (out);
 
 	  /* REG_RTX is now set or clobbered by the main instruction.
 	     As the comment above explains, forget_old_reloads_1 only
@@ -7724,7 +7756,7 @@ emit_reload_insns (struct insn_chain *ch
 	      /* If we can find a hard register that is stored, record
 		 the storing insn so that we may delete this insn with
 		 delete_output_reload.  */
-	      src_reg = rld[r].reg_rtx;
+	      src_reg = reload_reg_rtx_for_output[r];
 
 	      /* If this is an optional reload, try to find the source reg
 		 from an input reload.  */
@@ -7741,7 +7773,7 @@ emit_reload_insns (struct insn_chain *ch
 			{
 			  if (rld[k].in == src_reg)
 			    {
-			      src_reg = rld[k].reg_rtx;
+			      src_reg = reload_reg_rtx_for_input[k];
 			      break;
 			    }
 			}
@@ -7752,13 +7784,17 @@ emit_reload_insns (struct insn_chain *ch
 	      if (src_reg && REG_P (src_reg)
 		  && REGNO (src_reg) < FIRST_PSEUDO_REGISTER)
 		{
-		  int src_regno = REGNO (src_reg);
-		  int nr = hard_regno_nregs[src_regno][rld[r].mode];
+		  int src_regno, nr;
+		  rtx note;
+
+		  gcc_assert (GET_MODE (src_reg) == mode);
+		  src_regno = REGNO (src_reg);
+		  nr = hard_regno_nregs[src_regno][mode];
 		  /* The place where to find a death note varies with
 		     PRESERVE_DEATH_INFO_REGNO_P .  The condition is not
 		     necessarily checked exactly in the code that moves
 		     notes, so just check both locations.  */
-		  rtx note = find_regno_note (insn, REG_DEAD, src_regno);
+		  note = find_regno_note (insn, REG_DEAD, src_regno);
 		  if (! note && store_insn)
 		    note = find_regno_note (store_insn, REG_DEAD, src_regno);
 		  while (nr-- > 0)
@@ -7769,10 +7805,13 @@ emit_reload_insns (struct insn_chain *ch
 		      reg_reloaded_insn[src_regno + nr] = store_insn;
 		      CLEAR_HARD_REG_BIT (reg_reloaded_dead, src_regno + nr);
 		      SET_HARD_REG_BIT (reg_reloaded_valid, src_regno + nr);
-		      if (HARD_REGNO_CALL_PART_CLOBBERED (src_regno + nr, 
-							  GET_MODE (src_reg)))
+		      if (HARD_REGNO_CALL_PART_CLOBBERED (src_regno + nr,
+							  mode))
 			SET_HARD_REG_BIT (reg_reloaded_call_part_clobbered, 
 					  src_regno + nr);
+		      else
+			CLEAR_HARD_REG_BIT (reg_reloaded_call_part_clobbered,
+					    src_regno + nr);
 		      SET_HARD_REG_BIT (reg_is_output_reload, src_regno + nr);
 		      if (note)
 			SET_HARD_REG_BIT (reg_reloaded_died, src_regno);
@@ -7789,7 +7828,7 @@ emit_reload_insns (struct insn_chain *ch
 	    }
 	  else
 	    {
-	      int num_regs = hard_regno_nregs[nregno][GET_MODE (out)];
+	      int num_regs = hard_regno_nregs[nregno][mode];
 
 	      while (num_regs-- > 0)
 		reg_last_reload_reg[nregno + num_regs] = 0;
@@ -8075,10 +8114,11 @@ gen_reload (rtx out, rtx in, int opnum, 
    LAST_RELOAD_REG is the hard register number for which we want to delete
    the last output reload.
    J is the reload-number that originally used REG.  The caller has made
-   certain that reload J doesn't use REG any longer for input.  */
+   certain that reload J doesn't use REG any longer for input.
+   NEW_RELOAD_REG is reload register that reload J is using for REG.  */
 
 static void
-delete_output_reload (rtx insn, int j, int last_reload_reg)
+delete_output_reload (rtx insn, int j, int last_reload_reg, rtx new_reload_reg)
 {
   rtx output_reload_insn = spill_reg_store[last_reload_reg];
   rtx reg = spill_reg_stored_to[last_reload_reg];
@@ -8230,7 +8270,7 @@ delete_output_reload (rtx insn, int j, i
 	}
 
       /* For the debugging info, say the pseudo lives in this reload reg.  */
-      reg_renumber[REGNO (reg)] = REGNO (rld[j].reg_rtx);
+      reg_renumber[REGNO (reg)] = REGNO (new_reload_reg);
       alter_reg (REGNO (reg), -1);
     }
   else
Index: gcc/testsuite/gcc.target/mips/pr35232.c
===================================================================
--- /dev/null	2008-02-11 07:41:57.552097000 +0000
+++ gcc/testsuite/gcc.target/mips/pr35232.c	2008-02-17 22:31:26.000000000 +0000
@@ -0,0 +1,17 @@
+/* { dg-do run } */
+/* { dg-mips-options "-O" } */
+
+NOMIPS16 unsigned int
+f1 (unsigned long long x)
+{
+  unsigned int r;
+  asm ("# %0" : "=a" (r) : "0" (x));
+  asm ("# %0" : "=h" (r) : "0" (r));
+  return r;
+}
+
+int
+main (void)
+{
+  return f1 (4) != 4;
+}


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]