From: Richard Sandiford Date: Sat, 2 Feb 2008 09:55:42 +0000 (+0000) Subject: re PR target/34981 (Lazily-bound function called twice) X-Git-Tag: prereleases/gcc-4.2.4-rc1~171 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5e75160e52cc2e419732969ff621abb5a1582e0b;p=thirdparty%2Fgcc.git re PR target/34981 (Lazily-bound function called twice) gcc/ PR target/34981 * config/mips/mips-protos.h (mips_expand_call): Return an rtx. (build_mips16_call_stub): Likewise. * config/mips/mips.h (FIRST_PSEUDO_REGISTER): Rename FAKE_CALL_REGNO to GOT_VERSION_REGNUM. (CALL_REALLY_USED_REGISTERS): Set the GOT_VERSION_REGNUM entry to 0. (EPILOGUE_USES): Include GOT_VERSION_REGNUM if TARGET_ABICALLS. * config/mips/mips.c (mips_emit_call_insn): New function. (mips_call_tls_get_addr): Call mips_expand_call directly. (mips_expand_call): Update the call to build_mips16_call_stub and remove a redundant condition. Assert that MIPS16 stubs do not use lazy binding. Use mips_emit_call_insn and return the call insn. (override_options): Allow SImode for GOT_VERSION_REGNUM. (build_mips16_call_stub): Use mips_emit_call_insn rather than emit_call_insn. Return the call insn or null. (mips_avoid_hazard): Remove hazard_set handling. (mips_extra_live_on_entry): Include GOT_VERSION_REGNUM if TARGET_ABICALLS. * config/mips/mips.md (UNSPEC_EH_RECEIVER): Rename to... (UNSPEC_RESTORE_GP): ...this. (UNSPEC_SET_GOT_VERSION, UNSPEC_UPDATE_GOT_VERSION): New constants. (FAKE_CALL_REGNO): Rename to... (GOT_VERSION_REGNUM): ...this. (type): Add "ghost" value. Add an associated insn reservation. (hazard_set): Remove. (exception_receiver): Rename to... (restore_gp): ...this and update the unspec identifier accordingly. (exception_receiver, nonlocal_got_receiver): New expanders. (load_call): Use GOT_VERSION_REGNUM. Don't set FAKE_CALL_REGNO. Remove hazard_set attribute. (set_got_version, update_got_version): New patterns. gcc/testsuite/ PR target/34981 * gcc.target/mips/lazy-binding-1.c: New test. * gcc.target/mips/mips.exp (setup_mips_tests): Set mips_abi, mips_forced_gp, mips_forced_no_abicalls, mips_forced_no_shared and mips_forced_no_er. (dg-mips-options): Avoid using -mips16 -mhard-float for ABIs other than o32 and o64. Avoid using -mabicalls with an implicit -mabi=eabi. Avoid using small data with -mabicalls. Skip -mabi=*, -G*, -mabicalls, -mshared and -mexplicit-relocs tests if the multilib forces the an incompatible option. From-SVN: r132067 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 51ac67dc5032..97346e60e6b4 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,37 @@ +2008-02-02 Richard Sandiford + + PR target/34981 + * config/mips/mips-protos.h (mips_expand_call): Return an rtx. + (build_mips16_call_stub): Likewise. + * config/mips/mips.h (FIRST_PSEUDO_REGISTER): Rename FAKE_CALL_REGNO + to GOT_VERSION_REGNUM. + (CALL_REALLY_USED_REGISTERS): Set the GOT_VERSION_REGNUM entry to 0. + (EPILOGUE_USES): Include GOT_VERSION_REGNUM if TARGET_ABICALLS. + * config/mips/mips.c (mips_emit_call_insn): New function. + (mips_call_tls_get_addr): Call mips_expand_call directly. + (mips_expand_call): Update the call to build_mips16_call_stub + and remove a redundant condition. Assert that MIPS16 stubs do not + use lazy binding. Use mips_emit_call_insn and return the call insn. + (override_options): Allow SImode for GOT_VERSION_REGNUM. + (build_mips16_call_stub): Use mips_emit_call_insn rather than + emit_call_insn. Return the call insn or null. + (mips_avoid_hazard): Remove hazard_set handling. + (mips_extra_live_on_entry): Include GOT_VERSION_REGNUM if + TARGET_ABICALLS. + * config/mips/mips.md (UNSPEC_EH_RECEIVER): Rename to... + (UNSPEC_RESTORE_GP): ...this. + (UNSPEC_SET_GOT_VERSION, UNSPEC_UPDATE_GOT_VERSION): New constants. + (FAKE_CALL_REGNO): Rename to... + (GOT_VERSION_REGNUM): ...this. + (type): Add "ghost" value. Add an associated insn reservation. + (hazard_set): Remove. + (exception_receiver): Rename to... + (restore_gp): ...this and update the unspec identifier accordingly. + (exception_receiver, nonlocal_got_receiver): New expanders. + (load_call): Use GOT_VERSION_REGNUM. Don't set + FAKE_CALL_REGNO. Remove hazard_set attribute. + (set_got_version, update_got_version): New patterns. + 2008-02-02 Richard Sandiford PR target/31388 diff --git a/gcc/config/mips/mips-protos.h b/gcc/config/mips/mips-protos.h index 0cfe76756d55..737cc176a6af 100644 --- a/gcc/config/mips/mips-protos.h +++ b/gcc/config/mips/mips-protos.h @@ -171,7 +171,7 @@ extern void mips_expand_vcondv2sf (rtx, rtx, rtx, enum rtx_code, rtx, rtx); #endif extern void gen_conditional_move (rtx *); extern void mips_gen_conditional_trap (rtx *); -extern void mips_expand_call (rtx, rtx, rtx, rtx, int); +extern rtx mips_expand_call (rtx, rtx, rtx, rtx, int); extern void mips_emit_fcc_reload (rtx, rtx, rtx); extern void mips_set_return_address (rtx, rtx); extern bool mips_expand_block_move (rtx, rtx, rtx); @@ -231,7 +231,7 @@ extern enum reg_class mips_secondary_reload_class (enum reg_class, enum machine_mode, rtx, int); extern int mips_class_max_nregs (enum reg_class, enum machine_mode); -extern int build_mips16_call_stub (rtx, rtx, rtx, int); +extern rtx build_mips16_call_stub (rtx, rtx, rtx, int); extern int mips_register_move_cost (enum machine_mode, enum reg_class, enum reg_class); diff --git a/gcc/config/mips/mips.c b/gcc/config/mips/mips.c index da3ddf836d0e..1abbe49355b3 100644 --- a/gcc/config/mips/mips.c +++ b/gcc/config/mips/mips.c @@ -1904,6 +1904,31 @@ mips_idiv_insns (void) count++; return count; } + +/* Emit a call sequence with call pattern PATTERN and return the call + instruction itself (which is not necessarily the last instruction + emitted). LAZY_P is true if the call address is lazily-bound. */ + +static rtx +mips_emit_call_insn (rtx pattern, bool lazy_p) +{ + rtx insn; + + insn = emit_call_insn (pattern); + + /* Lazy-binding stubs require $gp to be valid on entry. */ + if (lazy_p) + use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx); + + if (TARGET_ABICALLS) + { + /* See the comment above load_call for details. */ + use_reg (&CALL_INSN_FUNCTION_USAGE (insn), + gen_rtx_REG (Pmode, GOT_VERSION_REGNUM)); + emit_insn (gen_update_got_version ()); + } + return insn; +} /* This function is used to implement GO_IF_LEGITIMATE_ADDRESS. It returns a nonzero value if X is a legitimate address for a memory @@ -2044,7 +2069,7 @@ static GTY(()) rtx mips_tls_symbol; static rtx mips_call_tls_get_addr (rtx sym, enum mips_symbol_type type, rtx v0) { - rtx insn, loc, tga, a0; + rtx insn, loc, a0; a0 = gen_rtx_REG (Pmode, GP_ARG_FIRST); @@ -2057,8 +2082,7 @@ mips_call_tls_get_addr (rtx sym, enum mips_symbol_type type, rtx v0) emit_insn (gen_rtx_SET (Pmode, a0, gen_rtx_LO_SUM (Pmode, pic_offset_table_rtx, loc))); - tga = gen_rtx_MEM (Pmode, mips_tls_symbol); - insn = emit_call_insn (gen_call_value (v0, tga, const0_rtx, const0_rtx)); + insn = mips_expand_call (v0, mips_tls_symbol, const0_rtx, const0_rtx, false); CONST_OR_PURE_CALL_P (insn) = 1; use_reg (&CALL_INSN_FUNCTION_USAGE (insn), v0); use_reg (&CALL_INSN_FUNCTION_USAGE (insn), a0); @@ -3433,9 +3457,11 @@ mips_load_call_address (rtx dest, rtx addr, int sibcall_p) function, ARGS_SIZE is the size of the arguments and AUX is the value passed to us by mips_function_arg. SIBCALL_P is true if we are expanding a sibling call, false if we're expanding - a normal call. */ + a normal call. -void + Return the call itself. */ + +rtx mips_expand_call (rtx result, rtx addr, rtx args_size, rtx aux, int sibcall_p) { rtx orig_addr, pattern, insn; @@ -3449,11 +3475,13 @@ mips_expand_call (rtx result, rtx addr, rtx args_size, rtx aux, int sibcall_p) lazy_p = mips_load_call_address (addr, orig_addr, sibcall_p); } - if (TARGET_MIPS16 - && mips16_hard_float - && build_mips16_call_stub (result, addr, args_size, - aux == 0 ? 0 : (int) GET_MODE (aux))) - return; + insn = build_mips16_call_stub (result, addr, args_size, + aux == 0 ? 0 : (int) GET_MODE (aux)); + if (insn) + { + gcc_assert (!sibcall_p && !lazy_p); + return insn; + } if (result == 0) pattern = (sibcall_p @@ -3475,17 +3503,7 @@ mips_expand_call (rtx result, rtx addr, rtx args_size, rtx aux, int sibcall_p) ? gen_sibcall_value_internal (result, addr, args_size) : gen_call_value_internal (result, addr, args_size)); - insn = emit_call_insn (pattern); - - /* Lazy-binding stubs require $gp to be valid on entry. We also pretend - that they use FAKE_CALL_REGNO; see the load_call patterns for - details. */ - if (lazy_p) - { - use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx); - use_reg (&CALL_INSN_FUNCTION_USAGE (insn), - gen_rtx_REG (Pmode, FAKE_CALL_REGNO)); - } + return mips_emit_call_insn (pattern, lazy_p); } @@ -5095,6 +5113,10 @@ override_options (void) else if (ALL_COP_REG_P (regno)) temp = (class == MODE_INT && size <= UNITS_PER_WORD); + + else if (regno == GOT_VERSION_REGNUM) + temp = (mode == SImode); + else temp = 0; @@ -8093,10 +8115,12 @@ static struct mips16_stub *mips16_stubs; RETVAL is the location of the return value, or null if this is a call rather than a call_value. FN is the address of the function and ARG_SIZE is the size of the arguments. FP_CODE - is the code built by function_arg. This function returns a nonzero - value if it builds the call instruction itself. */ + is the code built by function_arg. -int + If a stub was needed, emit the call and return the call insn itself. + Return null otherwise. */ + +rtx build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code) { int fpret; @@ -8110,7 +8134,7 @@ build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code) /* We don't need to do anything if we aren't in mips16 mode, or if we were invoked with the -msoft-float option. */ if (! TARGET_MIPS16 || ! mips16_hard_float) - return 0; + return NULL_RTX; /* Figure out whether the value might come back in a floating point register. */ @@ -8122,13 +8146,13 @@ build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code) arguments and the value will not be returned in a floating point register. */ if (fp_code == 0 && ! fpret) - return 0; + return NULL_RTX; /* We don't need to do anything if this is a call to a special mips16 support function. */ if (GET_CODE (fn) == SYMBOL_REF && strncmp (XSTR (fn, 0), "__mips16_", 9) == 0) - return 0; + return NULL_RTX; /* This code will only work for o32 and o64 abis. The other ABI's require more sophisticated support. */ @@ -8167,7 +8191,7 @@ build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code) insn = gen_call_internal (stub_fn, arg_size); else insn = gen_call_value_internal (retval, stub_fn, arg_size); - insn = emit_call_insn (insn); + insn = mips_emit_call_insn (insn, false); /* Put the register usage information on the CALL. */ CALL_INSN_FUNCTION_USAGE (insn) = @@ -8189,7 +8213,7 @@ build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code) /* Return 1 to tell the caller that we've generated the call insn. */ - return 1; + return insn; } /* We know the function we are going to call. If we have already @@ -8368,7 +8392,7 @@ build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code) insn = gen_call_internal (fn, arg_size); else insn = gen_call_value_internal (retval, fn, arg_size); - insn = emit_call_insn (insn); + insn = mips_emit_call_insn (insn, false); CALL_INSN_FUNCTION_USAGE (insn) = gen_rtx_EXPR_LIST (VOIDmode, @@ -8377,11 +8401,11 @@ build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code) /* Return 1 to tell the caller that we've generated the call insn. */ - return 1; + return insn; } /* Return 0 to let the caller generate the call insn. */ - return 0; + return NULL_RTX; } /* An entry in the mips16 constant pool. VALUE is the pool constant, @@ -9112,7 +9136,7 @@ mips_avoid_hazard (rtx after, rtx insn, int *hilo_delay, rtx *delayed_reg, rtx lo_reg) { rtx pattern, set; - int nops, ninsns, hazard_set; + int nops, ninsns; pattern = PATTERN (insn); @@ -9158,15 +9182,8 @@ mips_avoid_hazard (rtx after, rtx insn, int *hilo_delay, break; case HAZARD_DELAY: - hazard_set = (int) get_attr_hazard_set (insn); - if (hazard_set == 0) - set = single_set (insn); - else - { - gcc_assert (GET_CODE (PATTERN (insn)) == PARALLEL); - set = XVECEXP (PATTERN (insn), 0, hazard_set - 1); - } - gcc_assert (set && GET_CODE (set) == SET); + set = single_set (insn); + gcc_assert (set); *delayed_reg = SET_DEST (set); break; } @@ -11024,14 +11041,21 @@ mips_encode_section_info (tree decl, rtx rtl, int first) } } -/* Implement TARGET_EXTRA_LIVE_ON_ENTRY. PIC_FUNCTION_ADDR_REGNUM is live - on entry to a function when generating -mshared abicalls code. */ +/* Implement TARGET_EXTRA_LIVE_ON_ENTRY. */ static void mips_extra_live_on_entry (bitmap regs) { - if (TARGET_ABICALLS && !TARGET_ABSOLUTE_ABICALLS) - bitmap_set_bit (regs, PIC_FUNCTION_ADDR_REGNUM); + if (TARGET_ABICALLS) + { + /* PIC_FUNCTION_ADDR_REGNUM is live if we need it to set up + the global pointer. */ + if (!TARGET_ABSOLUTE_ABICALLS) + bitmap_set_bit (regs, PIC_FUNCTION_ADDR_REGNUM); + + /* See the comment above load_call for details. */ + bitmap_set_bit (regs, GOT_VERSION_REGNUM); + } } /* SImode values are represented as sign-extended to DImode. */ diff --git a/gcc/config/mips/mips.h b/gcc/config/mips/mips.h index d34cad46006b..e857c1d706df 100644 --- a/gcc/config/mips/mips.h +++ b/gcc/config/mips/mips.h @@ -1175,7 +1175,7 @@ extern const struct mips_rtx_cost_data *mips_cost; - 3 fake registers: - ARG_POINTER_REGNUM - FRAME_POINTER_REGNUM - - FAKE_CALL_REGNO (see the comment above load_callsi for details) + - GOT_VERSION_REGNUM (see the comment above load_call for details) - 3 dummy entries that were used at various times in the past. - 6 DSP accumulator registers (3 hi-lo pairs) for MIPS DSP ASE - 6 DSP control registers */ @@ -1255,7 +1255,7 @@ extern const struct mips_rtx_cost_data *mips_cost; 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ /* Others. */ \ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, \ /* COP0 registers */ \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ @@ -1966,8 +1966,12 @@ typedef struct mips_args { /* Say that the epilogue uses the return address register. Note that in the case of sibcalls, the values "used by the epilogue" are - considered live at the start of the called function. */ -#define EPILOGUE_USES(REGNO) ((REGNO) == 31) + considered live at the start of the called function. + + If using a GOT, say that the epilogue also uses GOT_VERSION_REGNUM. + See the comment above load_call for details. */ +#define EPILOGUE_USES(REGNO) \ + ((REGNO) == 31 || (TARGET_ABICALLS && (REGNO) == GOT_VERSION_REGNUM)) /* Treat LOC as a byte offset from the stack pointer and round it up to the next fully-aligned offset. */ diff --git a/gcc/config/mips/mips.md b/gcc/config/mips/mips.md index af62a3399ac9..f22043416b4a 100644 --- a/gcc/config/mips/mips.md +++ b/gcc/config/mips/mips.md @@ -29,7 +29,7 @@ (UNSPEC_GET_FNADDR 3) (UNSPEC_BLOCKAGE 4) (UNSPEC_CPRESTORE 5) - (UNSPEC_EH_RECEIVER 6) + (UNSPEC_RESTORE_GP 6) (UNSPEC_EH_RETURN 7) (UNSPEC_CONSTTABLE_INT 8) (UNSPEC_CONSTTABLE_FLOAT 9) @@ -46,10 +46,12 @@ (UNSPEC_MFHILO 26) (UNSPEC_TLS_LDM 27) (UNSPEC_TLS_GET_TP 28) + (UNSPEC_SET_GOT_VERSION 29) + (UNSPEC_UPDATE_GOT_VERSION 30) (UNSPEC_ADDRESS_FIRST 100) - (FAKE_CALL_REGNO 79) + (GOT_VERSION_REGNUM 79) ;; For MIPS Paired-Singled Floating Point Instructions. @@ -222,8 +224,9 @@ ;; frsqrt2 floating point reciprocal square root step2 ;; multi multiword sequence (or user asm statements) ;; nop no operation +;; ghost an instruction that produces no real code (define_attr "type" - "unknown,branch,jump,call,load,fpload,fpidxload,store,fpstore,fpidxstore,prefetch,prefetchx,condmove,xfer,mthilo,mfhilo,const,arith,shift,slt,clz,trap,imul,imul3,imadd,idiv,fmove,fadd,fmul,fmadd,fdiv,frdiv,frdiv1,frdiv2,fabs,fneg,fcmp,fcvt,fsqrt,frsqrt,frsqrt1,frsqrt2,multi,nop" + "unknown,branch,jump,call,load,fpload,fpidxload,store,fpstore,fpidxstore,prefetch,prefetchx,condmove,xfer,mthilo,mfhilo,const,arith,shift,slt,clz,trap,imul,imul3,imadd,idiv,fmove,fadd,fmul,fmadd,fdiv,frdiv,frdiv1,frdiv2,fabs,fneg,fcmp,fcvt,fsqrt,frsqrt,frsqrt1,frsqrt2,multi,nop,ghost" (cond [(eq_attr "jal" "!unset") (const_string "call") (eq_attr "got" "load") (const_string "load")] (const_string "unknown"))) @@ -370,17 +373,6 @@ (const_string "hilo")] (const_string "none"))) -;; Indicates which SET in an instruction pattern induces a hazard. -;; Only meaningful when "hazard" is not "none". SINGLE means that -;; the pattern has only one set while the other values are indexes -;; into a PARALLEL vector. -;; -;; Hazardous instructions with multiple sets should generally put the -;; hazardous set first. The only purpose of this attribute is to force -;; each multi-set pattern to explicitly assert that this condition holds. -(define_attr "hazard_set" "single,0" - (const_string "single")) - ;; Is it a single instruction? (define_attr "single_insn" "no,yes" (symbol_ref "get_attr_length (insn) == (TARGET_MIPS16 ? 2 : 4)")) @@ -585,6 +577,12 @@ (define_cpu_unit "alu" "alu") (define_cpu_unit "imuldiv" "imuldiv") +;; Ghost instructions produce no real code and introduce no hazards. +;; They exist purely to express an effect on dataflow. +(define_insn_reservation "ghost" 0 + (eq_attr "type" "ghost") + "nothing") + (include "4k.md") (include "5k.md") (include "24k.md") @@ -5029,9 +5027,33 @@ DONE; }) -(define_insn_and_split "exception_receiver" +(define_expand "exception_receiver" + [(const_int 0)] + "TARGET_ABICALLS" +{ + /* See the comment above load_call for details. */ + emit_insn (gen_set_got_version ()); + + /* If we have a call-clobbered $gp, restore it from its save slot. */ + if (HAVE_restore_gp) + emit_insn (gen_restore_gp ()); + DONE; +}) + +(define_expand "nonlocal_goto_receiver" + [(const_int 0)] + "TARGET_ABICALLS" +{ + /* See the comment above load_call for details. */ + emit_insn (gen_set_got_version ()); + DONE; +}) + +;; Restore $gp from its .cprestore stack slot. The instruction remains +;; volatile until all uses of $28 are exposed. +(define_insn_and_split "restore_gp" [(set (reg:SI 28) - (unspec_volatile:SI [(const_int 0)] UNSPEC_EH_RECEIVER))] + (unspec_volatile:SI [(const_int 0)] UNSPEC_RESTORE_GP))] "TARGET_ABICALLS && TARGET_OLDABI" "#" "&& reload_completed" @@ -5060,24 +5082,66 @@ ;; potentially modify the GOT entry. And once a stub has been called, ;; we must not call it again. ;; -;; We represent this restriction using an imaginary fixed register that -;; is set by the GOT load and used by the call. By making this register -;; call-clobbered, and by making the GOT load the only way of setting -;; the register, we ensure that the load cannot be moved past a call. +;; We represent this restriction using an imaginary, fixed, call-saved +;; register called GOT_VERSION_REGNUM. The idea is to make the register +;; live throughout the function and to change its value after every +;; potential call site. This stops any rtx value that uses the register +;; from being computed before an earlier call. To do this, we: +;; +;; - Ensure that the register is live on entry to the function, +;; so that it is never thought to be used uninitalized. +;; +;; - Ensure that the register is live on exit from the function, +;; so that it is live throughout. +;; +;; - Make each call (lazily-bound or not) use the current value +;; of GOT_VERSION_REGNUM, so that updates of the register are +;; not moved across call boundaries. +;; +;; - Add "ghost" definitions of the register to the beginning of +;; blocks reached by EH and ABNORMAL_CALL edges, because those +;; edges may involve calls that normal paths don't. (E.g. the +;; unwinding code that handles a non-call exception may change +;; lazily-bound GOT entries.) We do this by making the +;; exception_receiver and nonlocal_goto_receiver expanders emit +;; a set_got_version instruction. +;; +;; - After each call (lazily-bound or not), use a "ghost" +;; update_got_version instruction to change the register's value. +;; This instruction mimics the _possible_ effect of the dynamic +;; resolver during the call and it remains live even if the call +;; itself becomes dead. +;; +;; - Leave GOT_VERSION_REGNUM out of all register classes. +;; The register is therefore not a valid register_operand +;; and cannot be moved to or from other registers. (define_insn "load_call" [(set (match_operand:P 0 "register_operand" "=c") (unspec:P [(match_operand:P 1 "register_operand" "r") - (match_operand:P 2 "immediate_operand" "")] - UNSPEC_LOAD_CALL)) - (set (reg:P FAKE_CALL_REGNO) - (unspec:P [(match_dup 2)] UNSPEC_LOAD_CALL))] + (match_operand:P 2 "immediate_operand" "") + (reg:SI GOT_VERSION_REGNUM)] UNSPEC_LOAD_CALL))] "TARGET_ABICALLS" "\t%0,%R2(%1)" [(set_attr "type" "load") (set_attr "mode" "") - (set_attr "hazard_set" "0") (set_attr "length" "4")]) +(define_insn "set_got_version" + [(set (reg:SI GOT_VERSION_REGNUM) + (unspec_volatile:SI [(const_int 0)] UNSPEC_SET_GOT_VERSION))] + "TARGET_ABICALLS" + "" + [(set_attr "length" "0") + (set_attr "type" "ghost")]) + +(define_insn "update_got_version" + [(set (reg:SI GOT_VERSION_REGNUM) + (unspec:SI [(reg:SI GOT_VERSION_REGNUM)] UNSPEC_UPDATE_GOT_VERSION))] + "TARGET_ABICALLS" + "" + [(set_attr "length" "0") + (set_attr "type" "ghost")]) + ;; Sibling calls. All these patterns use jump instructions. ;; If TARGET_SIBCALLS, call_insn_operand will only accept constant diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 480c223bb9f1..8c0804b7ea85 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,16 @@ +2008-02-02 Richard Sandiford + + PR target/34981 + * gcc.target/mips/lazy-binding-1.c: New test. + * gcc.target/mips/mips.exp (setup_mips_tests): Set mips_abi, + mips_forced_gp, mips_forced_no_abicalls, mips_forced_no_shared + and mips_forced_no_er. + (dg-mips-options): Avoid using -mips16 -mhard-float for ABIs + other than o32 and o64. Avoid using -mabicalls with an implicit + -mabi=eabi. Avoid using small data with -mabicalls. Skip + -mabi=*, -G*, -mabicalls, -mshared and -mexplicit-relocs tests + if the multilib forces the an incompatible option. + 2008-02-01 Release Manager * GCC 4.2.3 released. diff --git a/gcc/testsuite/gcc.target/mips/lazy-binding-1.c b/gcc/testsuite/gcc.target/mips/lazy-binding-1.c new file mode 100644 index 000000000000..eb4f808f7787 --- /dev/null +++ b/gcc/testsuite/gcc.target/mips/lazy-binding-1.c @@ -0,0 +1,20 @@ +/* { dg-mips-options "-mabicalls -mshared -mexplicit-relocs -O2 -fno-delayed-branch" } */ + +void bar (void); + +void +foo (int n) +{ + while (n--) + { + bar (); + bar (); + } +} + +/* There should be exactly five uses of $25: one to set up $gp, two to + load the address of bar (), and two to call it. */ +/* { dg-final { scan-assembler-times "\tl.\t\\\$25,%call16\\\(bar\\\)" 2 } } */ +/* { dg-final { scan-assembler-times "\tjalr\t\\\$25" 2 } } */ +/* { dg-final { scan-assembler "(\\\$28,|\t.cpload\t)\\\$25" } } */ +/* { dg-final { scan-assembler-times "\\\$25" 5 } } */ diff --git a/gcc/testsuite/gcc.target/mips/mips.exp b/gcc/testsuite/gcc.target/mips/mips.exp index 889fac9952ab..f7a9137f9d25 100644 --- a/gcc/testsuite/gcc.target/mips/mips.exp +++ b/gcc/testsuite/gcc.target/mips/mips.exp @@ -33,22 +33,34 @@ load_lib gcc-dg.exp # $mips_mips16: true if MIPS16 mode is selected # $mips_mips64: true if 64-bit output is selected # $mips_float: "hard" or "soft" +# $mips_abi: the ABI specified by _MIPS_SIM # # $mips_forced_isa: true if the command line uses -march=* or -mips* # $mips_forced_abi: true if the command line uses -mabi=* or -mgp* # $mips_forced_float: true if the command line uses -mhard/soft-float # $mips_forced_le true if the command line uses -EL or -mel +# $mips_forced_gp true if the command line forces a particular GP mode +# $mips_forced_no_abicalls +# true if the command line contains -mno-abicalls +# $mips_forced_no_shared +# true if the command line contains -mno-shared +# $mips_forced_no_er true if the command line contains -mno-explicit-relocs proc setup_mips_tests {} { global mips_isa global mips_arch global mips_mips16 global mips_mips64 global mips_float + global mips_abi global mips_forced_isa global mips_forced_abi global mips_forced_float global mips_forced_le + global mips_forced_gp + global mips_forced_no_abicalls + global mips_forced_no_shared + global mips_forced_no_er global compiler_flags global tool @@ -69,6 +81,17 @@ proc setup_mips_tests {} { #else const char *float = "soft"; #endif + #if !defined _MIPS_SIM + const char *abi = "unknown"; + #elif _MIPS_SIM==_ABIO32 + const char *abi = "32"; + #elif _MIPS_SIM==_ABIO64 + const char *abi = "o64"; + #elif _MIPS_SIM==_ABIN32 + const char *abi = "n32"; + #else + const char *abi = "64"; + #endif } close $f set output [${tool}_target_compile $src "" preprocess ""] @@ -79,11 +102,16 @@ proc setup_mips_tests {} { set mips_mips16 [regexp {mips16 = 1} $output] set mips_mips64 [regexp {mips64 = 1} $output] regexp {float = "([^"]*)} $output dummy mips_float + regexp {abi = "([^"]*)} $output dummy mips_abi set mips_forced_isa [regexp -- {(-mips|-march)} $compiler_flags] set mips_forced_abi [regexp -- {(-mgp|-mabi)} $compiler_flags] set mips_forced_float [regexp -- {-m(hard|soft)-float} $compiler_flags] set mips_forced_le [regexp -- {-(EL|mel)[[:>:]]} $compiler_flags] + set mips_forced_gp [regexp -- {-(G|mabicalls)} $compiler_flags] + set mips_forced_no_abicalls [regexp -- {-mno-abicalls} $compiler_flags] + set mips_forced_no_shared [regexp -- {-mno-shared} $compiler_flags] + set mips_forced_no_er [regexp -- {-mno-explicit-relocs} $compiler_flags] } # Return true if command-line option FLAG forces 32-bit code. @@ -110,6 +138,12 @@ proc is_gp32_flag {flag} { # -mno-mips16 # Skip the test for MIPS16 targets. # +# -mabi=* +# Force a particular ABI. Skip the test if the multilib flags +# force a specific ABI or a different register size. If testing +# MIPS16 multilibs, try to force -msoft-float for ABIs other than +# o32 and o64, and skip the test if this is not possible. +# # -march=* # -mips* # Select the target architecture. Skip the test for MIPS16 targets @@ -123,6 +157,20 @@ proc is_gp32_flag {flag} { # -EB # Select big-endian code. Skip the test if the multilib flags # force a little-endian target. +# +# -G* +# Select the small-data mode, and -mno-abcialls. Skip the test if +# the multilib flags already contain such an option, or specify +# something that might be incompatible with them. +# +# -mabicalls +# -mshared +# Select the form of SVR4 PIC. Skip the test if the multilib flags +# conflict with the required setting. +# +# -mexplicit-relocs +# Select explicit relocations. Skip the test if the multilib flags +# force -mno-explicit-relocs. proc dg-mips-options {args} { upvar dg-extra-tool-flags extra_tool_flags upvar dg-do-what do_what @@ -132,16 +180,34 @@ proc dg-mips-options {args} { global mips_mips16 global mips_mips64 global mips_float + global mips_abi global mips_forced_isa global mips_forced_abi global mips_forced_float global mips_forced_le + global mips_forced_gp + global mips_forced_no_abicalls + global mips_forced_no_shared + global mips_forced_no_er set flags [lindex $args 1] set matches 1 - # First handle the -mgp* options. Add an architecture option if necessary. + foreach flag $flags { + if {[regexp -- {^-mabi=(.*)} $flag dummy abi] + && $mips_mips16 + && $abi != "32" + && $abi != "o64"} { + if {[lsearch $flags -mhard-float] >= 0} { + set matches 0 + } else { + append flags " -msoft-float" + } + } + } + + # Handle the -mgp* options. Add an architecture option if necessary. foreach flag $flags { if {[is_gp32_flag $flag] && $mips_mips64} { if {$mips_forced_abi} { @@ -160,12 +226,35 @@ proc dg-mips-options {args} { } } } + + foreach flag $flags { + if {[string match -mabicalls $flag]} { + # EABI has no SVR4-style PIC mode, so if we don't know for + # sure what the default ABI is, try to force an appropriate one. + if {$mips_abi == "unknown" && [lsearch $flags "-mabi=*"] < 0} { + if {!$mips_mips64} { + append flags " -mabi=32" + } else { + append flags " -mabi=n32" + } + } + # Turn off small data, if on by default. Also turn off + # MIPS16 code generation. (We don't support -mabicalls + # with either.) + append flags " -G0 -mno-mips16" + } + } + # Handle the other options. foreach flag $flags { if {$flag == "-mno-mips16"} { if {$mips_mips16} { set matches 0 } + } elseif {[regexp -- {^-mabi=(.*)} $flag dummy abi]} { + if {$abi != $mips_abi && $mips_forced_abi} { + set matches 0 + } } elseif {[regexp -- {^-march=(.*)} $flag dummy arch]} { if {$mips_mips16 || ($arch != $mips_arch && $mips_forced_isa)} { set matches 0 @@ -182,6 +271,25 @@ proc dg-mips-options {args} { if {$mips_forced_le} { set matches 0 } + } elseif {[regexp -- {^-G} $flag]} { + if {$flag != "-G0"} { + append flags " -mno-abicalls" + } + if {$mips_forced_gp} { + set matches 0 + } + } elseif {[regexp -- {^-mabicalls$} $flag]} { + if {$mips_forced_no_abicalls} { + set matches 0 + } + } elseif {[regexp -- {^-mshared$} $flag]} { + if {$mips_forced_no_shared} { + set matches 0 + } + } elseif {[regexp -- {^-mexplicit-relocs$} $flag]} { + if {$mips_forced_no_er} { + set matches 0 + } } } if {$matches} {