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]

Re: Automatic MMX/x87 FPU mode switching


Quoting Richard Henderson <rth@redhat.com>:

> On Tue, Jun 28, 2005 at 10:38:06AM +0200, Uros Bizjak wrote:
> > It looks that we have to invent some other way to tell reg allocator,
> > which register set is active.
> 
> Ug.  Yes, it certainly looks like it.

> What we want, I think, is to adjust struct insn_chain.live_throughout
> to make live the registers that we don't want to use.  There's
> existing precidence for hacks in this area: maybe_fix_stack_asms.
> Which, I'll note, is already protected by STACK_REGS, which seems
> convenient...

  I think  I have found another solution to register life info (it looks a bit
hacky to me, but it works for me). We can add a

(expr_list:REG_DEP_TRUE (use (reg:ALLREGS 8 st))

expressions to CALL patterns and this will force registers to be live at
function call.

  (A bit annoying fact is, that this change alone causes "internal consistency
failure" in verify_wide_reg, but this was solved by emitting an use pattern
just before CALL pattern).

  So, I have added following changes to my previous patch:

-- efpu/emms now use registers of opposite unit (as requested):

(define_insn "efpu"
  [(set (reg:ALLREGS FIRSTFP_REG)
       (unspec_volatile:ALLREGS [(reg:ALLREGS FIRSTMMX_REG)]
				UNSPECV_EFPU))]
  ""
  ""
  [(set_attr "length" "0")])

(define_insn "emms"
  [(set (reg:ALLREGS FIRSTMMX_REG)
       (unspec_volatile:ALLREGS [(reg:ALLREGS FIRSTFP_REG)]
				UNSPECV_EMMS))]
  "TARGET_MMX"
{
  return TARGET_3DNOW ? "femms" : "emms";
})

-- ix86_epilogue_uses function was added (via EPILOUGE_USES #define, as requested):

/* Return true to prevent register allocator from allocating registers
   from the unit that is not active.  If funciton returns in MMX
   register, mark all x87 registers as used.  If funciton returns in
   x87 register, mark all MMX registers as used.  */

bool
ix86_epilogue_uses (int regno)
{
  rtx freg = current_function_return_rtx;

  if (freg)
    {
      if (MMX_REG_P (freg))
	return TARGET_80387 && FP_REGNO_P (regno);

      if (FP_REG_P (freg))
	return TARGET_MMX && MMX_REGNO_P (regno);
    }

  if (FPU_MODE_DEFAULT == FPU_MODE_MMX)
    return TARGET_80387 && FP_REGNO_P (regno);
  else
    return TARGET_MMX && MMX_REGNO_P (regno);
}

-- ix86_mode_needed was changed to emit register usage patterns as described
above. The relevant part of ix86_mode_needed is shown below:

ix86_mode_needed (int entity, rtx insn)
{
  int unit, mode;

  if (entity == I387_FPU_MODE)
    {
      /* If a function call uses MMX registers, select MMX FPU mode and
	 if function call uses x87 registers, select x87 FPU mode.  If
	 no MMX or x87 registers are used, switch to default mode.  */
      if (CALL_P (insn))
	{
	  rtx link;

	  int mmx = 0, x87 = 0;
	  int regno = 0;

	  for (link = CALL_INSN_FUNCTION_USAGE (insn);
	       link;
	       link = XEXP (link, 1))
	    {
	      if (GET_CODE (XEXP (link, 0)) == USE)
		{
		  rtx reg = XEXP (XEXP (link, 0), 0);

		  if (reg)
		    {
		      if (MMX_REG_P (reg))
			mmx++;

		      if (FP_REG_P (reg))
			x87++;
		    }
		}
	    }

	  /* Mixing of x87 and MMX registers is not allowed
	     in function call.  */
	  gcc_assert (!mmx || !x87);

	  /* Find out which register set is blocked.  */
	  if (mmx && TARGET_80387)
	    regno = FIRST_FLOAT_REG;

	  if (x87 && TARGET_MMX)
	    regno = FIRST_MMX_REG;

	  if (!regno)
	    {
	      if (FPU_MODE_DEFAULT == FPU_MODE_MMX)
		regno = TARGET_80387 ? FIRST_FLOAT_REG : 0;
	      else
		regno = TARGET_MMX ? FIRST_MMX_REG : 0;
	    }

	  /* Emit use pattern and attach register usage
	     to call_insn pattern.  */
	  if (regno)
	    {
	      rtx reg = gen_rtx_REG (ALLREGSmode, regno);
	      rtx use = gen_rtx_USE (VOIDmode, reg);
	      rtx call_fusage = 0;

	      use_reg (&call_fusage, reg);
	      add_function_usage_to (insn, call_fusage);

	      emit_insn_before (use, insn);
	    }

	  if (mmx)
	    return FPU_MODE_MMX;

	  if (x87)
	    return FPU_MODE_X87;      

	  return FPU_MODE_DEFAULT;
	}

--cut here--

  With these changes applied, the testcase from my previous post now produces
correct register life information:

;; Start of basic block 0, registers live: 6 [bp] 7 [sp] 20 [frame] 29 [mm0] 30
[mm1] 31 [mm2] 32 [mm3] 33
 [mm4] 34 [mm5] 35 [mm6] 36 [mm7]
(note:HI 10 7 47 0 [bb 0] NOTE_INSN_BASIC_BLOCK)

...

(insn 41 50 12 0 (set (reg:ALLREGS 8 st)
        (unspec_volatile:ALLREGS [
                (reg:ALLREGS 29 mm0)
            ] 2)) 871 {efpu} (nil)
    (expr_list:REG_DEAD (reg:ALLREGS 29 mm0)
        (nil)))

...

(insn 39 13 14 0 (use (reg:ALLREGS 8 st)) -1 (nil)
    (nil))

(call_insn:HI 14 39 15 0 (set (reg:V8QI 29 mm0)
        (call (mem:QI (symbol_ref:SI ("aaa") [flags 0x41] <function_decl
0x40305360 aaa>) [0 S1 A8])
            (const_int 0 [0x0]))) 526 {*call_value_0} (insn_list:REG_DEP_TRUE 12
(insn_list:REG_DEP_TRUE 1
3 (nil)))
    (expr_list:REG_DEAD (reg:ALLREGS 8 st)
        (expr_list:REG_DEAD (reg:V8QI 30 mm1)
            (nil)))
    (expr_list:REG_DEP_TRUE (use (reg:V8QI 29 mm0))
        (expr_list:REG_DEP_TRUE (use (reg:V8QI 30 mm1))
            (expr_list:REG_DEP_TRUE (use (reg:ALLREGS 8 st))
                (nil)))))

...

(insn 40 21 36 0 (set (reg:ALLREGS 29 mm0)
        (unspec_volatile:ALLREGS [
                (reg:ALLREGS 8 st)
            ] 5)) 872 {emms} (nil)
    (expr_list:REG_DEAD (reg:ALLREGS 8 st)
        (nil)))

...

;; End of basic block 0, registers live:
 0 [ax] 6 [bp] 7 [sp] 20 [frame] 29 [mm0] 30 [mm1] 31 [mm2] 32 [mm3] 33 [mm4] 34
[mm5] 35 [mm6] 36 [mm7]

  If everything works OK, then efpu/emms/call patterns (that can change FPU
mode) should be marked with an expression:

   (expr_list:REG_DEAD (reg:ALLREGS 8 st)

   Which in fact "enables" registers of the opposite unit.

  As far as I have tested, this approach handles register life information
correctly through various combinations of call instructions, branches, etc.

I would be very grateful for your opinion on these changes, before I move to
reg-stack.c hacking (reg-stack.c needs some hacking in function entry and exit
compensation code, because it doesn't expect live FP registers on function
boundaries.)

  Uh, and http://gcc.gnu.org/wiki/reload was avoided this way :)

Uros.


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