Subject: DWARF2 exception doesn't work with gcc and gas on MIPS.

From: "H . J . Lu" <[email-masked]>
Date: Wed, 13 Jun 2001 21:29:40 -0700

In the MIPS gas, there is

    case M_JAL_A:
      if (mips_pic == NO_PIC)
	macro_build ((char *) NULL, &icnt, &offset_expr, "jal", "a");
      else if (mips_pic == SVR4_PIC)
	{
	  /* If this is a reference to an external symbol, and we are
	     using a small GOT, we want
	       lw	$25,<sym>($gp)		(BFD_RELOC_MIPS_CALL16)
	       nop
	       jalr	$25
	       nop
	       lw	$gp,cprestore($sp)
	     The cprestore value is set using the .cprestore
	     pseudo-op.  If we are using a big GOT, we want
	       lui	$25,<sym>		(BFD_RELOC_MIPS_CALL_HI16)
	       addu	$25,$25,$gp
	       lw	$25,<sym>($25)		(BFD_RELOC_MIPS_CALL_LO16)
	       nop
	       jalr	$25
	       nop
	       lw	$gp,cprestore($sp)
	     If the symbol is not external, we want
	       lw	$25,<sym>($gp)		(BFD_RELOC_MIPS_GOT16)
	       nop
	       addiu	$25,$25,<sym>		(BFD_RELOC_LO16)
	       jalr	$25
	       nop
	       lw $gp,cprestore($sp) */

When gcc emits

$LEHB5:
        la      $25,foobar
        jal     $31,$25 
$LEHE5:

It doesn't work since

        jal     $31,$25 

is expanded to

	jalr	$25
	nop
	lw	$gp,cprestore($sp)

Now we have

$LEHB5:
        la      $25,foobar
	jalr	$25
	nop
	lw	$gp,cprestore($sp)
$LEHE5:

When foobar throws an exception, GP won't get restored. What we want is

$LEHB5:
        la      $25,foobar
	jalr	$25
	nop
$LEHE5:
	lw	$gp,cprestore($sp)

Does anyone have any suggestions how to fix it?

Thanks.

H.J.

---

From: Ian Lance Taylor <[email-masked]>
Date: 13 Jun 2001 22:50:54 -0700

> In the MIPS gas, there is

Not the relevant bit of code, not that it matters much.  The
instruction
      jal     $31,$25
will be handled by the M_JAL_1 case in gas/config/tc-mips.c.

> Does anyone have any suggestions how to fix it?

Traditional MIPS assemblers try to make life easier by doing this sort
of translation.  Modern MIPS compilers sidestep the translation
because they can do better.  In this case gcc evidently needs to do
better in order to makes it exception handling model work.  gcc should
generate a jalr instruction, and should restore the GP register
itself.

(I suppose that it would be theoretically possible for gas to
recognize labels of the special form $LEHEn.  But that seems quite
dreadful and quite fragile.)

Ian

---

From: Ian Lance Taylor <[email-masked]>
Date: 13 Jun 2001 22:53:42 -0700

> will be handled by the M_JAL_1 case in gas/config/tc-mips.c.

Sorry, M_JAL_2.

Ian

---

From: "H . J . Lu" <[email-masked]>
Date: Wed, 13 Jun 2001 23:23:06 -0700

> dreadful and quite fragile.)

The more I look at the problem, the more I doubt DAWRF2 exception will
ever work with the SVR4 MIPS ABI without the full support from gcc. The
problem is GP is a caller saved register in the SVR4 MIPS ABI. So every
caller has to do

	call foo
	restore gp

Given a piece of C++ code:

  try
    {
      foo (...);
      .....
    }
  catch (...)
    {
    }

When foo () throws an exception, it is gcc who has to make sure that
GP gets properly restored. Is there a way to teach the gcc exception
code that GP is a caller saved register?

BTW, in IRIX 6, GP is changed to callee saved so that it is not a
problem. 

H.J.

---

From: Richard Henderson <[email-masked]>
Date: Thu, 14 Jun 2001 10:19:51 -0700

> When foobar throws an exception, GP won't get restored. What we want is

Um, what exactly do you think the difference between these two is?

Hint: nothing.

I could have sworn there was an exception_receiver pattern on mips
to handle this, but I don't see it now.  It's relatively easy to fix;
see TARGET_LD_BUGGY_LDGP is handled on alpha.

r~

---

From: "H . J . Lu" <[email-masked]>
Date: Thu, 14 Jun 2001 11:41:17 -0700

> see TARGET_LD_BUGGY_LDGP is handled on alpha.

Thanks. This patch seems to fix my problem. But I have several
questions:

1. Is the usage of current_frame_info.args_size right?
2. What should be in the place of [(const_int 4)]?
3. Does it need other patterns for similar situations?
4. Is my patch ok?

H.J.
----
2001-06-14  H.J. Lu <[email-masked]>

	* config/mips/mips.md (exception_receiver): New.

--- gcc/config/mips/mips.md.except	Thu Jun 14 10:34:34 2001
+++ gcc/config/mips/mips.md	Thu Jun 14 11:36:31 2001
@@ -10461,3 +10461,21 @@ ld\\t%2,%1-%S1(%2)\;daddu\\t%2,%2,$31\;j
   [(set_attr "type"	"arith")
    (set_attr "mode"	"DI")
    (set_attr "length"	"40")])
+
+;; For o32, the gp register is call clobbered, so it must
+;; be saved & restored around calls by the caller.  If the call
+;; doesn't return normally (nonlocal goto, or an exception is
+;; thrown), then the code at the exception handler label must
+;; restore the gp register.
+(define_insn "exception_receiver"
+  [(const_int 4)]
+  "mips_abi == ABI_32"
+  "*
+{
+  static char ldgp[40];
+  sprintf (ldgp, \"lw\\t$gp,%ld($sp)\", current_frame_info.args_size);
+  return ldgp;
+}"
+  [(set_attr "type"	"load")
+   (set_attr "mode"	"SI")
+   (set_attr "length"	"4")])

---

From: Richard Henderson <[email-masked]>
Date: Thu, 14 Jun 2001 12:05:43 -0700

> 1. Is the usage of current_frame_info.args_size right?

Yes.

> 2. What should be in the place of [(const_int 4)]?

Some unspec_volatile.

> 3. Does it need other patterns for similar situations?

Yes, O64.

> 4. Is my patch ok?

No.

This one should be ok.

r~

Index: mips.md
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/mips/mips.md,v
retrieving revision 1.94
diff -c -p -d -r1.94 mips.md
*** mips.md	2001/05/20 00:35:24	1.94
--- mips.md	2001/06/14 18:50:32
***************
*** 30,42 ****
  ;; Number	USE
  ;; 0		movsi_ul
  ;; 1		movsi_us, get_fnaddr
- ;; 2		loadgp
  ;; 3		eh_set_return
  ;; 20		builtin_setjmp_setup
  ;;
  ;; UNSPEC_VOLATILE values
  ;; 0		blockage
  ;; 3		builtin_longjmp
  ;; 10		consttable_qi
  ;; 11		consttable_hi
  ;; 12		consttable_si
--- 30,43 ----
  ;; Number	USE
  ;; 0		movsi_ul
  ;; 1		movsi_us, get_fnaddr
  ;; 3		eh_set_return
  ;; 20		builtin_setjmp_setup
  ;;
  ;; UNSPEC_VOLATILE values
  ;; 0		blockage
+ ;; 2		loadgp
  ;; 3		builtin_longjmp
+ ;; 4		exception_receiver
  ;; 10		consttable_qi
  ;; 11		consttable_hi
  ;; 12		consttable_si
*************** ld\\t%2,%1-%S1(%2)\;daddu\\t%2,%2,$31\;j
*** 9571,9576 ****
--- 9572,9598 ----
  		  operands[0]);
    DONE;
  }")
+ 
+ (define_insn "exception_receiver"
+   [(unspec_volatile [(const_int 0)] 4)]
+   "TARGET_ABICALLS && (mips_abi == ABI_32 || mips_abi == ABI_O64)"
+   "*
+ {
+   rtx loc;
+ 
+   operands[0] = gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM);
+ 
+   if (frame_pointer_needed)
+     loc = hard_frame_pointer_rtx;
+   else
+     loc = stack_pointer_rtx;
+   loc = plus_constant (loc, current_frame_info.args_size);
+   operands[1] = gen_rtx_MEM (Pmode, loc);
+ 
+   return mips_move_1word (operands, insn, 0);
+ }"
+   [(set_attr "type"   "load")
+    (set_attr "length" "8")])
  
  ;;
  ;;  ....................

---

From: "H . J . Lu" <[email-masked]>
Date: Thu, 14 Jun 2001 12:25:50 -0700

> +    (set_attr "length" "8")])

I have 3 questions:

1. I see PIC_FUNCTION_ADDR_REGNUM be $25. gp is $28.  How does your
patch restore $28?

2. I assum you set length to 8 for o64. Has anyone checked if the
instruction is 8 byte on o64?

2. Did you remove (set_attr "mode" "SI") for o64?

Thanks.

H.J.

---

From: Richard Henderson <[email-masked]>
Date: Thu, 14 Jun 2001 12:42:55 -0700

> 1. I see PIC_FUNCTION_ADDR_REGNUM be $25. gp is $28.  How does your

That should be pic_offset_table_rtx instead.

> instruction is 8 byte on o64?

It may be 8 on o32 as well -- consider the nop that the
assembler may add.

> 2. Did you remove (set_attr "mode" "SI") for o64?

As far as I can tell that is not used except for mult/div
scheduling.

r~

---

From: "H . J . Lu" <[email-masked]>
Date: Thu, 14 Jun 2001 12:55:49 -0700

> That should be pic_offset_table_rtx instead.

I used

operands[0] = pic_offset_table_rtx;

It seems to work for me.

Thanks a lot.

H.J.