[committed] hppa: Add mi_thunk support for vcalls

Message ID fc42ee7c-70af-250b-d62f-e5ade04c2460@bell.net
State New
Headers show
Series
  • [committed] hppa: Add mi_thunk support for vcalls
Related show

Commit Message

John David Anglin Feb. 22, 2021, 4:04 p.m.
The attached change adds mi_thunk support for vcalls on hppa.  Tested on hppa-unknown-linux-gnu,
hppa2.0w-hp-hpux11.11 and hppa64-hp-hpux11.11.  Committed to trunk and gcc-10.

Dave
-- 
John David Anglin  dave.anglin@bell.net

Add mi_thunk support for vcalls on hppa.

gcc/ChangeLog:

	PR target/85074
	* config/pa/pa.c (TARGET_ASM_CAN_OUTPUT_MI_THUNK): Define as
	hook_bool_const_tree_hwi_hwi_const_tree_true.
	(pa_asm_output_mi_thunk): Add support for nonzero vcall_offset.

Patch

diff --git a/gcc/config/pa/pa.c b/gcc/config/pa/pa.c
index 3921b5c98de..d7fcd11e504 100644
--- a/gcc/config/pa/pa.c
+++ b/gcc/config/pa/pa.c
@@ -293,7 +293,7 @@  static size_t n_deferred_plabels = 0;
 #undef TARGET_ASM_OUTPUT_MI_THUNK
 #define TARGET_ASM_OUTPUT_MI_THUNK pa_asm_output_mi_thunk
 #undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
-#define TARGET_ASM_CAN_OUTPUT_MI_THUNK default_can_output_mi_thunk_no_vcall
+#define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_const_tree_hwi_hwi_const_tree_true

 #undef TARGET_ASM_FILE_END
 #define TARGET_ASM_FILE_END pa_file_end
@@ -8461,12 +8461,15 @@  pa_is_function_label_plus_const (rtx op)
 	  && GET_CODE (XEXP (op, 1)) == CONST_INT);
 }

-/* Output assembly code for a thunk to FUNCTION.  */
+/* Output the assembler code for a thunk function.  THUNK_DECL is the
+   declaration for the thunk function itself, FUNCTION is the decl for
+   the target function.  DELTA is an immediate constant offset to be
+   added to THIS.  If VCALL_OFFSET is nonzero, the word at
+   *(*this + vcall_offset) should be added to THIS.  */

 static void
 pa_asm_output_mi_thunk (FILE *file, tree thunk_fndecl, HOST_WIDE_INT delta,
-			HOST_WIDE_INT vcall_offset ATTRIBUTE_UNUSED,
-			tree function)
+			HOST_WIDE_INT vcall_offset, tree function)
 {
   const char *fnname = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (thunk_fndecl));
   static unsigned int current_thunk_number;
@@ -8482,201 +8485,386 @@  pa_asm_output_mi_thunk (FILE *file, tree thunk_fndecl, HOST_WIDE_INT delta,
   assemble_start_function (thunk_fndecl, fnname);
   final_start_function (emit_barrier (), file, 1);

-  /* Output the thunk.  We know that the function is in the same
-     translation unit (i.e., the same space) as the thunk, and that
-     thunks are output after their method.  Thus, we don't need an
-     external branch to reach the function.  With SOM and GAS,
-     functions and thunks are effectively in different sections.
-     Thus, we can always use a IA-relative branch and the linker
-     will add a long branch stub if necessary.
-
-     However, we have to be careful when generating PIC code on the
-     SOM port to ensure that the sequence does not transfer to an
-     import stub for the target function as this could clobber the
-     return value saved at SP-24.  This would also apply to the
-     32-bit linux port if the multi-space model is implemented.  */
-  if ((!TARGET_LONG_CALLS && TARGET_SOM && !TARGET_PORTABLE_RUNTIME
-       && !(flag_pic && TREE_PUBLIC (function))
-       && (TARGET_GAS || last_address < 262132))
-      || (!TARGET_LONG_CALLS && !TARGET_SOM && !TARGET_PORTABLE_RUNTIME
-	  && ((targetm_common.have_named_sections
-	       && DECL_SECTION_NAME (thunk_fndecl) != NULL
-	       /* The GNU 64-bit linker has rather poor stub management.
-		  So, we use a long branch from thunks that aren't in
-		  the same section as the target function.  */
-	       && ((!TARGET_64BIT
-		    && (DECL_SECTION_NAME (thunk_fndecl)
-			!= DECL_SECTION_NAME (function)))
-		   || ((DECL_SECTION_NAME (thunk_fndecl)
-			== DECL_SECTION_NAME (function))
-		       && last_address < 262132)))
-	      /* In this case, we need to be able to reach the start of
-		 the stub table even though the function is likely closer
-		 and can be jumped to directly.  */
-	      || (targetm_common.have_named_sections
-		  && DECL_SECTION_NAME (thunk_fndecl) == NULL
-		  && DECL_SECTION_NAME (function) == NULL
-		  && total_code_bytes < MAX_PCREL17F_OFFSET)
-	      /* Likewise.  */
-	      || (!targetm_common.have_named_sections
-		  && total_code_bytes < MAX_PCREL17F_OFFSET))))
-    {
-      if (!val_14)
-	output_asm_insn ("addil L'%2,%%r26", xoperands);
-
-      output_asm_insn ("b %0", xoperands);
-
-      if (val_14)
-	{
-	  output_asm_insn ("ldo %2(%%r26),%%r26", xoperands);
-	  nbytes += 8;
+  if (!vcall_offset)
+    {
+      /* Output the thunk.  We know that the function is in the same
+	 translation unit (i.e., the same space) as the thunk, and that
+	 thunks are output after their method.  Thus, we don't need an
+	 external branch to reach the function.  With SOM and GAS,
+	 functions and thunks are effectively in different sections.
+	 Thus, we can always use a IA-relative branch and the linker
+	 will add a long branch stub if necessary.
+
+	 However, we have to be careful when generating PIC code on the
+	 SOM port to ensure that the sequence does not transfer to an
+	 import stub for the target function as this could clobber the
+	 return value saved at SP-24.  This would also apply to the
+	32-bit linux port if the multi-space model is implemented.  */
+      if ((!TARGET_LONG_CALLS && TARGET_SOM && !TARGET_PORTABLE_RUNTIME
+	   && !(flag_pic && TREE_PUBLIC (function))
+	   && (TARGET_GAS || last_address < 262132))
+	  || (!TARGET_LONG_CALLS && !TARGET_SOM && !TARGET_PORTABLE_RUNTIME
+	      && ((targetm_common.have_named_sections
+		   && DECL_SECTION_NAME (thunk_fndecl) != NULL
+		   /* The GNU 64-bit linker has rather poor stub management.
+		      So, we use a long branch from thunks that aren't in
+		      the same section as the target function.  */
+		    && ((!TARGET_64BIT
+			 && (DECL_SECTION_NAME (thunk_fndecl)
+			     != DECL_SECTION_NAME (function)))
+			|| ((DECL_SECTION_NAME (thunk_fndecl)
+			     == DECL_SECTION_NAME (function))
+			    && last_address < 262132)))
+		  /* In this case, we need to be able to reach the start of
+		     the stub table even though the function is likely closer
+		     and can be jumped to directly.  */
+		  || (targetm_common.have_named_sections
+		      && DECL_SECTION_NAME (thunk_fndecl) == NULL
+		      && DECL_SECTION_NAME (function) == NULL
+		      && total_code_bytes < MAX_PCREL17F_OFFSET)
+		  /* Likewise.  */
+		  || (!targetm_common.have_named_sections
+		      && total_code_bytes < MAX_PCREL17F_OFFSET))))
+	{
+	  if (!val_14)
+	    output_asm_insn ("addil L'%2,%%r26", xoperands);
+
+	  output_asm_insn ("b %0", xoperands);
+
+	  if (val_14)
+	    {
+	      output_asm_insn ("ldo %2(%%r26),%%r26", xoperands);
+	      nbytes += 8;
+	    }
+	  else
+	    {
+	      output_asm_insn ("ldo R'%2(%%r1),%%r26", xoperands);
+	      nbytes += 12;
+	    }
 	}
-      else
+      else if (TARGET_64BIT)
 	{
-	  output_asm_insn ("ldo R'%2(%%r1),%%r26", xoperands);
-	  nbytes += 12;
-	}
-    }
-  else if (TARGET_64BIT)
-    {
-      rtx xop[4];
+	  rtx xop[4];
+
+	  /* We only have one call-clobbered scratch register, so we can't
+	     make use of the delay slot if delta doesn't fit in 14 bits.  */
+	  if (!val_14)
+	    {
+	      output_asm_insn ("addil L'%2,%%r26", xoperands);
+	      output_asm_insn ("ldo R'%2(%%r1),%%r26", xoperands);
+	    }
+
+	  /* Load function address into %r1.  */
+	  xop[0] = xoperands[0];
+	  xop[1] = gen_rtx_REG (Pmode, 1);
+	  xop[2] = xop[1];
+	  pa_output_pic_pcrel_sequence (xop);

-      /* We only have one call-clobbered scratch register, so we can't
-         make use of the delay slot if delta doesn't fit in 14 bits.  */
-      if (!val_14)
+	  if (val_14)
+	    {
+	      output_asm_insn ("bv %%r0(%%r1)", xoperands);
+	      output_asm_insn ("ldo %2(%%r26),%%r26", xoperands);
+	      nbytes += 20;
+	    }
+	  else
+	    {
+	      output_asm_insn ("bv,n %%r0(%%r1)", xoperands);
+	      nbytes += 24;
+	    }
+	}
+      else if (TARGET_PORTABLE_RUNTIME)
 	{
-	  output_asm_insn ("addil L'%2,%%r26", xoperands);
-	  output_asm_insn ("ldo R'%2(%%r1),%%r26", xoperands);
+	  output_asm_insn ("ldil L'%0,%%r1", xoperands);
+	  output_asm_insn ("ldo R'%0(%%r1),%%r22", xoperands);
+
+	  if (!val_14)
+	    output_asm_insn ("ldil L'%2,%%r26", xoperands);
+
+	  output_asm_insn ("bv %%r0(%%r22)", xoperands);
+
+	  if (val_14)
+	    {
+	      output_asm_insn ("ldo %2(%%r26),%%r26", xoperands);
+	      nbytes += 16;
+	    }
+	  else
+	    {
+	      output_asm_insn ("ldo R'%2(%%r26),%%r26", xoperands);
+	      nbytes += 20;
+	    }
 	}
+      else if (TARGET_SOM && flag_pic && TREE_PUBLIC (function))
+	{
+	  /* The function is accessible from outside this module.  The only
+	     way to avoid an import stub between the thunk and function is to
+	     call the function directly with an indirect sequence similar to
+	     that used by $$dyncall.  This is possible because $$dyncall acts
+	     as the import stub in an indirect call.  */
+	  ASM_GENERATE_INTERNAL_LABEL (label, "LTHN", current_thunk_number);
+	  xoperands[3] = gen_rtx_SYMBOL_REF (Pmode, label);
+	  output_asm_insn ("addil LT'%3,%%r19", xoperands);
+	  output_asm_insn ("ldw RT'%3(%%r1),%%r22", xoperands);
+	  output_asm_insn ("ldw 0(%%sr0,%%r22),%%r22", xoperands);
+	  output_asm_insn ("bb,>=,n %%r22,30,.+16", xoperands);
+	  output_asm_insn ("depi 0,31,2,%%r22", xoperands);
+	  output_asm_insn ("ldw 4(%%sr0,%%r22),%%r19", xoperands);
+	  output_asm_insn ("ldw 0(%%sr0,%%r22),%%r22", xoperands);
+
+	  if (!val_14)
+	    {
+	      output_asm_insn ("addil L'%2,%%r26", xoperands);
+	      nbytes += 4;
+	    }

-      /* Load function address into %r1.  */
-      xop[0] = xoperands[0];
-      xop[1] = gen_rtx_REG (Pmode, 1);
-      xop[2] = xop[1];
-      pa_output_pic_pcrel_sequence (xop);
+	  if (TARGET_PA_20)
+	    {
+	      output_asm_insn ("bve (%%r22)", xoperands);
+	      nbytes += 36;
+	    }
+	  else if (TARGET_NO_SPACE_REGS)
+	    {
+	      output_asm_insn ("be 0(%%sr4,%%r22)", xoperands);
+	      nbytes += 36;
+	    }
+	  else
+	    {
+	      output_asm_insn ("ldsid (%%sr0,%%r22),%%r21", xoperands);
+	      output_asm_insn ("mtsp %%r21,%%sr0", xoperands);
+	      output_asm_insn ("be 0(%%sr0,%%r22)", xoperands);
+	      nbytes += 44;
+	    }

-      if (val_14)
-	{
-	  output_asm_insn ("bv %%r0(%%r1)", xoperands);
-	  output_asm_insn ("ldo %2(%%r26),%%r26", xoperands);
-	  nbytes += 20;
+	  if (val_14)
+	    output_asm_insn ("ldo %2(%%r26),%%r26", xoperands);
+	  else
+	    output_asm_insn ("ldo R'%2(%%r1),%%r26", xoperands);
 	}
-      else
+      else if (flag_pic)
 	{
-	  output_asm_insn ("bv,n %%r0(%%r1)", xoperands);
-	  nbytes += 24;
-	}
-    }
-  else if (TARGET_PORTABLE_RUNTIME)
-    {
-      output_asm_insn ("ldil L'%0,%%r1", xoperands);
-      output_asm_insn ("ldo R'%0(%%r1),%%r22", xoperands);
+	  rtx xop[4];

-      if (!val_14)
-	output_asm_insn ("ldil L'%2,%%r26", xoperands);
+	  /* Load function address into %r22.  */
+	  xop[0] = xoperands[0];
+	  xop[1] = gen_rtx_REG (Pmode, 1);
+	  xop[2] = gen_rtx_REG (Pmode, 22);
+	  pa_output_pic_pcrel_sequence (xop);

-      output_asm_insn ("bv %%r0(%%r22)", xoperands);
+	  if (!val_14)
+	    output_asm_insn ("addil L'%2,%%r26", xoperands);

-      if (val_14)
-	{
-	  output_asm_insn ("ldo %2(%%r26),%%r26", xoperands);
-	  nbytes += 16;
+	  output_asm_insn ("bv %%r0(%%r22)", xoperands);
+
+	  if (val_14)
+	    {
+	      output_asm_insn ("ldo %2(%%r26),%%r26", xoperands);
+	      nbytes += 20;
+	    }
+	  else
+	    {
+	      output_asm_insn ("ldo R'%2(%%r1),%%r26", xoperands);
+	      nbytes += 24;
+	    }
 	}
       else
 	{
-	  output_asm_insn ("ldo R'%2(%%r26),%%r26", xoperands);
-	  nbytes += 20;
+	  if (!val_14)
+	    output_asm_insn ("addil L'%2,%%r26", xoperands);
+
+	  output_asm_insn ("ldil L'%0,%%r22", xoperands);
+	  output_asm_insn ("be R'%0(%%sr4,%%r22)", xoperands);
+
+	  if (val_14)
+	    {
+	      output_asm_insn ("ldo %2(%%r26),%%r26", xoperands);
+	      nbytes += 12;
+	    }
+	  else
+	    {
+	      output_asm_insn ("ldo R'%2(%%r1),%%r26", xoperands);
+	      nbytes += 16;
+	    }
 	}
     }
-  else if (TARGET_SOM && flag_pic && TREE_PUBLIC (function))
+  else
     {
-      /* The function is accessible from outside this module.  The only
-	 way to avoid an import stub between the thunk and function is to
-	 call the function directly with an indirect sequence similar to
-	 that used by $$dyncall.  This is possible because $$dyncall acts
-	 as the import stub in an indirect call.  */
-      ASM_GENERATE_INTERNAL_LABEL (label, "LTHN", current_thunk_number);
-      xoperands[3] = gen_rtx_SYMBOL_REF (Pmode, label);
-      output_asm_insn ("addil LT'%3,%%r19", xoperands);
-      output_asm_insn ("ldw RT'%3(%%r1),%%r22", xoperands);
-      output_asm_insn ("ldw 0(%%sr0,%%r22),%%r22", xoperands);
-      output_asm_insn ("bb,>=,n %%r22,30,.+16", xoperands);
-      output_asm_insn ("depi 0,31,2,%%r22", xoperands);
-      output_asm_insn ("ldw 4(%%sr0,%%r22),%%r19", xoperands);
-      output_asm_insn ("ldw 0(%%sr0,%%r22),%%r22", xoperands);
+      rtx xop[4];

-      if (!val_14)
+      /* Add DELTA to THIS.  */
+      if (val_14)
 	{
-	  output_asm_insn ("addil L'%2,%%r26", xoperands);
+	  output_asm_insn ("ldo %2(%%r26),%%r26", xoperands);
 	  nbytes += 4;
 	}
-
-      if (TARGET_PA_20)
+      else
 	{
-	  output_asm_insn ("bve (%%r22)", xoperands);
-	  nbytes += 36;
+	  output_asm_insn ("addil L'%2,%%r26", xoperands);
+	  output_asm_insn ("ldo R'%2(%%r1),%%r26", xoperands);
+	  nbytes += 8;
 	}
-      else if (TARGET_NO_SPACE_REGS)
+
+      if (TARGET_64BIT)
 	{
-	  output_asm_insn ("be 0(%%sr4,%%r22)", xoperands);
-	  nbytes += 36;
+	  /* Load *(THIS + DELTA) to %r1.  */
+	  output_asm_insn ("ldd 0(%%r26),%%r1", xoperands);
+
+	  val_14 = VAL_14_BITS_P (vcall_offset);
+	  xoperands[2] = GEN_INT (vcall_offset);
+
+	  /* Load  *(*(THIS + DELTA) + VCALL_OFFSET) to %r1.  */
+	  if (val_14)
+	    {
+	      output_asm_insn ("ldd %2(%%r1),%%r1", xoperands);
+	      nbytes += 8;
+	    }
+	  else
+	    {
+	      output_asm_insn ("addil L'%2,%%r1", xoperands);
+	      output_asm_insn ("ldd R'%2(%%r1),%%r1", xoperands);
+	      nbytes += 12;
+	    }
 	}
       else
 	{
-	  output_asm_insn ("ldsid (%%sr0,%%r22),%%r21", xoperands);
-	  output_asm_insn ("mtsp %%r21,%%sr0", xoperands);
-	  output_asm_insn ("be 0(%%sr0,%%r22)", xoperands);
-	  nbytes += 44;
-	}
+	  /* Load *(THIS + DELTA) to %r1.  */
+	  output_asm_insn ("ldw 0(%%r26),%%r1", xoperands);

-      if (val_14)
-	output_asm_insn ("ldo %2(%%r26),%%r26", xoperands);
-      else
-	output_asm_insn ("ldo R'%2(%%r1),%%r26", xoperands);
-    }
-  else if (flag_pic)
-    {
-      rtx xop[4];
-
-      /* Load function address into %r22.  */
-      xop[0] = xoperands[0];
-      xop[1] = gen_rtx_REG (Pmode, 1);
-      xop[2] = gen_rtx_REG (Pmode, 22);
-      pa_output_pic_pcrel_sequence (xop);
+	  val_14 = VAL_14_BITS_P (vcall_offset);
+	  xoperands[2] = GEN_INT (vcall_offset);

-      if (!val_14)
-	output_asm_insn ("addil L'%2,%%r26", xoperands);
-
-      output_asm_insn ("bv %%r0(%%r22)", xoperands);
+	  /* Load  *(*(THIS + DELTA) + VCALL_OFFSET) to %r1.  */
+	  if (val_14)
+	    {
+	      output_asm_insn ("ldw %2(%%r1),%%r1", xoperands);
+	      nbytes += 8;
+	    }
+	  else
+	    {
+	      output_asm_insn ("addil L'%2,%%r1", xoperands);
+	      output_asm_insn ("ldw R'%2(%%r1),%%r1", xoperands);
+	      nbytes += 12;
+	    }
+	}

-      if (val_14)
+      /* Branch to FUNCTION and add %r1 to THIS in delay slot if possible.  */
+      if ((!TARGET_LONG_CALLS && TARGET_SOM && !TARGET_PORTABLE_RUNTIME
+	   && !(flag_pic && TREE_PUBLIC (function))
+	   && (TARGET_GAS || last_address < 262132))
+	  || (!TARGET_LONG_CALLS && !TARGET_SOM && !TARGET_PORTABLE_RUNTIME
+	      && ((targetm_common.have_named_sections
+		   && DECL_SECTION_NAME (thunk_fndecl) != NULL
+		   /* The GNU 64-bit linker has rather poor stub management.
+		      So, we use a long branch from thunks that aren't in
+		      the same section as the target function.  */
+		    && ((!TARGET_64BIT
+			 && (DECL_SECTION_NAME (thunk_fndecl)
+			     != DECL_SECTION_NAME (function)))
+			|| ((DECL_SECTION_NAME (thunk_fndecl)
+			     == DECL_SECTION_NAME (function))
+			    && last_address < 262132)))
+		  /* In this case, we need to be able to reach the start of
+		     the stub table even though the function is likely closer
+		     and can be jumped to directly.  */
+		  || (targetm_common.have_named_sections
+		      && DECL_SECTION_NAME (thunk_fndecl) == NULL
+		      && DECL_SECTION_NAME (function) == NULL
+		      && total_code_bytes < MAX_PCREL17F_OFFSET)
+		  /* Likewise.  */
+		  || (!targetm_common.have_named_sections
+		      && total_code_bytes < MAX_PCREL17F_OFFSET))))
 	{
-	  output_asm_insn ("ldo %2(%%r26),%%r26", xoperands);
-	  nbytes += 20;
+	  nbytes += 4;
+	  output_asm_insn ("b %0", xoperands);
+
+	  /* Add *(*(THIS + DELTA) + VCALL_OFFSET) to THIS.  */
+	  output_asm_insn ("addl %%r1,%%r26,%%r26", xoperands);
 	}
-      else
+      else if (TARGET_64BIT)
 	{
-	  output_asm_insn ("ldo R'%2(%%r1),%%r26", xoperands);
-	  nbytes += 24;
-	}
-    }
-  else
-    {
-      if (!val_14)
-	output_asm_insn ("addil L'%2,%%r26", xoperands);
+	  /* Add *(*(THIS + DELTA) + VCALL_OFFSET) to THIS.  */
+	  output_asm_insn ("addl %%r1,%%r26,%%r26", xoperands);

-      output_asm_insn ("ldil L'%0,%%r22", xoperands);
-      output_asm_insn ("be R'%0(%%sr4,%%r22)", xoperands);
+	  /* Load function address into %r1.  */
+	  nbytes += 16;
+	  xop[0] = xoperands[0];
+	  xop[1] = gen_rtx_REG (Pmode, 1);
+	  xop[2] = xop[1];
+	  pa_output_pic_pcrel_sequence (xop);

-      if (val_14)
+	  output_asm_insn ("bv,n %%r0(%%r1)", xoperands);
+	}
+      else if (TARGET_PORTABLE_RUNTIME)
 	{
-	  output_asm_insn ("ldo %2(%%r26),%%r26", xoperands);
+	  /* Load function address into %r22.  */
 	  nbytes += 12;
+	  output_asm_insn ("ldil L'%0,%%r22", xoperands);
+	  output_asm_insn ("ldo R'%0(%%r22),%%r22", xoperands);
+
+	  output_asm_insn ("bv %%r0(%%r22)", xoperands);
+
+	  /* Add *(*(THIS + DELTA) + VCALL_OFFSET) to THIS.  */
+	  output_asm_insn ("addl %%r1,%%r26,%%r26", xoperands);
+	}
+      else if (TARGET_SOM && flag_pic && TREE_PUBLIC (function))
+	{
+	  /* Add *(*(THIS + DELTA) + VCALL_OFFSET) to THIS.  */
+	  output_asm_insn ("addl %%r1,%%r26,%%r26", xoperands);
+
+	  /* The function is accessible from outside this module.  The only
+	     way to avoid an import stub between the thunk and function is to
+	     call the function directly with an indirect sequence similar to
+	     that used by $$dyncall.  This is possible because $$dyncall acts
+	     as the import stub in an indirect call.  */
+	  ASM_GENERATE_INTERNAL_LABEL (label, "LTHN", current_thunk_number);
+	  xoperands[3] = gen_rtx_SYMBOL_REF (Pmode, label);
+	  output_asm_insn ("addil LT'%3,%%r19", xoperands);
+	  output_asm_insn ("ldw RT'%3(%%r1),%%r22", xoperands);
+	  output_asm_insn ("ldw 0(%%sr0,%%r22),%%r22", xoperands);
+	  output_asm_insn ("bb,>=,n %%r22,30,.+16", xoperands);
+	  output_asm_insn ("depi 0,31,2,%%r22", xoperands);
+	  output_asm_insn ("ldw 4(%%sr0,%%r22),%%r19", xoperands);
+	  output_asm_insn ("ldw 0(%%sr0,%%r22),%%r22", xoperands);
+
+	  if (TARGET_PA_20)
+	    {
+	      output_asm_insn ("bve,n (%%r22)", xoperands);
+	      nbytes += 32;
+	    }
+	  else if (TARGET_NO_SPACE_REGS)
+	    {
+	      output_asm_insn ("be,n 0(%%sr4,%%r22)", xoperands);
+	      nbytes += 32;
+	    }
+	  else
+	    {
+	      output_asm_insn ("ldsid (%%sr0,%%r22),%%r21", xoperands);
+	      output_asm_insn ("mtsp %%r21,%%sr0", xoperands);
+	      output_asm_insn ("be,n 0(%%sr0,%%r22)", xoperands);
+	      nbytes += 40;
+	    }
 	}
-      else
+      else if (flag_pic)
 	{
-	  output_asm_insn ("ldo R'%2(%%r1),%%r26", xoperands);
+	  /* Add *(*(THIS + DELTA) + VCALL_OFFSET) to THIS.  */
+	  output_asm_insn ("addl %%r1,%%r26,%%r26", xoperands);
+
+	  /* Load function address into %r1.  */
 	  nbytes += 16;
+	  xop[0] = xoperands[0];
+	  xop[1] = gen_rtx_REG (Pmode, 1);
+	  xop[2] = xop[1];
+	  pa_output_pic_pcrel_sequence (xop);
+
+	  output_asm_insn ("bv,n %%r0(%%r1)", xoperands);
+	}
+      else
+	{
+	  /* Load function address into %r22.  */
+	  nbytes += 8;
+	  output_asm_insn ("ldil L'%0,%%r22", xoperands);
+	  output_asm_insn ("be R'%0(%%sr4,%%r22)", xoperands);
+
+	  /* Add *(*(THIS + DELTA) + VCALL_OFFSET) to THIS.  */
+	  output_asm_insn ("addl %%r1,%%r26,%%r26", xoperands);
 	}
     }