summaryrefslogtreecommitdiff
path: root/gcc/config/mn10300
diff options
context:
space:
mode:
authoraoliva <aoliva@138bc75d-0d04-0410-961f-82ee72b054a4>2003-07-10 03:25:32 +0000
committeraoliva <aoliva@138bc75d-0d04-0410-961f-82ee72b054a4>2003-07-10 03:25:32 +0000
commitb87a151a2a5914ee6dea4559cb1ca33d7c754231 (patch)
tree2431eaa1b2838fc78a056c559e1d707256745663 /gcc/config/mn10300
parent0bd69b3e01bb60df15a91b1df9d5527e553b11b6 (diff)
downloadgcc-b87a151a2a5914ee6dea4559cb1ca33d7c754231.tar.gz
2003-06-16 Alexandre Oliva <aoliva@redhat.com>
* config/mn10300/mn10300.c (mn10300_unspec_int_label_counter): Moved from... * config/mn10300/mn10300.md (GOTaddr2picreg): ... here. * config/mn10300/mn10300.h: GTY-declare it. 2003-06-11 Alexandre Oliva <aoliva@redhat.com> * config/mn10300/mn10300.c (mn10300_encode_section_info): Fix prototype. Use incoming RTL argument. 2002-12-12 Alexandre Oliva <aoliva@redhat.com> * config/mn10300/mn10300.md (int_label): Move C statements... (GOTaddr2picreg): ... here. 2002-08-15 Alexandre Oliva <aoliva@redhat.com> * config/mn10300/mn10300.h (ENCODE_SECTION_INFO): Move... * config/mn10300/mn10300.c (mn10300_encode_section_info): ... here. New function. (TARGET_ENCODE_SECTION_INFO): Define to it. 2001-11-04 Alexandre Oliva <aoliva@redhat.com> * config/mn10300/mn10300.md (builtin_setjmp_receiver): Fix typo in pattern name. (mn10300_loadPC): Define as insn splittable after reload. 2001-05-13 Alexandre Oliva <aoliva@redhat.com> * config/sh/mn10300.h (JUMP_TABLES_IN_TEXT_SECTION): Let them be defined in .rodata even in PIC, now that the assembler supports that. 2001-05-09 Alexandre Oliva <aoliva@redhat.com> * config/mn10300/mn10300.h (GOT_SYMBOL_NAME): Don't let the symbol take an underscore prefix. 2001-04-14 Alexandre Oliva <aoliva@redhat.com> * config/mn10300/mn10300-protos.h (legitimate_pic_operand_p, legitimize_pic_address): Declare. * config/mn10300/mn10300.h (CONDITIONAL_REGISTER_USAGE): Mark the PIC register as fixed. (EXTRA_CONSTRAINT): Match UNSPEC_PLT and UNSPEC_PIC for 'S'. (GO_IF_LEGITIMATE_ADDRESS): Require legitimate_pic_operand for PIC. (LEGITIMATE_PIC_OPERAND_P): Define. (PIC_OFFSET_TABLE_REGNUM): Define. (GOT_SYMBOL_NAME): Define. (SYMBOLIC_CONST_P): Define. (ENCODE_SECTION_INFO): Use SYMBOL_REF_FLAG to mark local symbols. (MN10300_GLOBAL_P): Test it. (OUTPUT_ADDR_CONST_EXTRA): Handle PIC-related unspecs. (JUMP_TABLES_IN_TEXT_SECTION): Enable for PIC. * config/mn10300/mn10300.c (print_operand): Handle unspec. (expand_prologue): Set PIC register. (call_address_operand): Don't match SYMBOL_REFs in PIC. (legitimize_address): Call legitimize_pic_address. (legitimize_pic_address): New fn. (legitimate_pic_operand_p): New fn. * config/mn10300/mn10300.md (PIC_REG, SP_REG): New constants. (UNSPEC_INT_LABEL, UNSPEC_PIC, UNSPEC_GOT, UNSPEC_GOTOFF, UNSPEC_PLT): New constants. (pop_pic_reg): New insn. (movsi): Adjust non-PIC addresses. (builtin_setjmp_receiver): Restore the PIC register. (casesi): New insn. (call): Adjust non-PIC addresses. (int_label, GOTaddr2picreg): New expands. (am33_loadPC): New insn. (mn10300_loadPC): New expand. (call_next_insn): New insn. (add_GOT_to_pic_reg): New expand. (symGOT2reg, symGOT2reg_i): New expands. (symGOTOFF2reg, symGOTOFF2reg_i): New expands. (sym2PIC, sym2PLT): New expands. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@69169 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/config/mn10300')
-rw-r--r--gcc/config/mn10300/mn10300-protos.h2
-rw-r--r--gcc/config/mn10300/mn10300.c122
-rw-r--r--gcc/config/mn10300/mn10300.h66
-rw-r--r--gcc/config/mn10300/mn10300.md240
4 files changed, 429 insertions, 1 deletions
diff --git a/gcc/config/mn10300/mn10300-protos.h b/gcc/config/mn10300/mn10300-protos.h
index 209a7e6c891..74154d92494 100644
--- a/gcc/config/mn10300/mn10300-protos.h
+++ b/gcc/config/mn10300/mn10300-protos.h
@@ -26,6 +26,8 @@ extern void mn10300_va_start PARAMS ((tree, rtx));
#endif /* TREE_CODE */
extern struct rtx_def *legitimize_address PARAMS ((rtx, rtx, enum machine_mode));
+extern rtx legitimize_pic_address (rtx, rtx);
+extern int legitimate_pic_operand_p (rtx);
extern void print_operand PARAMS ((FILE *, rtx, int));
extern void print_operand_address PARAMS ((FILE *, rtx));
extern void mn10300_print_reg_list PARAMS ((FILE *, int));
diff --git a/gcc/config/mn10300/mn10300.c b/gcc/config/mn10300/mn10300.c
index bb14b4c9023..e4ef4bffe8b 100644
--- a/gcc/config/mn10300/mn10300.c
+++ b/gcc/config/mn10300/mn10300.c
@@ -44,6 +44,10 @@ Boston, MA 02111-1307, USA. */
#include "target.h"
#include "target-def.h"
+/* This is used by GOTaddr2picreg to uniquely identify
+ UNSPEC_INT_LABELs. */
+int mn10300_unspec_int_label_counter;
+
/* The size of the callee register save area. Right now we save everything
on entry since it costs us nothing in code size. It does cost us from a
speed standpoint, so we want to optimize this sooner or later. */
@@ -75,6 +79,10 @@ static void mn10300_file_start PARAMS ((void));
#undef TARGET_ASM_FILE_START_FILE_DIRECTIVE
#define TARGET_ASM_FILE_START_FILE_DIRECTIVE true
+#undef TARGET_ENCODE_SECTION_INFO
+#define TARGET_ENCODE_SECTION_INFO mn10300_encode_section_info
+
+static void mn10300_encode_section_info (tree, rtx, int);
struct gcc_target targetm = TARGET_INITIALIZER;
static void
@@ -410,6 +418,7 @@ print_operand (file, x, code)
case CONST:
case LABEL_REF:
case CODE_LABEL:
+ case UNSPEC:
print_operand_address (file, x);
break;
default:
@@ -881,6 +890,24 @@ expand_prologue ()
emit_insn (gen_addsi3 (stack_pointer_rtx,
stack_pointer_rtx,
GEN_INT (-size)));
+ if (flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM])
+ {
+ rtx insn = get_last_insn ();
+ rtx last = emit_insn (gen_GOTaddr2picreg ());
+
+ /* Mark these insns as possibly dead. Sometimes, flow2 may
+ delete all uses of the PIC register. In this case, let it
+ delete the initialization too. */
+ do
+ {
+ insn = NEXT_INSN (insn);
+
+ REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD,
+ const0_rtx,
+ REG_NOTES (insn));
+ }
+ while (insn != last);
+ }
}
void
@@ -1269,6 +1296,9 @@ call_address_operand (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
+ if (flag_pic)
+ return (EXTRA_CONSTRAINT (op, 'S') || GET_CODE (op) == REG);
+
return (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == REG);
}
@@ -1756,6 +1786,9 @@ legitimize_address (x, oldx, mode)
rtx oldx ATTRIBUTE_UNUSED;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
+ if (flag_pic && ! legitimate_pic_operand_p (x))
+ x = legitimize_pic_address (oldx, NULL_RTX);
+
/* Uh-oh. We might have an address for x[n-100000]. This needs
special handling to avoid creating an indexed memory address
with x-100000 as the base. */
@@ -1786,6 +1819,75 @@ legitimize_address (x, oldx, mode)
return x;
}
+/* Convert a non-PIC address in `orig' to a PIC address using @GOT or
+ @GOTOFF in `reg'. */
+rtx
+legitimize_pic_address (orig, reg)
+ rtx orig;
+ rtx reg;
+{
+ if (GET_CODE (orig) == LABEL_REF
+ || (GET_CODE (orig) == SYMBOL_REF
+ && (CONSTANT_POOL_ADDRESS_P (orig)
+ || ! MN10300_GLOBAL_P (orig))))
+ {
+ if (reg == 0)
+ reg = gen_reg_rtx (Pmode);
+
+ emit_insn (gen_symGOTOFF2reg (reg, orig));
+ return reg;
+ }
+ else if (GET_CODE (orig) == SYMBOL_REF)
+ {
+ if (reg == 0)
+ reg = gen_reg_rtx (Pmode);
+
+ emit_insn (gen_symGOT2reg (reg, orig));
+ return reg;
+ }
+ return orig;
+}
+
+/* Return zero if X references a SYMBOL_REF or LABEL_REF whose symbol
+ isn't protected by a PIC unspec; non-zero otherwise. */
+int
+legitimate_pic_operand_p (x)
+ rtx x;
+{
+ register const char *fmt;
+ register int i;
+
+ if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF)
+ return 0;
+
+ if (GET_CODE (x) == UNSPEC
+ && (XINT (x, 1) == UNSPEC_PIC
+ || XINT (x, 1) == UNSPEC_GOT
+ || XINT (x, 1) == UNSPEC_GOTOFF
+ || XINT (x, 1) == UNSPEC_PLT))
+ return 1;
+
+ if (GET_CODE (x) == QUEUED)
+ return legitimate_pic_operand_p (QUEUED_VAR (x));
+
+ fmt = GET_RTX_FORMAT (GET_CODE (x));
+ for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'E')
+ {
+ register int j;
+
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ if (! legitimate_pic_operand_p (XVECEXP (x, i, j)))
+ return 0;
+ }
+ else if (fmt[i] == 'e' && ! legitimate_pic_operand_p (XEXP (x, i)))
+ return 0;
+ }
+
+ return 1;
+}
+
static int
mn10300_address_cost_1 (x, unsig)
rtx x;
@@ -1973,3 +2075,23 @@ mn10300_wide_const_load_uses_clr (operands)
return val[0] == 0 || val[1] == 0;
}
+/* If using PIC, mark a SYMBOL_REF for a non-global symbol so that we
+ may access it using GOTOFF instead of GOT. */
+
+static void
+mn10300_encode_section_info (decl, rtl, first)
+ tree decl;
+ rtx rtl;
+ int first;
+{
+ rtx symbol;
+
+ if (GET_CODE (rtl) != MEM)
+ return;
+ symbol = XEXP (rtl, 0);
+ if (GET_CODE (symbol) != SYMBOL_REF)
+ return;
+
+ if (flag_pic)
+ SYMBOL_REF_FLAG (symbol) = (*targetm.binds_local_p) (decl);
+}
diff --git a/gcc/config/mn10300/mn10300.h b/gcc/config/mn10300/mn10300.h
index 75d4183af74..2432a38a1ce 100644
--- a/gcc/config/mn10300/mn10300.h
+++ b/gcc/config/mn10300/mn10300.h
@@ -46,6 +46,8 @@ Boston, MA 02111-1307, USA. */
extern int target_flags;
+extern GTY(()) int mn10300_unspec_int_label_counter;
+
/* Macros used in the machine description to test the flags. */
/* Macro to define tables used to set the flags.
@@ -212,6 +214,8 @@ extern int target_flags;
i++) \
fixed_regs[i] = call_used_regs[i] = 1; \
} \
+ if (flag_pic) \
+ fixed_regs[PIC_OFFSET_TABLE_REGNUM] = 1; \
}
/* Return number of consecutive hard regs needed starting at reg REGNO
@@ -735,6 +739,9 @@ struct cum_arg {int nbytes; };
#define EXTRA_CONSTRAINT(OP, C) \
((C) == 'R' ? OK_FOR_R (OP) \
: (C) == 'Q' ? OK_FOR_Q (OP) \
+ : (C) == 'S' && flag_pic \
+ ? GET_CODE (OP) == UNSPEC && (XINT (OP, 1) == UNSPEC_PLT \
+ || XINT (OP, 1) == UNSPEC_PIC) \
: (C) == 'S' ? GET_CODE (OP) == SYMBOL_REF \
: (C) == 'T' ? OK_FOR_T (OP) \
: 0)
@@ -775,7 +782,8 @@ struct cum_arg {int nbytes; };
#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \
{ \
- if (CONSTANT_ADDRESS_P (X)) \
+ if (CONSTANT_ADDRESS_P (X) \
+ && (! flag_pic || legitimate_pic_operand_p (X))) \
goto ADDR; \
if (RTX_OK_FOR_BASE_P (X)) \
goto ADDR; \
@@ -797,6 +805,8 @@ struct cum_arg {int nbytes; };
{ \
if (GET_CODE (index) == CONST_INT) \
goto ADDR; \
+ if (GET_CODE (index) == CONST) \
+ goto ADDR; \
} \
} \
}
@@ -833,6 +843,60 @@ struct cum_arg {int nbytes; };
#define LEGITIMATE_CONSTANT_P(X) 1
+/* Zero if this needs fixing up to become PIC. */
+
+#define LEGITIMATE_PIC_OPERAND_P(X) (legitimate_pic_operand_p (X))
+
+/* Register to hold the addressing base for
+ position independent code access to data items. */
+#define PIC_OFFSET_TABLE_REGNUM PIC_REG
+
+/* The name of the pseudo-symbol representing the Global Offset Table. */
+#define GOT_SYMBOL_NAME "*_GLOBAL_OFFSET_TABLE_"
+
+#define SYMBOLIC_CONST_P(X) \
+((GET_CODE (X) == SYMBOL_REF || GET_CODE (X) == LABEL_REF) \
+ && ! LEGITIMATE_PIC_OPERAND_P (X))
+
+/* Non-global SYMBOL_REFs have SYMBOL_REF_FLAG enabled. */
+#define MN10300_GLOBAL_P(X) (! SYMBOL_REF_FLAG (X))
+
+/* Recognize machine-specific patterns that may appear within
+ constants. Used for PIC-specific UNSPECs. */
+#define OUTPUT_ADDR_CONST_EXTRA(STREAM, X, FAIL) \
+ do \
+ if (GET_CODE (X) == UNSPEC && XVECLEN ((X), 0) == 1) \
+ { \
+ switch (XINT ((X), 1)) \
+ { \
+ case UNSPEC_INT_LABEL: \
+ asm_fprintf ((STREAM), ".%LLIL%d", \
+ INTVAL (XVECEXP ((X), 0, 0))); \
+ break; \
+ case UNSPEC_PIC: \
+ /* GLOBAL_OFFSET_TABLE or local symbols, no suffix. */ \
+ output_addr_const ((STREAM), XVECEXP ((X), 0, 0)); \
+ break; \
+ case UNSPEC_GOT: \
+ output_addr_const ((STREAM), XVECEXP ((X), 0, 0)); \
+ fputs ("@GOT", (STREAM)); \
+ break; \
+ case UNSPEC_GOTOFF: \
+ output_addr_const ((STREAM), XVECEXP ((X), 0, 0)); \
+ fputs ("@GOTOFF", (STREAM)); \
+ break; \
+ case UNSPEC_PLT: \
+ output_addr_const ((STREAM), XVECEXP ((X), 0, 0)); \
+ fputs ("@PLT", (STREAM)); \
+ break; \
+ default: \
+ goto FAIL; \
+ } \
+ break; \
+ } \
+ else \
+ goto FAIL; \
+ while (0)
/* Tell final.c how to eliminate redundant test instructions. */
diff --git a/gcc/config/mn10300/mn10300.md b/gcc/config/mn10300/mn10300.md
index a615f6b97ea..27a40ef3720 100644
--- a/gcc/config/mn10300/mn10300.md
+++ b/gcc/config/mn10300/mn10300.md
@@ -37,6 +37,17 @@
;; clobber - value of cc is unknown
(define_attr "cc" "none,none_0hit,set_znv,set_zn,compare,clobber,invert"
(const_string "clobber"))
+
+(define_constants [
+ (PIC_REG 6)
+ (SP_REG 9)
+
+ (UNSPEC_INT_LABEL 0)
+ (UNSPEC_PIC 1)
+ (UNSPEC_GOT 2)
+ (UNSPEC_GOTOFF 3)
+ (UNSPEC_PLT 4)
+])
;; ----------------------------------------------------------------------
;; MOVE INSTRUCTIONS
@@ -269,6 +280,12 @@
DONE;
}")
+(define_insn "pop_pic_reg"
+ [(set (reg:SI PIC_REG)
+ (mem:SI (post_inc:SI (reg:SI SP_REG))))]
+ "reload_completed"
+ "movm (sp),[a2]")
+
(define_expand "movsi"
[(set (match_operand:SI 0 "general_operand" "")
(match_operand:SI 1 "general_operand" ""))]
@@ -279,6 +296,33 @@
if (!register_operand (operand1, SImode)
&& !register_operand (operand0, SImode))
operands[1] = copy_to_mode_reg (SImode, operand1);
+ if (flag_pic)
+ {
+ rtx temp;
+ if (SYMBOLIC_CONST_P (operands[1]))
+ {
+ if (GET_CODE (operands[0]) == MEM)
+ operands[1] = force_reg (Pmode, operands[1]);
+ else
+ {
+ temp = no_new_pseudos ? operands[0] : gen_reg_rtx (Pmode);
+ operands[1] = legitimize_pic_address (operands[1], temp);
+ }
+ }
+ else if (GET_CODE (operands[1]) == CONST
+ && GET_CODE (XEXP (operands[1], 0)) == PLUS
+ && SYMBOLIC_CONST_P (XEXP (XEXP (operands[1], 0), 0)))
+ {
+ temp = no_new_pseudos ? operands[0] : gen_reg_rtx (Pmode);
+ temp = legitimize_pic_address (XEXP (XEXP (operands[1], 0), 0),
+ temp);
+ operands[1] = expand_binop (SImode, add_optab, temp,
+ XEXP (XEXP (operands[1], 0), 1),
+ no_new_pseudos ? temp
+ : gen_reg_rtx (Pmode),
+ 0, OPTAB_LIB_WIDEN);
+ }
+ }
}")
(define_insn ""
@@ -1666,6 +1710,43 @@
"jmp (%0)"
[(set_attr "cc" "none")])
+(define_expand "builtin_setjmp_receiver"
+ [(match_operand 0 "" "")]
+ "flag_pic"
+ "
+{
+ if (flag_pic)
+ emit_insn (gen_GOTaddr2picreg ());
+
+ DONE;
+}")
+
+(define_expand "casesi"
+ [(match_operand:SI 0 "register_operand" "")
+ (match_operand:SI 1 "immediate_operand" "")
+ (match_operand:SI 2 "immediate_operand" "")
+ (match_operand 3 "" "") (match_operand 4 "" "")]
+ ""
+ "
+{
+ rtx table = gen_reg_rtx (SImode);
+ rtx index = gen_reg_rtx (SImode);
+ rtx addr = gen_reg_rtx (Pmode);
+
+ emit_move_insn (table, gen_rtx_LABEL_REF (VOIDmode, operands[3]));
+ emit_move_insn (index, plus_constant (operands[0], - INTVAL (operands[1])));
+ emit_insn (gen_cmpsi (index, operands[2]));
+ emit_jump_insn (gen_bgtu (operands[4]));
+ emit_move_insn (index, gen_rtx_ASHIFT (SImode, index, GEN_INT (2)));
+ emit_move_insn (addr, gen_rtx_MEM (SImode,
+ gen_rtx_PLUS (SImode, table, index)));
+ if (flag_pic)
+ emit_move_insn (addr, gen_rtx_PLUS (SImode, addr, table));
+
+ emit_jump_insn (gen_tablejump (addr, operands[3]));
+ DONE;
+}")
+
(define_insn "tablejump"
[(set (pc) (match_operand:SI 0 "register_operand" "a"))
(use (label_ref (match_operand 1 "" "")))]
@@ -1681,6 +1762,20 @@
""
"
{
+ if (flag_pic && GET_CODE (XEXP (operands[0], 0)) == SYMBOL_REF)
+ {
+ if (MN10300_GLOBAL_P (XEXP (operands[0], 0)))
+ {
+ /* The PLT code won't run on AM30, but then, there's no
+ shared library support for AM30 either, so we just assume
+ the linker is going to adjust all @PLT relocs to the
+ actual symbols. */
+ emit_insn (gen_rtx_USE (VOIDmode, pic_offset_table_rtx));
+ XEXP (operands[0], 0) = gen_sym2PLT (XEXP (operands[0], 0));
+ }
+ else
+ XEXP (operands[0], 0) = gen_sym2PIC (XEXP (operands[0], 0));
+ }
if (! call_address_operand (XEXP (operands[0], 0), VOIDmode))
XEXP (operands[0], 0) = force_reg (SImode, XEXP (operands[0], 0));
emit_call_insn (gen_call_internal (XEXP (operands[0], 0), operands[1]));
@@ -1710,6 +1805,20 @@
""
"
{
+ if (flag_pic && GET_CODE (XEXP (operands[1], 0)) == SYMBOL_REF)
+ {
+ if (MN10300_GLOBAL_P (XEXP (operands[1], 0)))
+ {
+ /* The PLT code won't run on AM30, but then, there's no
+ shared library support for AM30 either, so we just assume
+ the linker is going to adjust all @PLT relocs to the
+ actual symbols. */
+ emit_insn (gen_rtx_USE (VOIDmode, pic_offset_table_rtx));
+ XEXP (operands[1], 0) = gen_sym2PLT (XEXP (operands[1], 0));
+ }
+ else
+ XEXP (operands[1], 0) = gen_sym2PIC (XEXP (operands[1], 0));
+ }
if (! call_address_operand (XEXP (operands[1], 0), VOIDmode))
XEXP (operands[1], 0) = force_reg (SImode, XEXP (operands[1], 0));
emit_call_insn (gen_call_value_internal (operands[0],
@@ -2402,3 +2511,134 @@
"add %0,%0\;bcc %1"
[(set_attr "cc" "clobber")])
+(define_expand "int_label"
+ [(unspec [(match_operand:SI 0 "" "")] UNSPEC_INT_LABEL)]
+ "" "")
+
+(define_expand "GOTaddr2picreg"
+ [(match_dup 0)]
+ "" "
+{
+ /* It would be nice to be able to have int_label keep track of the
+ counter and all, but if we add C code to it, we'll get an insn
+ back, and we just want the pattern. */
+ operands[0] = gen_int_label (GEN_INT (mn10300_unspec_int_label_counter++));
+ if (TARGET_AM33)
+ emit_insn (gen_am33_loadPC (operands[0]));
+ else
+ emit_insn (gen_mn10300_loadPC (operands[0]));
+ emit_insn (gen_add_GOT_to_pic_reg (operands[0]));
+ DONE;
+}
+")
+
+(define_insn "am33_loadPC"
+ [(parallel
+ [(set (reg:SI PIC_REG) (pc))
+ (use (match_operand 0 "" ""))])]
+ "TARGET_AM33"
+ "%0:\;mov pc,a2")
+
+
+(define_insn_and_split "mn10300_loadPC"
+ [(parallel
+ [(set (reg:SI PIC_REG) (pc))
+ (use (match_operand 0 "" ""))])]
+ ""
+ "#"
+ "reload_completed"
+ [(match_operand 0 "" "")]
+ "
+{
+ rtx sp_reg = gen_rtx_REG (SImode, SP_REG);
+ int need_stack_space = (get_frame_size () == 0
+ && current_function_outgoing_args_size == 0);
+
+ if (need_stack_space)
+ emit_move_insn (sp_reg, plus_constant (sp_reg, -4));
+
+ emit_insn (gen_call_next_insn (operands[0]));
+
+ if (need_stack_space)
+ emit_insn (gen_pop_pic_reg ());
+ else
+ emit_move_insn (pic_offset_table_rtx, gen_rtx_MEM (SImode, sp_reg));
+
+ DONE;
+}")
+
+(define_insn "call_next_insn"
+ [(parallel
+ [(set (mem:SI (reg:SI SP_REG)) (pc))
+ (use (match_operand 0 "" ""))])]
+ "reload_completed"
+ "calls %0\;%0:")
+
+(define_expand "add_GOT_to_pic_reg"
+ [(set (reg:SI PIC_REG)
+ (plus:SI
+ (reg:SI PIC_REG)
+ (const
+ (unspec [(minus:SI
+ (match_dup 1)
+ (const (minus:SI
+ (const (match_operand:SI 0 "" ""))
+ (pc))))
+ ] UNSPEC_PIC))))]
+ ""
+ "
+{
+ operands[1] = gen_rtx_SYMBOL_REF (VOIDmode, GOT_SYMBOL_NAME);
+}")
+
+(define_expand "symGOT2reg"
+ [(match_operand:SI 0 "" "")
+ (match_operand:SI 1 "" "")]
+ ""
+ "
+{
+ rtx insn = emit_insn (gen_symGOT2reg_i (operands[0], operands[1]));
+
+ RTX_UNCHANGING_P (SET_SRC (PATTERN (insn))) = 1;
+
+ REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EQUAL, operands[1],
+ REG_NOTES (insn));
+
+ DONE;
+}")
+
+(define_expand "symGOT2reg_i"
+ [(set (match_operand:SI 0 "" "")
+ (mem:SI (plus:SI (reg:SI PIC_REG)
+ (const (unspec [(match_operand:SI 1 "" "")]
+ UNSPEC_GOT)))))]
+ ""
+ "")
+
+(define_expand "symGOTOFF2reg"
+ [(match_operand:SI 0 "" "") (match_operand:SI 1 "" "")]
+ ""
+ "
+{
+ rtx insn = emit_insn (gen_symGOTOFF2reg_i (operands[0], operands[1]));
+
+ REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EQUAL, operands[1],
+ REG_NOTES (insn));
+
+ DONE;
+}")
+
+(define_expand "symGOTOFF2reg_i"
+ [(set (match_operand:SI 0 "" "")
+ (const (unspec [(match_operand:SI 1 "" "")] UNSPEC_GOTOFF)))
+ (set (match_dup 0) (plus:SI (match_dup 0) (reg:SI PIC_REG)))]
+ ""
+ "")
+
+(define_expand "sym2PIC"
+ [(unspec [(match_operand:SI 0 "" "")] UNSPEC_PIC)]
+ "" "")
+
+(define_expand "sym2PLT"
+ [(unspec [(match_operand:SI 0 "" "")] UNSPEC_PLT)]
+ "" "")