diff options
Diffstat (limited to 'gcc/config/rs6000/rs6000.c')
| -rw-r--r-- | gcc/config/rs6000/rs6000.c | 2750 |
1 files changed, 1754 insertions, 996 deletions
diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c index a408d7e5921..e7923587aa4 100644 --- a/gcc/config/rs6000/rs6000.c +++ b/gcc/config/rs6000/rs6000.c @@ -1,6 +1,6 @@ /* Subroutines used for code generation on IBM RS/6000. - Copyright (C) 1991, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000 - Free Software Foundation, Inc. + Copyright (C) 1991, 1993, 1994, 1995, 1996, 1997, 1998, 1999, + 2000 Free Software Foundation, Inc. Contributed by Richard Kenner (kenner@vlsi1.ultra.nyu.edu) This file is part of GNU CC. @@ -40,6 +40,8 @@ Boston, MA 02111-1307, USA. */ #include "output.h" #include "toplev.h" #include "ggc.h" +#include "hashtab.h" +#include "tm_p.h" #ifndef TARGET_NO_PROTOTYPE #define TARGET_NO_PROTOTYPE 0 @@ -78,12 +80,11 @@ static int common_mode_defined; rtx rs6000_compare_op0, rs6000_compare_op1; int rs6000_compare_fp_p; -#ifdef USING_SVR4_H /* Label number of label created for -mrelocatable, to call to so we can get the address of the GOT section */ int rs6000_pic_labelno; -int rs6000_pic_func_labelno; +#ifdef USING_SVR4_H /* Which abi to adhere to */ const char *rs6000_abi_name = RS6000_ABI_NAME; @@ -92,6 +93,9 @@ enum rs6000_sdata_type rs6000_sdata = SDATA_DATA; /* Which small data model to use */ const char *rs6000_sdata_name = (char *)0; + +/* Counter for labels which are to be placed in .fixup. */ +int fixuplabelno = 0; #endif /* Whether a System V.4 varargs area was created. */ @@ -100,11 +104,6 @@ int rs6000_sysv_varargs_p; /* ABI enumeration available for subtarget to use. */ enum rs6000_abi rs6000_current_abi; -/* Offset & size for fpmem stack locations used for converting between - float and integral types. */ -int rs6000_fpmem_offset; -int rs6000_fpmem_size; - /* Debug flags */ const char *rs6000_debug_name; int rs6000_debug_stack; /* debug stack applications */ @@ -112,9 +111,23 @@ int rs6000_debug_arg; /* debug argument handling */ /* Flag to say the TOC is initialized */ int toc_initialized; +char toc_label_name[10]; -static void rs6000_add_gc_roots PARAMS ((void)); +/* Alias set for saves and restores from the rs6000 stack. */ +static int rs6000_sr_alias_set; +static void rs6000_add_gc_roots PARAMS ((void)); +static int num_insns_constant_wide PARAMS ((HOST_WIDE_INT)); +static rtx expand_block_move_mem PARAMS ((enum machine_mode, rtx, rtx)); +static void rs6000_emit_stack_tie PARAMS ((void)); +static void rs6000_frame_related PARAMS ((rtx, rtx, HOST_WIDE_INT, rtx, rtx)); +static void rs6000_emit_allocate_stack PARAMS ((HOST_WIDE_INT, int)); +static unsigned rs6000_hash_constant PARAMS ((rtx)); +static unsigned toc_hash_function PARAMS ((const void *)); +static int toc_hash_eq PARAMS ((const void *, const void *)); +static int toc_hash_mark_entry PARAMS ((void *, void *)); +static void toc_hash_mark_table PARAMS ((void *)); +static int constant_pool_expr_1 PARAMS ((rtx, int *, int *)); /* Default register names. */ char rs6000_reg_names[][8] = @@ -129,7 +142,7 @@ char rs6000_reg_names[][8] = "24", "25", "26", "27", "28", "29", "30", "31", "mq", "lr", "ctr","ap", "0", "1", "2", "3", "4", "5", "6", "7", - "fpmem" + "xer" }; #ifdef TARGET_REGNAMES @@ -145,7 +158,7 @@ static char alt_reg_names[][8] = "%f24", "%f25", "%f26", "%f27", "%f28", "%f29", "%f30", "%f31", "mq", "lr", "ctr", "ap", "%cr0", "%cr1", "%cr2", "%cr3", "%cr4", "%cr5", "%cr6", "%cr7", - "fpmem" + "xer" }; #endif @@ -374,6 +387,12 @@ rs6000_override_options (default_cpu) /* Register global variables with the garbage collector. */ rs6000_add_gc_roots (); + + /* Allocate an alias set for register saves & restores from stack. */ + rs6000_sr_alias_set = new_alias_set (); + + if (TARGET_TOC) + ASM_GENERATE_INTERNAL_LABEL (toc_label_name, "LCTOC", 1); } void @@ -496,24 +515,17 @@ count_register_operand(op, mode) return 0; } -/* Returns 1 if op is memory location for float/int conversions that masquerades - as a register. */ int -fpmem_operand(op, mode) +xer_operand(op, mode) register rtx op; enum machine_mode mode ATTRIBUTE_UNUSED; { if (GET_CODE (op) != REG) return 0; - if (FPMEM_REGNO_P (REGNO (op))) + if (XER_REGNO_P (REGNO (op))) return 1; -#if 0 - if (REGNO (op) > FIRST_PSEUDO_REGISTER) - return 1; -#endif - return 0; } @@ -560,8 +572,9 @@ gpc_reg_operand (op, mode) { return (register_operand (op, mode) && (GET_CODE (op) != REG - || (REGNO (op) >= 67 && !FPMEM_REGNO_P (REGNO (op))) - || REGNO (op) < 64)); + || (REGNO (op) >= ARG_POINTER_REGNUM + && !XER_REGNO_P (REGNO (op))) + || REGNO (op) < MQ_REGNO)); } /* Returns 1 if OP is either a pseudo-register or a register denoting a @@ -641,6 +654,27 @@ reg_or_cint_operand (op, mode) || gpc_reg_operand (op, mode)); } +/* Return 1 is the operand is either a non-special register or ANY + 32-bit unsigned constant integer. */ + +int +reg_or_u_cint_operand (op, mode) + register rtx op; + enum machine_mode mode; +{ + return (gpc_reg_operand (op, mode) + || (GET_CODE (op) == CONST_INT +#if HOST_BITS_PER_WIDE_INT != 32 + && INTVAL (op) < ((HOST_WIDE_INT) 1 << 32) +#endif + && INTVAL (op) > 0) +#if HOST_BITS_PER_WIDE_INT == 32 + || (GET_CODE (op) == CONST_DOUBLE + && CONST_DOUBLE_HIGH (op) == 0) +#endif + ); +} + /* Return 1 if the operand is an operand that can be loaded via the GOT */ int @@ -685,10 +719,10 @@ num_insns_constant_wide (value) HOST_WIDE_INT low = value & 0xffffffff; HOST_WIDE_INT high = value >> 32; - if (high == 0 && (low & 0x80000000) == 0) + if (high == 0 && (low & 0x80000000u) == 0) return 2; - else if (high == -1 && (low & 0x80000000) != 0) + else if (high == -1 && (low & 0x80000000u) != 0) return 2; else if (! low) @@ -749,10 +783,10 @@ num_insns_constant (op, mode) else { - if (high == 0 && (low & 0x80000000) == 0) + if (high == 0 && (low & 0x80000000u) == 0) return num_insns_constant_wide (low); - else if (high == -1 && (low & 0x80000000) != 0) + else if (high == -1 && (low & 0x80000000u) != 0) return num_insns_constant_wide (low); else if (mask64_operand (op, mode)) @@ -917,10 +951,41 @@ logical_operand (op, mode) { return (gpc_reg_operand (op, mode) || (GET_CODE (op) == CONST_INT +#if HOST_BITS_PER_WIDE_INT != 32 + && INTVAL (op) > 0 + && INTVAL (op) < ((HOST_WIDE_INT) 1 << 32) +#endif && ((INTVAL (op) & GET_MODE_MASK (mode) & (~ (HOST_WIDE_INT) 0xffff)) == 0 || (INTVAL (op) & GET_MODE_MASK (mode) - & (~ (HOST_WIDE_INT) 0xffff0000)) == 0))); + & (~ (unsigned HOST_WIDE_INT) 0xffff0000u)) == 0))); +} + +/* Return 1 if the operand is a non-special register or a 32-bit constant + that can be used as the operand of an OR or XOR insn on the RS/6000. */ + +int +logical_u_operand (op, mode) + register rtx op; + enum machine_mode mode; +{ + return (gpc_reg_operand (op, mode) + || (GET_CODE (op) == CONST_INT + && INTVAL (op) > 0 +#if HOST_BITS_PER_WIDE_INT != 32 + && INTVAL (op) < ((HOST_WIDE_INT) 1 << 32) +#endif + && ((INTVAL (op) & GET_MODE_MASK (mode) + & (~ (HOST_WIDE_INT) 0xffff)) == 0 + || (INTVAL (op) & GET_MODE_MASK (mode) + & (~ (unsigned HOST_WIDE_INT) 0xffff0000u)) == 0)) +#if HOST_BITS_PER_WIDE_INT == 32 + || (GET_CODE (op) == CONST_DOUBLE + && CONST_DOUBLE_HIGH (op) == 0 + && ((CONST_DOUBLE_LOW (op) + & (~ (unsigned HOST_WIDE_INT) 0xffff0000u)) == 0)) +#endif + ); } /* Return 1 if C is a constant that is not a logical operand (as @@ -932,10 +997,39 @@ non_logical_cint_operand (op, mode) enum machine_mode mode; { return (GET_CODE (op) == CONST_INT +#if HOST_BITS_PER_WIDE_INT != 32 + && INTVAL (op) < ((HOST_WIDE_INT) 1 << 32) +#endif && (INTVAL (op) & GET_MODE_MASK (mode) & (~ (HOST_WIDE_INT) 0xffff)) != 0 && (INTVAL (op) & GET_MODE_MASK (mode) & - (~ (HOST_WIDE_INT) 0xffff0000)) != 0); + (~ (unsigned HOST_WIDE_INT) 0xffff0000u)) != 0); +} + +/* Return 1 if C is an unsigned 32-bit constant that is not a + logical operand (as above). */ + +int +non_logical_u_cint_operand (op, mode) + register rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return ((GET_CODE (op) == CONST_INT + && INTVAL (op) > 0 +#if HOST_BITS_PER_WIDE_INT != 32 + && INTVAL (op) < ((HOST_WIDE_INT) 1 << 32) +#endif + && (INTVAL (op) & GET_MODE_MASK (mode) + & (~ (HOST_WIDE_INT) 0xffff)) != 0 + && (INTVAL (op) & GET_MODE_MASK (mode) + & (~ (unsigned HOST_WIDE_INT) 0xffff0000u)) != 0) +#if HOST_BITS_PER_WIDE_INT == 32 + || (GET_CODE (op) == CONST_DOUBLE + && CONST_DOUBLE_HIGH (op) == 0 + && (CONST_DOUBLE_LOW (op) & (~ (HOST_WIDE_INT) 0xffff)) != 0 + && (CONST_DOUBLE_LOW (op) + & (~ (unsigned HOST_WIDE_INT) 0xffff0000u)) != 0)); +#endif } /* Return 1 if C is a constant that can be encoded in a 32-bit mask on the @@ -1055,7 +1149,7 @@ and64_operand (op, mode) register rtx op; enum machine_mode mode; { - if (fixed_regs[68]) /* CR0 not available, don't do andi./andis. */ + if (fixed_regs[CR0_REGNO]) /* CR0 not available, don't do andi./andis. */ return (gpc_reg_operand (op, mode) || mask64_operand (op, mode)); return (logical_operand (op, mode) || mask64_operand (op, mode)); @@ -1069,7 +1163,7 @@ and_operand (op, mode) register rtx op; enum machine_mode mode; { - if (fixed_regs[68]) /* CR0 not available, don't do andi./andis. */ + if (fixed_regs[CR0_REGNO]) /* CR0 not available, don't do andi./andis. */ return (gpc_reg_operand (op, mode) || mask_operand (op, mode)); return (logical_operand (op, mode) || mask_operand (op, mode)); @@ -1182,10 +1276,8 @@ input_operand (op, mode) if (LEGITIMATE_CONSTANT_POOL_ADDRESS_P (op)) return 1; - /* Windows NT allows SYMBOL_REFs and LABEL_REFs against the TOC - directly in the instruction stream */ - if (DEFAULT_ABI == ABI_NT - && (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF)) + /* A constant pool expression (relative to the TOC) is valid */ + if (TOC_RELATIVE_EXPR_P (op)) return 1; /* V.4 allows SYMBOL_REFs and CONSTs that are in the small data region @@ -1246,6 +1338,138 @@ small_data_operand (op, mode) return 0; #endif } + +static int +constant_pool_expr_1 (op, have_sym, have_toc) + rtx op; + int *have_sym; + int *have_toc; +{ + switch (GET_CODE(op)) + { + case SYMBOL_REF: + if (CONSTANT_POOL_ADDRESS_P (op)) + { + if (ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (op))) + { + *have_sym = 1; + return 1; + } + else + return 0; + } + else if (! strcmp (XSTR (op, 0), toc_label_name)) + { + *have_toc = 1; + return 1; + } + else + return 0; + case PLUS: + case MINUS: + return constant_pool_expr_1 (XEXP (op, 0), have_sym, have_toc) && + constant_pool_expr_1 (XEXP (op, 1), have_sym, have_toc); + case CONST: + return constant_pool_expr_1 (XEXP (op, 0), have_sym, have_toc); + case CONST_INT: + return 1; + default: + return 0; + } +} + +int +constant_pool_expr_p (op) + rtx op; +{ + int have_sym = 0; + int have_toc = 0; + return constant_pool_expr_1 (op, &have_sym, &have_toc) && have_sym; +} + +int +toc_relative_expr_p (op) + rtx op; +{ + int have_sym = 0; + int have_toc = 0; + return constant_pool_expr_1 (op, &have_sym, &have_toc) && have_toc; +} + +/* Try machine-dependent ways of modifying an illegitimate address + to be legitimate. If we find one, return the new, valid address. + This is used from only one place: `memory_address' in explow.c. + + OLDX is the address as it was before break_out_memory_refs was called. + In some cases it is useful to look at this to decide what needs to be done. + + MODE is passed so that this macro can use GO_IF_LEGITIMATE_ADDRESS. + + It is always safe for this macro to do nothing. It exists to recognize + opportunities to optimize the output. + + On RS/6000, first check for the sum of a register with a constant + integer that is out of range. If so, generate code to add the + constant with the low-order 16 bits masked to the register and force + this result into another register (this can be done with `cau'). + Then generate an address of REG+(CONST&0xffff), allowing for the + possibility of bit 16 being a one. + + Then check for the sum of a register and something not constant, try to + load the other things into a register and return the sum. */ +rtx +rs6000_legitimize_address (x, oldx, mode) + rtx x; + rtx oldx ATTRIBUTE_UNUSED; + enum machine_mode mode; +{ + if (GET_CODE (x) == PLUS + && GET_CODE (XEXP (x, 0)) == REG + && GET_CODE (XEXP (x, 1)) == CONST_INT + && (unsigned HOST_WIDE_INT) (INTVAL (XEXP (x, 1)) + 0x8000) >= 0x10000) + { + HOST_WIDE_INT high_int, low_int; + rtx sum; + high_int = INTVAL (XEXP (x, 1)) & (~ (HOST_WIDE_INT) 0xffff); + low_int = INTVAL (XEXP (x, 1)) & 0xffff; + if (low_int & 0x8000) + high_int += 0x10000, low_int |= ((HOST_WIDE_INT) -1) << 16; + sum = force_operand (gen_rtx_PLUS (Pmode, XEXP (x, 0), + GEN_INT (high_int)), 0); + return gen_rtx_PLUS (Pmode, sum, GEN_INT (low_int)); + } + else if (GET_CODE (x) == PLUS + && GET_CODE (XEXP (x, 0)) == REG + && GET_CODE (XEXP (x, 1)) != CONST_INT + && (TARGET_HARD_FLOAT || TARGET_POWERPC64 || mode != DFmode) + && (TARGET_POWERPC64 || mode != DImode) + && mode != TImode) + { + return gen_rtx_PLUS (Pmode, XEXP (x, 0), + force_reg (Pmode, force_operand (XEXP (x, 1), 0))); + } + else if (TARGET_ELF && TARGET_32BIT && TARGET_NO_TOC && ! flag_pic + && GET_CODE (x) != CONST_INT + && GET_CODE (x) != CONST_DOUBLE + && CONSTANT_P (x) + && (TARGET_HARD_FLOAT || mode != DFmode) + && mode != DImode + && mode != TImode) + { + rtx reg = gen_reg_rtx (Pmode); + emit_insn (gen_elf_high (reg, (x))); + return gen_rtx_LO_SUM (Pmode, reg, (x)); + } + else if (TARGET_TOC + && CONSTANT_POOL_EXPR_P (x) + && ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (x))) + { + return create_TOC_reference (x); + } + else + return NULL_RTX; +} + /* Initialize a variable CUM of type CUMULATIVE_ARGS @@ -1263,7 +1487,6 @@ init_cumulative_args (cum, fntype, libname, incoming) int incoming; { static CUMULATIVE_ARGS zero_cumulative; - enum rs6000_abi abi = DEFAULT_ABI; *cum = zero_cumulative; cum->words = 0; @@ -1285,14 +1508,8 @@ init_cumulative_args (cum, fntype, libname, incoming) cum->orig_nargs = cum->nargs_prototype; - /* Check for DLL import functions */ - if (abi == ABI_NT - && fntype - && lookup_attribute ("dllimport", TYPE_ATTRIBUTES (fntype))) - cum->call_cookie = CALL_NT_DLLIMPORT; - - /* Also check for longcall's */ - else if (fntype && lookup_attribute ("longcall", TYPE_ATTRIBUTES (fntype))) + /* Check for longcall's */ + if (fntype && lookup_attribute ("longcall", TYPE_ATTRIBUTES (fntype))) cum->call_cookie = CALL_LONG; if (TARGET_DEBUG_ARG) @@ -1305,9 +1522,6 @@ init_cumulative_args (cum, fntype, libname, incoming) tree_code_name[ (int)TREE_CODE (ret_type) ]); } - if (cum->call_cookie & CALL_NT_DLLIMPORT) - fprintf (stderr, " dllimport,"); - if (cum->call_cookie & CALL_LONG) fprintf (stderr, " longcall,"); @@ -1325,48 +1539,40 @@ init_cumulative_args (cum, fntype, libname, incoming) For the AIX ABI structs are always stored left shifted in their argument slot. */ -int +enum direction function_arg_padding (mode, type) enum machine_mode mode; tree type; { if (type != 0 && AGGREGATE_TYPE_P (type)) - return (int)upward; + return upward; /* This is the default definition. */ return (! BYTES_BIG_ENDIAN - ? (int)upward + ? upward : ((mode == BLKmode ? (type && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST && int_size_in_bytes (type) < (PARM_BOUNDARY / BITS_PER_UNIT)) : GET_MODE_BITSIZE (mode) < PARM_BOUNDARY) - ? (int)downward : (int)upward)); + ? downward : upward)); } /* If defined, a C expression that gives the alignment boundary, in bits, of an argument with the specified mode and type. If it is not defined, PARM_BOUNDARY is used for all arguments. - Windows NT wants anything >= 8 bytes to be double word aligned. - V.4 wants long longs to be double word aligned. */ int function_arg_boundary (mode, type) enum machine_mode mode; - tree type; + tree type ATTRIBUTE_UNUSED; { if ((DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS) && (mode == DImode || mode == DFmode)) return 64; - - if (DEFAULT_ABI != ABI_NT || TARGET_64BIT) + else return PARM_BOUNDARY; - - if (mode != BLKmode) - return (GET_MODE_SIZE (mode)) >= 8 ? 64 : 32; - - return (int_size_in_bytes (type) >= 8) ? 64 : 32; } /* Update the data in CUM to advance over an argument @@ -1660,7 +1866,6 @@ function_arg_pass_by_reference (cum, mode, type, named) return 0; } - /* Perform any needed actions needed for a function that is receiving a variable number of arguments. @@ -1749,7 +1954,7 @@ setup_incoming_varargs (cum, mode, type, pretend_size, no_rtl) && next_cum.fregno <= FP_ARG_V4_MAX_REG) { int fregno = next_cum.fregno; - rtx cr1 = gen_rtx_REG (CCmode, 69); + rtx cr1 = gen_rtx_REG (CCmode, CR1_REGNO); rtx lab = gen_label_rtx (); int off = (GP_ARG_NUM_REG * reg_size) + ((fregno - FP_ARG_MIN_REG) * 8); @@ -1780,18 +1985,19 @@ tree rs6000_build_va_list () { tree f_gpr, f_fpr, f_ovf, f_sav, record, type_decl; - tree uchar_type_node; - /* Only SVR4 needs something special. */ + /* For AIX, prefer 'char *' because that's what the system + header files like. */ if (DEFAULT_ABI != ABI_V4 && DEFAULT_ABI != ABI_SOLARIS) - return ptr_type_node; + return build_pointer_type (char_type_node); record = make_lang_type (RECORD_TYPE); type_decl = build_decl (TYPE_DECL, get_identifier ("__va_list_tag"), record); - uchar_type_node = make_unsigned_type (CHAR_TYPE_SIZE); - f_gpr = build_decl (FIELD_DECL, get_identifier ("gpr"), uchar_type_node); - f_fpr = build_decl (FIELD_DECL, get_identifier ("fpr"), uchar_type_node); + f_gpr = build_decl (FIELD_DECL, get_identifier ("gpr"), + unsigned_char_type_node); + f_fpr = build_decl (FIELD_DECL, get_identifier ("fpr"), + unsigned_char_type_node); f_ovf = build_decl (FIELD_DECL, get_identifier ("overflow_arg_area"), ptr_type_node); f_sav = build_decl (FIELD_DECL, get_identifier ("reg_save_area"), @@ -1891,9 +2097,48 @@ rs6000_va_arg (valist, type) int indirect_p, size, rsize, n_reg, sav_ofs, sav_scale; rtx lab_false, lab_over, addr_rtx, r; - /* Only SVR4 needs something special. */ + /* For AIX, the rule is that structures are passed left-aligned in + their stack slot. However, GCC does not presently do this: + structures which are the same size as integer types are passed + right-aligned, as if they were in fact integers. This only + matters for structures of size 1 or 2, or 4 when TARGET_64BIT. */ if (DEFAULT_ABI != ABI_V4 && DEFAULT_ABI != ABI_SOLARIS) - return std_expand_builtin_va_arg (valist, type); + { + HOST_WIDE_INT align, rounded_size; + enum machine_mode mode; + tree addr_tree; + + /* Compute the rounded size of the type. */ + align = PARM_BOUNDARY / BITS_PER_UNIT; + rounded_size = (((int_size_in_bytes (type) + align - 1) / align) + * align); + + addr_tree = valist; + + mode = TYPE_MODE (type); + if (mode != BLKmode) + { + HOST_WIDE_INT adj; + adj = TREE_INT_CST_LOW (TYPE_SIZE (type)) / BITS_PER_UNIT; + if (rounded_size > align) + adj = rounded_size; + + addr_tree = build (PLUS_EXPR, TREE_TYPE (addr_tree), addr_tree, + build_int_2 (rounded_size - adj, 0)); + } + + addr_rtx = expand_expr (addr_tree, NULL_RTX, Pmode, EXPAND_NORMAL); + addr_rtx = copy_to_reg (addr_rtx); + + /* Compute new value for AP. */ + t = build (MODIFY_EXPR, TREE_TYPE (valist), valist, + build (PLUS_EXPR, TREE_TYPE (valist), valist, + build_int_2 (rounded_size, 0))); + TREE_SIDE_EFFECTS (t) = 1; + expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); + + return addr_rtx; + } f_gpr = TYPE_FIELDS (TREE_TYPE (va_list_type_node)); f_fpr = TREE_CHAIN (f_gpr); @@ -2431,6 +2676,215 @@ store_multiple_operation (op, mode) return 1; } + +/* Return 1 for an PARALLEL suitable for mtcrf. */ + +int +mtcrf_operation (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + int count = XVECLEN (op, 0); + int i; + int bitmap = 0; + rtx src_reg; + + /* Perform a quick check so we don't blow up below. */ + if (count < 2 + || GET_CODE (XVECEXP (op, 0, 0)) != USE + || GET_CODE (XEXP (XVECEXP (op, 0, 0), 0)) != CONST_INT + || GET_CODE (XVECEXP (op, 0, 1)) != SET + || GET_CODE (SET_SRC (XVECEXP (op, 0, 1))) != UNSPEC + || XVECLEN (SET_SRC (XVECEXP (op, 0, 1)), 0) != 2) + return 0; + src_reg = XVECEXP (SET_SRC (XVECEXP (op, 0, 1)), 0, 0); + + if (GET_CODE (src_reg) != REG + || GET_MODE (src_reg) != SImode + || ! INT_REGNO_P (REGNO (src_reg))) + return 0; + + for (i = 1; i < count; i++) + { + rtx exp = XVECEXP (op, 0, i); + rtx unspec; + int maskval; + + if (GET_CODE (exp) != SET + || GET_CODE (SET_DEST (exp)) != REG + || GET_MODE (SET_DEST (exp)) != CCmode + || ! CR_REGNO_P (REGNO (SET_DEST (exp)))) + return 0; + unspec = SET_SRC (exp); + maskval = 1 << (MAX_CR_REGNO - REGNO (SET_DEST (exp))); + bitmap |= maskval; + + if (GET_CODE (unspec) != UNSPEC + || XINT (unspec, 1) != 20 + || XVECLEN (unspec, 0) != 2 + || XVECEXP (unspec, 0, 0) != src_reg + || GET_CODE (XVECEXP (unspec, 0, 1)) != CONST_INT + || INTVAL (XVECEXP (unspec, 0, 1)) != maskval) + return 0; + } + return INTVAL (XEXP (XVECEXP (op, 0, 0), 0)) == bitmap; +} + +/* Return 1 for an PARALLEL suitable for lmw. */ + +int +lmw_operation (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + int count = XVECLEN (op, 0); + int dest_regno; + rtx src_addr; + int base_regno; + HOST_WIDE_INT offset; + int i; + + /* Perform a quick check so we don't blow up below. */ + if (count <= 1 + || GET_CODE (XVECEXP (op, 0, 0)) != SET + || GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != REG + || GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != MEM) + return 0; + + dest_regno = REGNO (SET_DEST (XVECEXP (op, 0, 0))); + src_addr = XEXP (SET_SRC (XVECEXP (op, 0, 0)), 0); + + if (dest_regno > 31 + || count != 32 - dest_regno) + return 0; + + if (LEGITIMATE_INDIRECT_ADDRESS_P (src_addr)) + { + offset = 0; + base_regno = REGNO (src_addr); + if (base_regno == 0) + return 0; + } + else if (LEGITIMATE_OFFSET_ADDRESS_P (SImode, src_addr)) + { + offset = INTVAL (XEXP (src_addr, 1)); + base_regno = REGNO (XEXP (src_addr, 0)); + } + else + return 0; + + for (i = 0; i < count; i++) + { + rtx elt = XVECEXP (op, 0, i); + rtx newaddr; + rtx addr_reg; + HOST_WIDE_INT newoffset; + + if (GET_CODE (elt) != SET + || GET_CODE (SET_DEST (elt)) != REG + || GET_MODE (SET_DEST (elt)) != SImode + || REGNO (SET_DEST (elt)) != dest_regno + i + || GET_CODE (SET_SRC (elt)) != MEM + || GET_MODE (SET_SRC (elt)) != SImode) + return 0; + newaddr = XEXP (SET_SRC (elt), 0); + if (LEGITIMATE_INDIRECT_ADDRESS_P (newaddr)) + { + newoffset = 0; + addr_reg = newaddr; + } + else if (LEGITIMATE_OFFSET_ADDRESS_P (SImode, newaddr)) + { + addr_reg = XEXP (newaddr, 0); + newoffset = INTVAL (XEXP (newaddr, 1)); + } + else + return 0; + if (REGNO (addr_reg) != base_regno + || newoffset != offset + 4 * i) + return 0; + } + + return 1; +} + +/* Return 1 for an PARALLEL suitable for stmw. */ + +int +stmw_operation (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + int count = XVECLEN (op, 0); + int src_regno; + rtx dest_addr; + int base_regno; + HOST_WIDE_INT offset; + int i; + + /* Perform a quick check so we don't blow up below. */ + if (count <= 1 + || GET_CODE (XVECEXP (op, 0, 0)) != SET + || GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != MEM + || GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != REG) + return 0; + + src_regno = REGNO (SET_SRC (XVECEXP (op, 0, 0))); + dest_addr = XEXP (SET_DEST (XVECEXP (op, 0, 0)), 0); + + if (src_regno > 31 + || count != 32 - src_regno) + return 0; + + if (LEGITIMATE_INDIRECT_ADDRESS_P (dest_addr)) + { + offset = 0; + base_regno = REGNO (dest_addr); + if (base_regno == 0) + return 0; + } + else if (LEGITIMATE_OFFSET_ADDRESS_P (SImode, dest_addr)) + { + offset = INTVAL (XEXP (dest_addr, 1)); + base_regno = REGNO (XEXP (dest_addr, 0)); + } + else + return 0; + + for (i = 0; i < count; i++) + { + rtx elt = XVECEXP (op, 0, i); + rtx newaddr; + rtx addr_reg; + HOST_WIDE_INT newoffset; + + if (GET_CODE (elt) != SET + || GET_CODE (SET_SRC (elt)) != REG + || GET_MODE (SET_SRC (elt)) != SImode + || REGNO (SET_SRC (elt)) != src_regno + i + || GET_CODE (SET_DEST (elt)) != MEM + || GET_MODE (SET_DEST (elt)) != SImode) + return 0; + newaddr = XEXP (SET_DEST (elt), 0); + if (LEGITIMATE_INDIRECT_ADDRESS_P (newaddr)) + { + newoffset = 0; + addr_reg = newaddr; + } + else if (LEGITIMATE_OFFSET_ADDRESS_P (SImode, newaddr)) + { + addr_reg = XEXP (newaddr, 0); + newoffset = INTVAL (XEXP (newaddr, 1)); + } + else + return 0; + if (REGNO (addr_reg) != base_regno + || newoffset != offset + 4 * i) + return 0; + } + + return 1; +} /* Return 1 if OP is a comparison operation that is valid for a branch insn. We only check the opcode against the mode of the CC value here. */ @@ -2691,13 +3145,20 @@ ccr_bit (op, scc_p) enum machine_mode cc_mode; int cc_regnum; int base_bit; + rtx reg; if (GET_RTX_CLASS (code) != '<') return -1; - cc_mode = GET_MODE (XEXP (op, 0)); - cc_regnum = REGNO (XEXP (op, 0)); - base_bit = 4 * (cc_regnum - 68); + reg = XEXP (op, 0); + + if (GET_CODE (reg) != REG + || ! CR_REGNO_P (REGNO (reg))) + abort (); + + cc_mode = GET_MODE (reg); + cc_regnum = REGNO (reg); + base_bit = 4 * (cc_regnum - CR0_REGNO); /* In CCEQmode cases we have made sure that the result is always in the third bit of the CR field. */ @@ -2746,39 +3207,14 @@ rs6000_got_register (value) return pic_offset_table_rtx; } - -/* Search for any occurrence of the GOT_TOC register marker that should - have been eliminated, but may have crept back in. - - This function could completely go away now (June 1999), but we leave it - in for a while until all the possible issues with the new -fpic handling - are resolved. */ - -void -rs6000_reorg (insn) - rtx insn; -{ - if (flag_pic && (DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS)) - { - rtx got_reg = gen_rtx_REG (Pmode, 2); - for ( ; insn != NULL_RTX; insn = NEXT_INSN (insn)) - if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' - && reg_mentioned_p (got_reg, PATTERN (insn))) - fatal_insn ("GOT/TOC register marker not removed:", PATTERN (insn)); - } -} - /* Define the structure for the machine field in struct function. */ struct machine_function { int sysv_varargs_p; - int save_toc_p; - int fpmem_size; - int fpmem_offset; }; -/* Functions to save and restore rs6000_fpmem_size. +/* Functions to save and restore sysv_varargs_p. These will be called, via pointer variables, from push_function_context and pop_function_context. */ @@ -2791,8 +3227,6 @@ rs6000_save_machine_status (p) p->machine = machine; machine->sysv_varargs_p = rs6000_sysv_varargs_p; - machine->fpmem_size = rs6000_fpmem_size; - machine->fpmem_offset = rs6000_fpmem_offset; } void @@ -2802,8 +3236,6 @@ rs6000_restore_machine_status (p) struct machine_function *machine = p->machine; rs6000_sysv_varargs_p = machine->sysv_varargs_p; - rs6000_fpmem_size = machine->fpmem_size; - rs6000_fpmem_offset = machine->fpmem_offset; free (machine); p->machine = (struct machine_function *)0; @@ -2814,10 +3246,8 @@ rs6000_restore_machine_status (p) void rs6000_init_expanders () { - /* Reset varargs and save TOC indicator */ + /* Reset varargs */ rs6000_sysv_varargs_p = 0; - rs6000_fpmem_size = 0; - rs6000_fpmem_offset = 0; /* Arrange to save and restore machine status around nested functions. */ save_machine_status = rs6000_save_machine_status; @@ -2839,7 +3269,7 @@ void print_operand (file, x, code) FILE *file; rtx x; - char code; + int code; { int i; HOST_WIDE_INT val; @@ -2860,11 +3290,6 @@ print_operand (file, x, code) asm_fprintf (file, RS6000_CALL_GLUE); return; - case '*': - /* Write the register number of the TOC register. */ - fputs (TARGET_MINIMAL_TOC ? reg_names[30] : reg_names[2 /* PIC_OFFSET_TABLE_REGNUM? */ ], file); - return; - case '$': /* Write out either a '.' or '$' for the current location, depending on whether this is Solaris or not. */ @@ -2902,7 +3327,7 @@ print_operand (file, x, code) if ((GET_CODE (x) == LE || GET_CODE (x) == GE) && GET_MODE (XEXP (x, 0)) == CCFPmode) { - int base_bit = 4 * (REGNO (XEXP (x, 0)) - 68); + int base_bit = 4 * (REGNO (XEXP (x, 0)) - CR0_REGNO); fprintf (file, "cror %d,%d,%d\n\t", base_bit + 3, base_bit + 2, base_bit + (GET_CODE (x) == GE)); @@ -2916,7 +3341,7 @@ print_operand (file, x, code) if (GET_CODE (x) == LE || GET_CODE (x) == GE || GET_CODE (x) == LEU || GET_CODE (x) == GEU) { - int base_bit = 4 * (REGNO (XEXP (x, 0)) - 68); + int base_bit = 4 * (REGNO (XEXP (x, 0)) - CR0_REGNO); fprintf (file, "cror %d,%d,%d\n\t", base_bit + 3, base_bit + 2, @@ -2925,7 +3350,7 @@ print_operand (file, x, code) else if (GET_CODE (x) == NE) { - int base_bit = 4 * (REGNO (XEXP (x, 0)) - 68); + int base_bit = 4 * (REGNO (XEXP (x, 0)) - CR0_REGNO); fprintf (file, "crnor %d,%d,%d\n\t", base_bit + 3, base_bit + 2, base_bit + 2); @@ -2937,7 +3362,7 @@ print_operand (file, x, code) if (GET_CODE (x) != REG || ! CR_REGNO_P (REGNO (x))) output_operand_lossage ("invalid %%E value"); - fprintf(file, "%d", 4 * (REGNO (x) - 68) + 3); + fprintf(file, "%d", 4 * (REGNO (x) - CR0_REGNO) + 3); return; case 'f': @@ -2946,7 +3371,7 @@ print_operand (file, x, code) if (GET_CODE (x) != REG || ! CR_REGNO_P (REGNO (x))) output_operand_lossage ("invalid %%f value"); else - fprintf (file, "%d", 4 * (REGNO (x) - 68)); + fprintf (file, "%d", 4 * (REGNO (x) - CR0_REGNO)); return; case 'F': @@ -2955,7 +3380,7 @@ print_operand (file, x, code) if (GET_CODE (x) != REG || ! CR_REGNO_P (REGNO (x))) output_operand_lossage ("invalid %%F value"); else - fprintf (file, "%d", 32 - 4 * (REGNO (x) - 68)); + fprintf (file, "%d", 32 - 4 * (REGNO (x) - CR0_REGNO)); return; case 'G': @@ -3022,6 +3447,27 @@ print_operand (file, x, code) fprintf (file, HOST_WIDE_INT_PRINT_DEC, ~ INT_LOWPART (x)); return; + case 'l': + /* X must be a symbolic constant on ELF. Write an + expression suitable for an 'addi' that adds in the low 16 + bits of the MEM. */ + if (GET_CODE (x) != CONST) + { + print_operand_address (file, x); + fputs ("@l", file); + } + else + { + if (GET_CODE (XEXP (x, 0)) != PLUS + || (GET_CODE (XEXP (XEXP (x, 0), 0)) != SYMBOL_REF + && GET_CODE (XEXP (XEXP (x, 0), 0)) != LABEL_REF) + || GET_CODE (XEXP (XEXP (x, 0), 1)) != CONST_INT) + output_operand_lossage ("invalid %%l value"); + print_operand_address (file, XEXP (XEXP (x, 0), 0)); + fputs ("@l", file); + print_operand (file, XEXP (XEXP (x, 0), 1), 0); + } + case 'L': /* Write second word of DImode or DFmode reference. Works on register or non-indexed memory only. */ @@ -3054,15 +3500,15 @@ print_operand (file, x, code) /* If the high bit is set and the low bit is not, the value is zero. If the high bit is zero, the value is the first 1 bit we find from the left. */ - if ((val & 0x80000000) && ((val & 1) == 0)) + if ((val & 0x80000000u) && ((val & 1) == 0)) { putc ('0', file); return; } - else if ((val & 0x80000000) == 0) + else if ((val & 0x80000000u) == 0) { for (i = 1; i < 32; i++) - if ((val <<= 1) & 0x80000000) + if ((val <<= 1) & 0x80000000u) break; fprintf (file, "%d", i); return; @@ -3089,7 +3535,7 @@ print_operand (file, x, code) /* If the low bit is set and the high bit is not, the value is 31. If the low bit is zero, the value is the first 1 bit we find from the right. */ - if ((val & 1) && ((val & 0x80000000) == 0)) + if ((val & 1) && ((val & 0x80000000u) == 0)) { fputs ("31", file); return; @@ -3109,7 +3555,7 @@ print_operand (file, x, code) /* Otherwise, look for the first 0 bit from the left. The result is its number minus 1. We know the high-order bit is one. */ for (i = 0; i < 32; i++) - if (((val <<= 1) & 0x80000000) == 0) + if (((val <<= 1) & 0x80000000u) == 0) break; fprintf (file, "%d", i); @@ -3155,7 +3601,7 @@ print_operand (file, x, code) if (GET_CODE (x) != REG || ! CR_REGNO_P (REGNO (x))) output_operand_lossage ("invalid %%R value"); else - fprintf (file, "%d", 128 >> (REGNO (x) - 68)); + fprintf (file, "%d", 128 >> (REGNO (x) - CR0_REGNO)); return; case 's': @@ -3399,13 +3845,13 @@ print_operand (file, x, code) case ABI_AIX_NODESC: case ABI_SOLARIS: break; - - case ABI_NT: - fputs ("..", file); - break; } } +#if TARGET_AIX RS6000_OUTPUT_BASENAME (file, XSTR (x, 0)); +#else + assemble_name (file, XSTR (x, 0)); +#endif return; case 'Z': @@ -3459,19 +3905,15 @@ print_operand_address (file, x) { if (GET_CODE (x) == REG) fprintf (file, "0(%s)", reg_names[ REGNO (x) ]); - else if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == CONST || GET_CODE (x) == LABEL_REF) + else if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == CONST + || GET_CODE (x) == LABEL_REF) { output_addr_const (file, x); if (small_data_operand (x, GET_MODE (x))) fprintf (file, "@%s(%s)", SMALL_DATA_RELOC, reg_names[SMALL_DATA_REG]); - -#ifdef TARGET_NO_TOC - else if (TARGET_NO_TOC) - ; -#endif - else - fprintf (file, "(%s)", reg_names[ TARGET_MINIMAL_TOC ? 30 : 2 /* PIC_OFFSET_TABLE_REGNUM? */ ]); + else if (TARGET_TOC) + abort(); } else if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == REG) { @@ -3495,6 +3937,28 @@ print_operand_address (file, x) fprintf (file, "@l(%s)", reg_names[ REGNO (XEXP (x, 0)) ]); } #endif + else if (LEGITIMATE_CONSTANT_POOL_ADDRESS_P (x)) + { + if (TARGET_AIX) + { + rtx contains_minus = XEXP (x, 1); + rtx minus; + + /* Find the (minus (sym) (toc)) buried in X, and temporarily + turn it into (sym) for output_addr_const. */ + while (GET_CODE (XEXP (contains_minus, 0)) != MINUS) + contains_minus = XEXP (contains_minus, 0); + + minus = XEXP (contains_minus, 0); + XEXP (contains_minus, 0) = XEXP (minus, 0); + output_addr_const (file, XEXP (x, 1)); + XEXP (contains_minus, 0) = minus; + } + else + output_addr_const (file, XEXP (x, 1)); + + fprintf (file, "(%s)", reg_names[REGNO (XEXP (x, 0))]); + } else abort (); } @@ -3577,27 +4041,6 @@ first_fp_reg_to_save () return first_reg; } - -/* Return non-zero if this function makes calls. */ - -int -rs6000_makes_calls () -{ - rtx insn; - - /* If we are profiling, we will be making a call to __mcount. - Under the System V ABI's, we store the LR directly, so - we don't need to do it here. */ - if (DEFAULT_ABI == ABI_AIX && profile_flag) - return 1; - - for (insn = get_insns (); insn; insn = next_insn (insn)) - if (GET_CODE (insn) == CALL_INSN) - return 1; - - return 0; -} - /* Calculate the stack information for the current function. This is complicated by having two separate calling sequences, the AIX calling @@ -3671,53 +4114,6 @@ rs6000_makes_calls () -mno-eabi libraries can be used with -meabi programs.) - A PowerPC Windows/NT frame looks like: - - SP----> +---------------------------------------+ - | back chain to caller | 0 - +---------------------------------------+ - | reserved | 4 - +---------------------------------------+ - | reserved | 8 - +---------------------------------------+ - | reserved | 12 - +---------------------------------------+ - | reserved | 16 - +---------------------------------------+ - | reserved | 20 - +---------------------------------------+ - | Parameter save area (P) | 24 - +---------------------------------------+ - | Alloca space (A) | 24+P - +---------------------------------------+ - | Local variable space (L) | 24+P+A - +---------------------------------------+ - | Float/int conversion temporary (X) | 24+P+A+L - +---------------------------------------+ - | Save area for FP registers (F) | 24+P+A+L+X - +---------------------------------------+ - | Possible alignment area (Y) | 24+P+A+L+X+F - +---------------------------------------+ - | Save area for GP registers (G) | 24+P+A+L+X+F+Y - +---------------------------------------+ - | Save area for CR (C) | 24+P+A+L+X+F+Y+G - +---------------------------------------+ - | Save area for TOC (T) | 24+P+A+L+X+F+Y+G+C - +---------------------------------------+ - | Save area for LR (R) | 24+P+A+L+X+F+Y+G+C+T - +---------------------------------------+ - old SP->| back chain to caller's caller | - +---------------------------------------+ - - For NT, there is no specific order to save the registers, but in - order to support __builtin_return_address, the save area for the - link register needs to be in a known place, so we use -4 off of the - old SP. To support calls through pointers, we also allocate a - fixed slot to store the TOC, -8 off the old SP. - - The required alignment for NT is 16 bytes. - - The EABI configuration defaults to the V.4 layout, unless -mcall-aix is used, in which case the AIX layout is used. However, the stack alignment requirements may differ. If -mno-eabi is not @@ -3734,7 +4130,7 @@ rs6000_stack_info () { static rs6000_stack_t info, zero_info; rs6000_stack_t *info_ptr = &info; - int reg_size = TARGET_32BIT ? 4 : 8; + int reg_size = TARGET_POWERPC64 ? 8 : 4; enum rs6000_abi abi; int total_raw_size; @@ -3759,54 +4155,10 @@ rs6000_stack_info () info_ptr->fp_size = 8 * (64 - info_ptr->first_fp_reg_save); /* Does this function call anything? */ - info_ptr->calls_p = rs6000_makes_calls (); - - /* Allocate space to save the toc. */ - if (abi == ABI_NT && info_ptr->calls_p) - { - info_ptr->toc_save_p = 1; - info_ptr->toc_size = reg_size; - } - - /* Does this machine need the float/int conversion area? */ - info_ptr->fpmem_p = regs_ever_live[FPMEM_REGNUM]; - - /* If this is main and we need to call a function to set things up, - save main's arguments around the call. */ -#ifdef TARGET_EABI - if (TARGET_EABI) -#endif - { - if (0 == strcmp (IDENTIFIER_POINTER (DECL_NAME (current_function_decl)), - "main") - && DECL_CONTEXT (current_function_decl) == NULL_TREE) - { - info_ptr->main_p = 1; - -#ifdef NAME__MAIN - info_ptr->calls_p = 1; - - if (DECL_ARGUMENTS (current_function_decl)) - { - int i; - tree arg; - - info_ptr->main_save_p = 1; - info_ptr->main_size = 0; - - for ((i = 0), (arg = DECL_ARGUMENTS (current_function_decl)); - arg != NULL_TREE && i < 8; - (arg = TREE_CHAIN (arg)), i++) - { - info_ptr->main_size += reg_size; - } - } -#endif - } - } + info_ptr->calls_p = ! current_function_is_leaf; /* Determine if we need to save the link register */ - if (regs_ever_live[65] + if (regs_ever_live[LINK_REGISTER_REGNUM] || (DEFAULT_ABI == ABI_AIX && profile_flag) #ifdef TARGET_RELOCATABLE || (TARGET_RELOCATABLE && (get_pool_size () != 0)) @@ -3818,16 +4170,16 @@ rs6000_stack_info () || info_ptr->calls_p) { info_ptr->lr_save_p = 1; - regs_ever_live[65] = 1; - if (abi == ABI_NT) - info_ptr->lr_size = reg_size; + regs_ever_live[LINK_REGISTER_REGNUM] = 1; } - /* Determine if we need to save the condition code registers */ - if (regs_ever_live[70] || regs_ever_live[71] || regs_ever_live[72]) + /* Determine if we need to save the condition code registers. */ + if (regs_ever_live[CR2_REGNO] + || regs_ever_live[CR3_REGNO] + || regs_ever_live[CR4_REGNO]) { info_ptr->cr_save_p = 1; - if (abi == ABI_V4 || abi == ABI_NT || abi == ABI_SOLARIS) + if (abi == ABI_V4 || abi == ABI_SOLARIS) info_ptr->cr_size = reg_size; } @@ -3837,13 +4189,11 @@ rs6000_stack_info () info_ptr->varargs_size = RS6000_VARARGS_AREA; info_ptr->vars_size = RS6000_ALIGN (get_frame_size (), 8); info_ptr->parm_size = RS6000_ALIGN (current_function_outgoing_args_size, 8); - info_ptr->fpmem_size = (info_ptr->fpmem_p) ? 8 : 0; info_ptr->save_size = RS6000_ALIGN (info_ptr->fp_size + info_ptr->gp_size + info_ptr->cr_size + info_ptr->lr_size - + info_ptr->toc_size - + info_ptr->main_size, 8); + + info_ptr->toc_size, 8); /* Calculate the offsets */ switch (abi) @@ -3856,7 +4206,6 @@ rs6000_stack_info () case ABI_AIX_NODESC: info_ptr->fp_save_offset = - info_ptr->fp_size; info_ptr->gp_save_offset = info_ptr->fp_save_offset - info_ptr->gp_size; - info_ptr->main_save_offset = info_ptr->gp_save_offset - info_ptr->main_size; info_ptr->cr_save_offset = reg_size; /* first word when 64-bit. */ info_ptr->lr_save_offset = 2*reg_size; break; @@ -3867,31 +4216,12 @@ rs6000_stack_info () info_ptr->gp_save_offset = info_ptr->fp_save_offset - info_ptr->gp_size; info_ptr->cr_save_offset = info_ptr->gp_save_offset - info_ptr->cr_size; info_ptr->toc_save_offset = info_ptr->cr_save_offset - info_ptr->toc_size; - info_ptr->main_save_offset = info_ptr->toc_save_offset - info_ptr->main_size; info_ptr->lr_save_offset = reg_size; break; - - case ABI_NT: - info_ptr->lr_save_offset = -reg_size; - info_ptr->toc_save_offset = info_ptr->lr_save_offset - info_ptr->lr_size; - info_ptr->cr_save_offset = info_ptr->toc_save_offset - info_ptr->toc_size; - info_ptr->gp_save_offset = info_ptr->cr_save_offset - info_ptr->cr_size - info_ptr->gp_size + reg_size; - info_ptr->fp_save_offset = info_ptr->gp_save_offset - info_ptr->fp_size; - if (info_ptr->fp_size && ((- info_ptr->fp_save_offset) % 8) != 0) - info_ptr->fp_save_offset -= reg_size; - - info_ptr->main_save_offset = info_ptr->fp_save_offset - info_ptr->main_size; - break; } - /* Ensure that fpmem_offset will be aligned to an 8-byte boundary. */ - if (info_ptr->fpmem_p - && (info_ptr->main_save_offset - info_ptr->fpmem_size) % 8) - info_ptr->fpmem_size += reg_size; - total_raw_size = (info_ptr->vars_size + info_ptr->parm_size - + info_ptr->fpmem_size + info_ptr->save_size + info_ptr->varargs_size + info_ptr->fixed_size); @@ -3902,7 +4232,7 @@ rs6000_stack_info () For AIX we need to push the stack if a frame pointer is needed (because the stack might be dynamically adjusted), if we are debugging, if we - make calls, or if the sum of fp_save, gp_save, fpmem, and local variables + make calls, or if the sum of fp_save, gp_save, and local variables are more than the space needed to save all non-volatile registers: 32-bit: 18*8 + 19*4 = 220 or 64-bit: 18*8 + 18*8 = 288 (GPR13 reserved). @@ -3912,10 +4242,9 @@ rs6000_stack_info () if (info_ptr->calls_p) info_ptr->push_p = 1; - else if (abi == ABI_V4 || abi == ABI_NT || abi == ABI_SOLARIS) + else if (abi == ABI_V4 || abi == ABI_SOLARIS) info_ptr->push_p = (total_raw_size > info_ptr->fixed_size - || (abi == ABI_NT ? info_ptr->lr_save_p - : info_ptr->calls_p)); + || info_ptr->calls_p); else info_ptr->push_p = (frame_pointer_needed @@ -3923,17 +4252,6 @@ rs6000_stack_info () || ((total_raw_size - info_ptr->fixed_size) > (TARGET_32BIT ? 220 : 288))); - if (info_ptr->fpmem_p) - { - info_ptr->fpmem_offset = info_ptr->main_save_offset - info_ptr->fpmem_size; - rs6000_fpmem_size = info_ptr->fpmem_size; - rs6000_fpmem_offset = (info_ptr->push_p - ? info_ptr->total_size + info_ptr->fpmem_offset - : info_ptr->fpmem_offset); - } - else - info_ptr->fpmem_offset = 0; - /* Zero offsets if we're not saving those registers */ if (info_ptr->fp_size == 0) info_ptr->fp_save_offset = 0; @@ -3950,9 +4268,6 @@ rs6000_stack_info () if (! info_ptr->toc_save_p) info_ptr->toc_save_offset = 0; - if (! info_ptr->main_save_p) - info_ptr->main_save_offset = 0; - return info_ptr; } @@ -3978,7 +4293,6 @@ debug_stack_info (info) case ABI_AIX_NODESC: abi_string = "AIX"; break; case ABI_V4: abi_string = "V.4"; break; case ABI_SOLARIS: abi_string = "Solaris"; break; - case ABI_NT: abi_string = "NT"; break; } fprintf (stderr, "\tABI = %5s\n", abi_string); @@ -4004,15 +4318,6 @@ debug_stack_info (info) if (info->calls_p) fprintf (stderr, "\tcalls_p = %5d\n", info->calls_p); - if (info->main_p) - fprintf (stderr, "\tmain_p = %5d\n", info->main_p); - - if (info->main_save_p) - fprintf (stderr, "\tmain_save_p = %5d\n", info->main_save_p); - - if (info->fpmem_p) - fprintf (stderr, "\tfpmem_p = %5d\n", info->fpmem_p); - if (info->gp_save_offset) fprintf (stderr, "\tgp_save_offset = %5d\n", info->gp_save_offset); @@ -4031,12 +4336,6 @@ debug_stack_info (info) if (info->varargs_save_offset) fprintf (stderr, "\tvarargs_save_offset = %5d\n", info->varargs_save_offset); - if (info->main_save_offset) - fprintf (stderr, "\tmain_save_offset = %5d\n", info->main_save_offset); - - if (info->fpmem_offset) - fprintf (stderr, "\tfpmem_offset = %5d\n", info->fpmem_offset); - if (info->total_size) fprintf (stderr, "\ttotal_size = %5d\n", info->total_size); @@ -4049,9 +4348,6 @@ debug_stack_info (info) if (info->parm_size) fprintf (stderr, "\tparm_size = %5d\n", info->parm_size); - if (info->fpmem_size) - fprintf (stderr, "\tfpmem_size = %5d\n", info->fpmem_size); - if (info->fixed_size) fprintf (stderr, "\tfixed_size = %5d\n", info->fixed_size); @@ -4070,9 +4366,6 @@ debug_stack_info (info) if (info->toc_size) fprintf (stderr, "\ttoc_size = %5d\n", info->toc_size); - if (info->main_size) - fprintf (stderr, "\tmain_size = %5d\n", info->main_size); - if (info->save_size) fprintf (stderr, "\tsave_size = %5d\n", info->save_size); @@ -4082,104 +4375,303 @@ debug_stack_info (info) fprintf (stderr, "\n"); } -/* Write out an instruction to load the TOC_TABLE address into register 30. +/* Emit instructions needed to load the TOC register. This is only needed when TARGET_TOC, TARGET_MINIMAL_TOC, and there is - a constant pool. */ + a constant pool; or for SVR4 -fpic. */ void -rs6000_output_load_toc_table (file, reg) - FILE *file; - int reg; +rs6000_emit_load_toc_table (fromprolog) + int fromprolog; { - char buf[256]; + rtx dest; + dest = gen_rtx_REG (Pmode, PIC_OFFSET_TABLE_REGNUM); -#ifdef USING_SVR4_H - if (TARGET_RELOCATABLE) + if (TARGET_ELF) { - ASM_GENERATE_INTERNAL_LABEL (buf, "LCF", rs6000_pic_labelno); - fprintf (file, "\tbl "); - assemble_name (file, buf); - fprintf (file, "\n"); - - /* possibly create the toc section */ - if (! toc_initialized) + if ((DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS) + && flag_pic == 1) { - toc_section (); - function_section (current_function_decl); + rtx temp = (fromprolog + ? gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM) + : gen_reg_rtx (Pmode)); + if (TARGET_32BIT) + emit_insn (gen_load_toc_v4_pic_si (temp)); + else + emit_insn (gen_load_toc_v4_pic_di (temp)); + emit_move_insn (dest, temp); } + else if (flag_pic == 2) + { + char buf[30]; + rtx tempLR = (fromprolog + ? gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM) + : gen_reg_rtx (Pmode)); + rtx temp0 = (fromprolog + ? gen_rtx_REG (Pmode, 0) + : gen_reg_rtx (Pmode)); + rtx symF; + + /* possibly create the toc section */ + if (! toc_initialized) + { + toc_section (); + function_section (current_function_decl); + } + + if (fromprolog) + { + rtx symL; + + ASM_GENERATE_INTERNAL_LABEL (buf, "LCF", rs6000_pic_labelno); + symF = gen_rtx_SYMBOL_REF (Pmode, ggc_alloc_string (buf, -1)); - /* If not first call in this function, we need to put the - different between .LCTOC1 and the address we get to right - after the bl. It will mess up disassembling the instructions - but that can't be helped. We will later need to bias the - address before loading. */ - if (rs6000_pic_func_labelno != rs6000_pic_labelno) - { - const char *init_ptr = TARGET_32BIT ? ".long" : ".quad"; - const char *buf_ptr; + ASM_GENERATE_INTERNAL_LABEL (buf, "LCL", rs6000_pic_labelno); + symL = gen_rtx_SYMBOL_REF (Pmode, ggc_alloc_string (buf, -1)); - ASM_OUTPUT_INTERNAL_LABEL (file, "LCL", rs6000_pic_labelno); + emit_insn (gen_load_toc_v4_PIC_1 (tempLR, symF)); + emit_move_insn (dest, tempLR); + emit_insn (gen_load_toc_v4_PIC_2 (temp0, dest, symL, symF)); + } + else + { + rtx tocsym; + static int reload_toc_labelno = 0; + + tocsym = gen_rtx_SYMBOL_REF (Pmode, + ggc_alloc_string (toc_label_name, -1)); + ASM_GENERATE_INTERNAL_LABEL (buf, "LCG", reload_toc_labelno++); + symF = gen_rtx_SYMBOL_REF (Pmode, ggc_alloc_string (buf, -1)); + + emit_insn (gen_load_toc_v4_PIC_1b (tempLR, symF, tocsym)); + emit_move_insn (dest, tempLR); + emit_move_insn (temp0, gen_rtx_MEM (Pmode, dest)); + } + emit_insn (gen_addsi3 (dest, temp0, dest)); + } + else if (flag_pic == 0 && TARGET_MINIMAL_TOC) + { + /* This is for AIX code running in non-PIC ELF. */ + char buf[30]; + rtx realsym; ASM_GENERATE_INTERNAL_LABEL (buf, "LCTOC", 1); - STRIP_NAME_ENCODING (buf_ptr, buf); - fprintf (file, "\t%s %s-", init_ptr, buf_ptr); + realsym = gen_rtx_SYMBOL_REF (Pmode, ggc_alloc_string (buf, -1)); + + emit_insn (gen_elf_high (dest, realsym)); + emit_insn (gen_elf_low (dest, dest, realsym)); + } + else + abort(); + } + else + { + if (TARGET_32BIT) + emit_insn (gen_load_toc_aix_si (dest)); + else + emit_insn (gen_load_toc_aix_di (dest)); + } +} - ASM_GENERATE_INTERNAL_LABEL (buf, "LCF", rs6000_pic_labelno); - fprintf (file, "%s\n", buf_ptr); +int +get_TOC_alias_set () +{ + static int set = -1; + if (set == -1) + set = new_alias_set (); + return set; +} + +/* This retuns nonzero if the current function uses the TOC. This is + determined by the presence of (unspec ... 7), which is generated by + the various load_toc_* patterns. */ +int +uses_TOC () +{ + rtx insn; + + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') + { + rtx pat = PATTERN (insn); + int i; + + if (GET_CODE(pat) == PARALLEL) + for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++) + if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == UNSPEC + && XINT (XVECEXP (PATTERN (insn), 0, i), 1) == 7) + return 1; } + return 0; +} - ASM_OUTPUT_INTERNAL_LABEL (file, "LCF", rs6000_pic_labelno); - fprintf (file, "\tmflr %s\n", reg_names[reg]); +rtx +create_TOC_reference(symbol) + rtx symbol; +{ + return gen_rtx_PLUS (Pmode, + gen_rtx_REG (Pmode, TOC_REGISTER), + gen_rtx_CONST (Pmode, + gen_rtx_MINUS (Pmode, symbol, + gen_rtx_SYMBOL_REF (Pmode, + ggc_alloc_string (toc_label_name, -1))))); +} - if (rs6000_pic_func_labelno != rs6000_pic_labelno) - asm_fprintf(file, "\t{cal|la} %s,%d(%s)\n", reg_names[reg], - (TARGET_32BIT ? 4 : 8), reg_names[reg]); +#if TARGET_AIX +/* __throw will restore its own return address to be the same as the + return address of the function that the throw is being made to. + This is unfortunate, because we want to check the original + return address to see if we need to restore the TOC. + So we have to squirrel it away here. + This is used only in compiling __throw and __rethrow. - asm_fprintf (file, (TARGET_32BIT) ? "\t{l|lwz} %s,(" : "\tld %s,(", - reg_names[0]); - ASM_GENERATE_INTERNAL_LABEL (buf, "LCL", rs6000_pic_labelno); - assemble_name (file, buf); - putc ('-', file); - ASM_GENERATE_INTERNAL_LABEL (buf, "LCF", rs6000_pic_labelno); - assemble_name (file, buf); - fprintf (file, ")(%s)\n", reg_names[reg]); - asm_fprintf (file, "\t{cax|add} %s,%s,%s\n", - reg_names[reg], reg_names[0], reg_names[reg]); - rs6000_pic_labelno++; - } - else if (! TARGET_64BIT) - { - ASM_GENERATE_INTERNAL_LABEL (buf, "LCTOC", 1); - asm_fprintf (file, "\t{liu|lis} %s,", reg_names[reg]); - assemble_name (file, buf); - fputs ("@ha\n", file); - asm_fprintf (file, "\t{cal|la} %s,", reg_names[reg]); - assemble_name (file, buf); - asm_fprintf (file, "@l(%s)\n", reg_names[reg]); - } - else - abort (); + Most of this code should be removed by CSE. */ +static rtx insn_after_throw; -#else /* !USING_SVR4_H */ - ASM_GENERATE_INTERNAL_LABEL (buf, "LCTOC", 0); - asm_fprintf (file, TARGET_32BIT ? "\t{l|lwz} %s," : "\tld %s,", - reg_names[reg]); - assemble_name (file, buf); - asm_fprintf (file, "(%s)\n", reg_names[2]); -#endif /* USING_SVR4_H */ +/* This does the saving... */ +void +rs6000_aix_emit_builtin_unwind_init () +{ + rtx mem; + rtx stack_top = gen_reg_rtx (Pmode); + rtx opcode_addr = gen_reg_rtx (Pmode); + + insn_after_throw = gen_reg_rtx (SImode); + + mem = gen_rtx_MEM (Pmode, hard_frame_pointer_rtx); + emit_move_insn (stack_top, mem); + + mem = gen_rtx_MEM (Pmode, + gen_rtx_PLUS (Pmode, stack_top, + GEN_INT (2 * GET_MODE_SIZE (Pmode)))); + emit_move_insn (opcode_addr, mem); + emit_move_insn (insn_after_throw, gen_rtx_MEM (SImode, opcode_addr)); } +/* Emit insns to _restore_ the TOC register, at runtime (specifically in _eh.o). + Only used on AIX. + + The idea is that on AIX, function calls look like this: + bl somefunction-trampoline + lwz r2,20(sp) + + and later, + somefunction-trampoline: + stw r2,20(sp) + ... load function address in the count register ... + bctr + or like this, if the linker determines that this is not a cross-module call + and so the TOC need not be restored: + bl somefunction + nop + or like this, if the compiler could determine that this is not a + cross-module call: + bl somefunction + now, the tricky bit here is that register 2 is saved and restored + by the _linker_, so we can't readily generate debugging information + for it. So we need to go back up the call chain looking at the + insns at return addresses to see which calls saved the TOC register + and so see where it gets restored from. + + Oh, and all this gets done in RTL inside the eh_epilogue pattern, + just before the actual epilogue. + + On the bright side, this incurs no space or time overhead unless an + exception is thrown, except for the extra code in libgcc.a. + + The parameter STACKSIZE is a register containing (at runtime) + the amount to be popped off the stack in addition to the stack frame + of this routine (which will be __throw or __rethrow, and so is + guaranteed to have a stack frame). */ +void +rs6000_emit_eh_toc_restore (stacksize) + rtx stacksize; +{ + rtx top_of_stack; + rtx bottom_of_stack = gen_reg_rtx (Pmode); + rtx tocompare = gen_reg_rtx (SImode); + rtx opcode = gen_reg_rtx (SImode); + rtx opcode_addr = gen_reg_rtx (Pmode); + rtx mem; + rtx loop_start = gen_label_rtx (); + rtx no_toc_restore_needed = gen_label_rtx (); + rtx loop_exit = gen_label_rtx (); + + mem = gen_rtx_MEM (Pmode, hard_frame_pointer_rtx); + MEM_ALIAS_SET (mem) = rs6000_sr_alias_set; + emit_move_insn (bottom_of_stack, mem); + + top_of_stack = expand_binop (Pmode, add_optab, + bottom_of_stack, stacksize, + NULL_RTX, 1, OPTAB_WIDEN); + + emit_move_insn (tocompare, + GEN_INT (trunc_int_for_mode (TARGET_32BIT + ? 0x80410014 + : 0xE8410028, SImode))); + + if (insn_after_throw == NULL_RTX) + abort(); + emit_move_insn (opcode, insn_after_throw); + + emit_note (NULL_PTR, NOTE_INSN_LOOP_BEG); + emit_label (loop_start); + + do_compare_rtx_and_jump (opcode, tocompare, NE, 1, + SImode, NULL_RTX, 0, NULL_RTX, + no_toc_restore_needed); + + mem = gen_rtx_MEM (Pmode, + gen_rtx_PLUS (Pmode, bottom_of_stack, + GEN_INT (5 * GET_MODE_SIZE (Pmode)))); + emit_move_insn (gen_rtx_REG (Pmode, 2), mem); + + emit_label (no_toc_restore_needed); + do_compare_rtx_and_jump (top_of_stack, bottom_of_stack, EQ, 1, + Pmode, NULL_RTX, 0, NULL_RTX, + loop_exit); + + mem = gen_rtx_MEM (Pmode, bottom_of_stack); + MEM_ALIAS_SET (mem) = rs6000_sr_alias_set; + emit_move_insn (bottom_of_stack, mem); + + mem = gen_rtx_MEM (Pmode, + gen_rtx_PLUS (Pmode, bottom_of_stack, + GEN_INT (2 * GET_MODE_SIZE (Pmode)))); + emit_move_insn (opcode_addr, mem); + emit_move_insn (opcode, gen_rtx_MEM (SImode, opcode_addr)); + + emit_note (NULL_PTR, NOTE_INSN_LOOP_CONT); + emit_jump (loop_start); + emit_note (NULL_PTR, NOTE_INSN_LOOP_END); + emit_label (loop_exit); +} +#endif /* TARGET_AIX */ -/* Emit the correct code for allocating stack space. If COPY_R12, make sure a copy - of the old frame is left in r12. */ +/* This ties together stack memory + (MEM with an alias set of rs6000_sr_alias_set) + and the change to the stack pointer. */ +static void +rs6000_emit_stack_tie () +{ + rtx mem; + mem = gen_rtx_MEM (BLKmode, gen_rtx_REG (Pmode, STACK_POINTER_REGNUM)); + MEM_ALIAS_SET (mem) = rs6000_sr_alias_set; + emit_insn (gen_stack_tie (mem)); +} -void -rs6000_allocate_stack_space (file, size, copy_r12) - FILE *file; - int size; +/* Emit the correct code for allocating stack space, as insns. + If COPY_R12, make sure a copy of the old frame is left in r12. + The generated code may use hard register 0 as a temporary. */ + +static void +rs6000_emit_allocate_stack (size, copy_r12) + HOST_WIDE_INT size; int copy_r12; { - int neg_size = -size; + rtx insn; + rtx stack_reg = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM); + rtx tmp_reg = gen_rtx_REG (Pmode, 0); + rtx todec = GEN_INT (-size); if (current_function_limit_stack) { @@ -4187,111 +4679,400 @@ rs6000_allocate_stack_space (file, size, copy_r12) && REGNO (stack_limit_rtx) > 1 && REGNO (stack_limit_rtx) <= 31) { - if (size <= 32767) - asm_fprintf (file, "\t{cal %s,%d(%s)|addi %s,%s,%d}\n", - reg_names[0], reg_names[REGNO (stack_limit_rtx)], - size); - else - { - asm_fprintf (file, "\t{cau|addis} %s,%s,0x%x\n", - reg_names[0], reg_names[REGNO (stack_limit_rtx)], - ((size + 0x8000) >> 16) & 0xffff); - asm_fprintf (file, "\t{ai|addic} %s,%s,%d\n", - reg_names[0], reg_names[0], - (size & 0x7fff) | -(size & 0x8000)); - } - if (TARGET_32BIT) - asm_fprintf (file, "\t{t|tw}llt %s,%s\n", - reg_names[1], reg_names[0]); - else - asm_fprintf (file, "\ttdllt %s,%s\n", reg_names[1], reg_names[0]); + emit_insn (Pmode == SImode + ? gen_addsi3 (tmp_reg, + stack_limit_rtx, + GEN_INT (size)) + : gen_adddi3 (tmp_reg, + stack_limit_rtx, + GEN_INT (size))); + + emit_insn (gen_cond_trap (LTU, stack_reg, tmp_reg, + const0_rtx)); } else if (GET_CODE (stack_limit_rtx) == SYMBOL_REF + && TARGET_32BIT && (DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS)) { - const char * l_name = XSTR (stack_limit_rtx, 0); - const char * stripped_name; - - STRIP_NAME_ENCODING (stripped_name, l_name); - asm_fprintf (file, "\t{liu|lis} %s,%s@ha+%d\n", - reg_names[0], stripped_name, size); - asm_fprintf (file, "\t{ai|addic} %s,%s,%s@l+%d\n", - reg_names[0], reg_names[0], stripped_name, size); - if (TARGET_32BIT) - asm_fprintf (file, "\t{t|tw}llt %s,%s\n", - reg_names[1], reg_names[0]); - else - asm_fprintf (file, "\ttdllt %s,%s\n", reg_names[1], reg_names[0]); + rtx toload = gen_rtx_CONST (VOIDmode, + gen_rtx_PLUS (Pmode, + stack_limit_rtx, + GEN_INT (size))); + + emit_insn (gen_elf_high (tmp_reg, toload)); + emit_insn (gen_elf_low (tmp_reg, tmp_reg, toload)); + emit_insn (gen_cond_trap (LTU, stack_reg, tmp_reg, + const0_rtx)); } else warning ("stack limit expression is not supported"); } + if (copy_r12 || ! TARGET_UPDATE) + emit_move_insn (gen_rtx_REG (Pmode, 12), stack_reg); + if (TARGET_UPDATE) { - if (size < 32767) - asm_fprintf (file, - (TARGET_32BIT) ? "\t{stu|stwu} %s,%d(%s)\n" : "\tstdu %s,%d(%s)\n", - reg_names[1], neg_size, reg_names[1]); - else + if (size > 32767) { - if (copy_r12) - fprintf (file, "\tmr %s,%s\n", reg_names[12], reg_names[1]); - - asm_fprintf (file, "\t{liu|lis} %s,0x%x\n\t{oril|ori} %s,%s,%d\n", - reg_names[0], (neg_size >> 16) & 0xffff, - reg_names[0], reg_names[0], neg_size & 0xffff); - asm_fprintf (file, - (TARGET_32BIT) ? "\t{stux|stwux} %s,%s,%s\n" : "\tstdux %s,%s,%s\n", - reg_names[1], reg_names[1], reg_names[0]); + /* Need a note here so that try_split doesn't get confused. */ + if (get_last_insn() == NULL_RTX) + emit_note (0, NOTE_INSN_DELETED); + insn = emit_move_insn (tmp_reg, todec); + try_split (PATTERN (insn), insn, 0); + todec = tmp_reg; } + + if (Pmode == SImode) + insn = emit_insn (gen_movsi_update (stack_reg, stack_reg, + todec, stack_reg)); + else + insn = emit_insn (gen_movdi_update (stack_reg, stack_reg, + todec, stack_reg)); } else { - fprintf (file, "\tmr %s,%s\n", reg_names[12], reg_names[1]); - if (size < 32767) - asm_fprintf (file, "\t{cal|la} %s,%d(%s)\n", - reg_names[1], neg_size, reg_names[1]); + if (Pmode == SImode) + insn = emit_insn (gen_addsi3 (stack_reg, stack_reg, todec)); else + insn = emit_insn (gen_adddi3 (stack_reg, stack_reg, todec)); + emit_move_insn (gen_rtx_MEM (Pmode, stack_reg), + gen_rtx_REG (Pmode, 12)); + } + + RTX_FRAME_RELATED_P (insn) = 1; + REG_NOTES (insn) = + gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, + gen_rtx_SET (VOIDmode, stack_reg, + gen_rtx_PLUS (Pmode, stack_reg, + GEN_INT (-size))), + REG_NOTES (insn)); +} + +/* Add to 'insn' a note which is PATTERN (INSN) but with REG replaced with + (plus:P (reg 1) VAL), and with REG2 replaced with RREG if REG2 is not + NULL. + It would be nice if dwarf2out_frame_debug_expr could deduce these + equivalences by itself so it wasn't necessary to hold its hand so much. */ + +static void +rs6000_frame_related (insn, reg, val, reg2, rreg) + rtx insn; + rtx reg; + HOST_WIDE_INT val; + rtx reg2; + rtx rreg; +{ + rtx real, temp; + + real = copy_rtx (PATTERN (insn)); + + real = replace_rtx (real, reg, + gen_rtx_PLUS (Pmode, gen_rtx_REG (Pmode, + STACK_POINTER_REGNUM), + GEN_INT (val))); + + /* We expect that 'real' is either a SET or a PARALLEL containing + SETs (and possibly other stuff). In a PARALLEL, all the SETs + are important so they all have to be marked RTX_FRAME_RELATED_P. */ + + if (GET_CODE (real) == SET) + { + rtx set = real; + + temp = simplify_rtx (SET_SRC (set)); + if (temp) + SET_SRC (set) = temp; + temp = simplify_rtx (SET_DEST (set)); + if (temp) + SET_DEST (set) = temp; + if (GET_CODE (SET_DEST (set)) == MEM) { - asm_fprintf (file, "\t{liu|lis} %s,0x%x\n\t{oril|ori} %s,%s,%d\n", - reg_names[0], (neg_size >> 16) & 0xffff, - reg_names[0], reg_names[0], neg_size & 0xffff); - asm_fprintf (file, "\t{cax|add} %s,%s,%s\n", reg_names[1], - reg_names[0], reg_names[1]); + temp = simplify_rtx (XEXP (SET_DEST (set), 0)); + if (temp) + XEXP (SET_DEST (set), 0) = temp; } - - asm_fprintf (file, - (TARGET_32BIT) ? "\t{st|stw} %s,0(%s)\n" : "\tstd %s,0(%s)\n", - reg_names[12], reg_names[1]); } + else if (GET_CODE (real) == PARALLEL) + { + int i; + for (i = 0; i < XVECLEN (real, 0); i++) + if (GET_CODE (XVECEXP (real, 0, i)) == SET) + { + rtx set = XVECEXP (real, 0, i); + + temp = simplify_rtx (SET_SRC (set)); + if (temp) + SET_SRC (set) = temp; + temp = simplify_rtx (SET_DEST (set)); + if (temp) + SET_DEST (set) = temp; + if (GET_CODE (SET_DEST (set)) == MEM) + { + temp = simplify_rtx (XEXP (SET_DEST (set), 0)); + if (temp) + XEXP (SET_DEST (set), 0) = temp; + } + RTX_FRAME_RELATED_P (set) = 1; + } + } + else + abort(); + + if (reg2 != NULL_RTX) + real = replace_rtx (real, reg2, rreg); + + RTX_FRAME_RELATED_P (insn) = 1; + REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, + real, + REG_NOTES (insn)); } - -/* Write function prologue. */ +/* Emit function prologue as insns. */ + void -output_prolog (file, size) - FILE *file; - int size ATTRIBUTE_UNUSED; +rs6000_emit_prologue() { rs6000_stack_t *info = rs6000_stack_info (); - int reg_size = info->reg_size; - const char *store_reg; - const char *load_reg; - int sp_reg = 1; - int sp_offset = 0; + enum machine_mode reg_mode = TARGET_POWERPC64 ? DImode : SImode; + int reg_size = TARGET_POWERPC64 ? 8 : 4; + rtx sp_reg_rtx = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM); + rtx frame_ptr_rtx = gen_rtx_REG (Pmode, 12); + rtx frame_reg_rtx = sp_reg_rtx; + rtx cr_save_rtx = NULL; + rtx insn; + int saving_FPRs_inline; + int using_store_multiple; + HOST_WIDE_INT sp_offset = 0; + + using_store_multiple = (TARGET_MULTIPLE && ! TARGET_POWERPC64 + && info->first_gp_reg_save < 31); + saving_FPRs_inline = (info->first_fp_reg_save == 64 + || FP_SAVE_INLINE (info->first_fp_reg_save)); - if (TARGET_32BIT) + /* For V.4, update stack before we do any saving and set back pointer. */ + if (info->push_p && (DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS)) { - store_reg = "\t{st|stw} %s,%d(%s)\n"; - load_reg = "\t{l|lwz} %s,%d(%s)\n"; + if (info->total_size < 32767) + sp_offset = info->total_size; + else + frame_reg_rtx = frame_ptr_rtx; + rs6000_emit_allocate_stack (info->total_size, + (frame_reg_rtx != sp_reg_rtx + && (info->cr_save_p + || info->lr_save_p + || info->first_fp_reg_save < 64 + || info->first_gp_reg_save < 32 + ))); + if (frame_reg_rtx != sp_reg_rtx) + rs6000_emit_stack_tie (); + } + + /* If we use the link register, get it into r0. */ + if (info->lr_save_p) + emit_move_insn (gen_rtx_REG (Pmode, 0), + gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM)); + + /* If we need to save CR, put it into r12. */ + if (info->cr_save_p && frame_reg_rtx != frame_ptr_rtx) + { + cr_save_rtx = gen_rtx_REG (SImode, 12); + emit_insn (gen_movesi_from_cr (cr_save_rtx)); + } + + /* Do any required saving of fpr's. If only one or two to save, do it + ourself. Otherwise, call function. */ + if (saving_FPRs_inline) + { + int i; + for (i = 0; i < 64 - info->first_fp_reg_save; i++) + if ((regs_ever_live[info->first_fp_reg_save+i] + && ! call_used_regs[info->first_fp_reg_save+i])) + { + rtx addr, reg, mem; + reg = gen_rtx_REG (DFmode, info->first_fp_reg_save + i); + addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, + GEN_INT (info->fp_save_offset + + sp_offset + + 8*i)); + mem = gen_rtx_MEM (DFmode, addr); + MEM_ALIAS_SET (mem) = rs6000_sr_alias_set; + + insn = emit_move_insn (mem, reg); + rs6000_frame_related (insn, frame_ptr_rtx, info->total_size, + NULL_RTX, NULL_RTX); + } + } + else if (info->first_fp_reg_save != 64) + { + int i; + char rname[30]; + char *alloc_rname; + rtvec p; + p = rtvec_alloc (2 + 64 - info->first_fp_reg_save); + + RTVEC_ELT (p, 0) = gen_rtx_CLOBBER (VOIDmode, + gen_rtx_REG (Pmode, + LINK_REGISTER_REGNUM)); + sprintf (rname, "%s%d%s", SAVE_FP_PREFIX, + info->first_fp_reg_save - 32, SAVE_FP_SUFFIX); + alloc_rname = ggc_alloc_string (rname, -1); + RTVEC_ELT (p, 1) = gen_rtx_USE (VOIDmode, + gen_rtx_SYMBOL_REF (Pmode, + alloc_rname)); + for (i = 0; i < 64 - info->first_fp_reg_save; i++) + { + rtx addr, reg, mem; + reg = gen_rtx_REG (DFmode, info->first_fp_reg_save + i); + addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, + GEN_INT (info->fp_save_offset + + sp_offset + 8*i)); + mem = gen_rtx_MEM (DFmode, addr); + MEM_ALIAS_SET (mem) = rs6000_sr_alias_set; + + RTVEC_ELT (p, i + 2) = gen_rtx_SET (VOIDmode, mem, reg); + } + insn = emit_insn (gen_rtx_PARALLEL (VOIDmode, p)); + rs6000_frame_related (insn, frame_ptr_rtx, info->total_size, + NULL_RTX, NULL_RTX); + } + + /* Save GPRs. This is done as a PARALLEL if we are using + the store-multiple instructions. */ + if (using_store_multiple) + { + rtvec p, dwarfp; + int i; + p = rtvec_alloc (32 - info->first_gp_reg_save); + dwarfp = rtvec_alloc (32 - info->first_gp_reg_save); + for (i = 0; i < 32 - info->first_gp_reg_save; i++) + { + rtx addr, reg, mem; + reg = gen_rtx_REG (reg_mode, info->first_gp_reg_save + i); + addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, + GEN_INT (info->gp_save_offset + + sp_offset + + reg_size * i)); + mem = gen_rtx_MEM (reg_mode, addr); + MEM_ALIAS_SET (mem) = rs6000_sr_alias_set; + + RTVEC_ELT (p, i) = gen_rtx_SET (VOIDmode, mem, reg); + } + insn = emit_insn (gen_rtx_PARALLEL (VOIDmode, p)); + rs6000_frame_related (insn, frame_ptr_rtx, info->total_size, + NULL_RTX, NULL_RTX); } else { - store_reg = "\tstd %s,%d(%s)\n"; - load_reg = "\tlld %s,%d(%s)\n"; + int i; + for (i = 0; i < 32 - info->first_gp_reg_save; i++) + if ((regs_ever_live[info->first_gp_reg_save+i] + && ! call_used_regs[info->first_gp_reg_save+i]) + || (i+info->first_gp_reg_save == PIC_OFFSET_TABLE_REGNUM + && (DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS) + && flag_pic == 1)) + { + rtx addr, reg, mem; + reg = gen_rtx_REG (reg_mode, info->first_gp_reg_save + i); + addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, + GEN_INT (info->gp_save_offset + + sp_offset + + reg_size * i)); + mem = gen_rtx_MEM (reg_mode, addr); + MEM_ALIAS_SET (mem) = rs6000_sr_alias_set; + + insn = emit_move_insn (mem, reg); + rs6000_frame_related (insn, frame_ptr_rtx, info->total_size, + NULL_RTX, NULL_RTX); + } + } + + /* Save lr if we used it. */ + if (info->lr_save_p) + { + rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, + GEN_INT (info->lr_save_offset + sp_offset)); + rtx reg = gen_rtx_REG (Pmode, 0); + rtx mem = gen_rtx_MEM (Pmode, addr); + /* This should not be of rs6000_sr_alias_set, because of + __builtin_return_address. */ + + insn = emit_move_insn (mem, reg); + rs6000_frame_related (insn, frame_ptr_rtx, info->total_size, + reg, gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM)); } + /* Save CR if we use any that must be preserved. */ + if (info->cr_save_p) + { + rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, + GEN_INT (info->cr_save_offset + sp_offset)); + rtx mem = gen_rtx_MEM (SImode, addr); + MEM_ALIAS_SET (mem) = rs6000_sr_alias_set; + + /* If r12 was used to hold the original sp, copy cr into r0 now + that it's free. */ + if (REGNO (frame_reg_rtx) == 12) + { + cr_save_rtx = gen_rtx_REG (SImode, 0); + emit_insn (gen_movesi_from_cr (cr_save_rtx)); + } + insn = emit_move_insn (mem, cr_save_rtx); + + /* Now, there's no way that dwarf2out_frame_debug_expr is going + to understand '(unspec:SI [(reg:CC 68) ...] 19)'. But that's + OK. All we have to do is specify that _one_ condition code + register is saved in this stack slot. The thrower's epilogue + will then restore all the call-saved registers. */ + rs6000_frame_related (insn, frame_ptr_rtx, info->total_size, + cr_save_rtx, gen_rtx_REG (SImode, CR0_REGNO)); + } + + /* Update stack and set back pointer unless this is V.4, + for which it was done previously. */ + if (info->push_p && DEFAULT_ABI != ABI_V4 && DEFAULT_ABI != ABI_SOLARIS) + rs6000_emit_allocate_stack (info->total_size, FALSE); + + /* Set frame pointer, if needed. */ + if (frame_pointer_needed) + { + insn = emit_move_insn (gen_rtx_REG (reg_mode, FRAME_POINTER_REGNUM), + sp_reg_rtx); + RTX_FRAME_RELATED_P (insn) = 1; + } + + /* If we are using PIC_OFFSET_TABLE_REGNUM, we need to set it up. */ + if ((TARGET_TOC && TARGET_MINIMAL_TOC && get_pool_size () != 0) + || ((DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS) + && flag_pic == 1 && regs_ever_live[PIC_OFFSET_TABLE_REGNUM])) + { + /* If emit_load_toc_table will use the link register, we need to save + it. We use R11 for this purpose because emit_load_toc_table + can use register 0. This allows us to use a plain 'blr' to return + from the procedure more often. */ + int save_LR_around_toc_setup = (TARGET_ELF && flag_pic != 0 && + ! info->lr_save_p); + if (save_LR_around_toc_setup) + emit_move_insn (gen_rtx_REG (Pmode, 11), + gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM)); + + rs6000_emit_load_toc_table (TRUE); + + if (save_LR_around_toc_setup) + emit_move_insn (gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM), + gen_rtx_REG (Pmode, 11)); + } +} + + +/* Write function prologue. */ +void +output_prolog (file, size) + FILE *file; + int size ATTRIBUTE_UNUSED; +{ + rs6000_stack_t *info = rs6000_stack_info (); + if (TARGET_DEBUG_STACK) debug_stack_info (info); @@ -4322,222 +5103,295 @@ output_prolog (file, size) common_mode_defined = 1; } - /* For V.4, update stack before we do any saving and set back pointer. */ - if (info->push_p && (DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS)) + if (! HAVE_prologue) { - if (info->total_size < 32767) - sp_offset = info->total_size; - else - sp_reg = 12; - rs6000_allocate_stack_space (file, info->total_size, sp_reg == 12); + start_sequence (); + + /* A NOTE_INSN_DELETED is supposed to be at the start + and end of the "toplevel" insn chain. */ + emit_note (0, NOTE_INSN_DELETED); + rs6000_emit_prologue (); + emit_note (0, NOTE_INSN_DELETED); + + if (TARGET_DEBUG_STACK) + debug_rtx_list (get_insns(), 100); + final (get_insns(), file, FALSE, FALSE); + end_sequence (); } - /* If we use the link register, get it into r0. */ - if (info->lr_save_p) - asm_fprintf (file, "\tmflr %s\n", reg_names[0]); + rs6000_pic_labelno++; +} + +/* Emit function epilogue as insns. - /* If we need to save CR, put it into r12. */ - if (info->cr_save_p && sp_reg != 12) - asm_fprintf (file, "\tmfcr %s\n", reg_names[12]); + At present, dwarf2out_frame_debug_expr doesn't understand + register restores, so we don't bother setting RTX_FRAME_RELATED_P + anywhere in the epilogue. Most of the insns below would in any case + need special notes to explain where r11 is in relation to the stack. */ - /* Do any required saving of fpr's. If only one or two to save, do it - ourself. Otherwise, call function. Note that since they are statically - linked, we do not need a nop following them. */ - if (FP_SAVE_INLINE (info->first_fp_reg_save)) +void +rs6000_emit_epilogue(sibcall) + int sibcall; +{ + rs6000_stack_t *info; + int restoring_FPRs_inline; + int using_load_multiple; + int using_mfcr_multiple; + int use_backchain_to_restore_sp; + int sp_offset = 0; + rtx sp_reg_rtx = gen_rtx_REG (Pmode, 1); + rtx frame_reg_rtx = sp_reg_rtx; + enum machine_mode reg_mode = TARGET_POWERPC64 ? DImode : SImode; + int reg_size = TARGET_POWERPC64 ? 8 : 4; + int i; + + info = rs6000_stack_info (); + using_load_multiple = (TARGET_MULTIPLE && ! TARGET_POWERPC64 + && info->first_gp_reg_save < 31); + restoring_FPRs_inline = (sibcall + || info->first_fp_reg_save == 64 + || FP_SAVE_INLINE (info->first_fp_reg_save)); + use_backchain_to_restore_sp = (frame_pointer_needed + || current_function_calls_alloca + || info->total_size > 32767); + using_mfcr_multiple = (rs6000_cpu == PROCESSOR_PPC601 + || rs6000_cpu == PROCESSOR_PPC603 + || rs6000_cpu == PROCESSOR_PPC750 + || optimize_size); + + /* If we have a frame pointer, a call to alloca, or a large stack + frame, restore the old stack pointer using the backchain. Otherwise, + we know what size to update it with. */ + if (use_backchain_to_restore_sp) { - int regno = info->first_fp_reg_save; - int loc = info->fp_save_offset + sp_offset; + /* Under V.4, don't reset the stack pointer until after we're done + loading the saved registers. */ + if (DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS) + frame_reg_rtx = gen_rtx_REG (Pmode, 11); - for ( ; regno < 64; regno++, loc += 8) - asm_fprintf (file, "\tstfd %s,%d(%s)\n", reg_names[regno], loc, reg_names[sp_reg]); + emit_move_insn (frame_reg_rtx, + gen_rtx_MEM (Pmode, sp_reg_rtx)); + } - else if (info->first_fp_reg_save != 64) - asm_fprintf (file, "\tbl %s%d%s\n", SAVE_FP_PREFIX, - info->first_fp_reg_save - 32, SAVE_FP_SUFFIX); - - /* Now save gpr's. */ - if (! TARGET_MULTIPLE || info->first_gp_reg_save == 31 || TARGET_64BIT) + else if (info->push_p) { - int regno = info->first_gp_reg_save; - int loc = info->gp_save_offset + sp_offset; - - for ( ; regno < 32; regno++, loc += reg_size) - asm_fprintf (file, store_reg, reg_names[regno], loc, reg_names[sp_reg]); + if (DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS) + sp_offset = info->total_size; + else + { + emit_insn (TARGET_32BIT + ? gen_addsi3 (sp_reg_rtx, sp_reg_rtx, + GEN_INT (info->total_size)) + : gen_adddi3 (sp_reg_rtx, sp_reg_rtx, + GEN_INT (info->total_size))); + } } - - else if (info->first_gp_reg_save != 32) - asm_fprintf (file, "\t{stm|stmw} %s,%d(%s)\n", - reg_names[info->first_gp_reg_save], - info->gp_save_offset + sp_offset, - reg_names[sp_reg]); - - /* Save main's arguments if we need to call a function */ -#ifdef NAME__MAIN - if (info->main_save_p) + + /* Get the old lr if we saved it. */ + if (info->lr_save_p) { - int regno; - int loc = info->main_save_offset + sp_offset; - int size = info->main_size; + rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, + GEN_INT (info->lr_save_offset + sp_offset)); + rtx mem = gen_rtx_MEM (Pmode, addr); + MEM_ALIAS_SET (mem) = rs6000_sr_alias_set; - for (regno = 3; size > 0; regno++, loc += reg_size, size -= reg_size) - asm_fprintf (file, store_reg, reg_names[regno], loc, reg_names[sp_reg]); + emit_move_insn (gen_rtx_REG (Pmode, 0), mem); } -#endif + + /* Get the old cr if we saved it. */ + if (info->cr_save_p) + { + rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, + GEN_INT (info->cr_save_offset + sp_offset)); + rtx mem = gen_rtx_MEM (SImode, addr); + MEM_ALIAS_SET (mem) = rs6000_sr_alias_set; - /* Save lr if we used it. */ + emit_move_insn (gen_rtx_REG (SImode, 12), mem); + } + + /* Set LR here to try to overlap restores below. */ if (info->lr_save_p) - asm_fprintf (file, store_reg, reg_names[0], info->lr_save_offset + sp_offset, - reg_names[sp_reg]); - - /* Save CR if we use any that must be preserved. */ - if (info->cr_save_p) + emit_move_insn (gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM), + gen_rtx_REG (Pmode, 0)); + + + /* Restore GPRs. This is done as a PARALLEL if we are using + the load-multiple instructions. */ + if (using_load_multiple) { - if (sp_reg == 12) /* If r12 is used to hold the original sp, copy cr now */ + rtvec p; + p = rtvec_alloc (32 - info->first_gp_reg_save); + for (i = 0; i < 32 - info->first_gp_reg_save; i++) { - asm_fprintf (file, "\tmfcr %s\n", reg_names[0]); - asm_fprintf (file, store_reg, reg_names[0], - info->cr_save_offset + sp_offset, - reg_names[sp_reg]); + rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, + GEN_INT (info->gp_save_offset + + sp_offset + + reg_size * i)); + rtx mem = gen_rtx_MEM (reg_mode, addr); + MEM_ALIAS_SET (mem) = rs6000_sr_alias_set; + + RTVEC_ELT (p, i) = + gen_rtx_SET (VOIDmode, + gen_rtx_REG (reg_mode, info->first_gp_reg_save + i), + mem); } - else - asm_fprintf (file, store_reg, reg_names[12], info->cr_save_offset + sp_offset, - reg_names[sp_reg]); + emit_insn (gen_rtx_PARALLEL (VOIDmode, p)); } + else + for (i = 0; i < 32 - info->first_gp_reg_save; i++) + if ((regs_ever_live[info->first_gp_reg_save+i] + && ! call_used_regs[info->first_gp_reg_save+i]) + || (i+info->first_gp_reg_save == PIC_OFFSET_TABLE_REGNUM + && (DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS) + && flag_pic == 1)) + { + rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, + GEN_INT (info->gp_save_offset + + sp_offset + + reg_size * i)); + rtx mem = gen_rtx_MEM (reg_mode, addr); + MEM_ALIAS_SET (mem) = rs6000_sr_alias_set; + + emit_move_insn (gen_rtx_REG (reg_mode, + info->first_gp_reg_save + i), + mem); + } - /* If we need PIC_OFFSET_TABLE_REGNUM, initialize it now */ - if ((DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS) - && flag_pic == 1 && regs_ever_live[PIC_OFFSET_TABLE_REGNUM]) - { - if (! info->lr_save_p) - asm_fprintf (file, "\tmflr %s\n", reg_names[0]); - - fputs ("\tbl _GLOBAL_OFFSET_TABLE_@local-4\n", file); - asm_fprintf (file, "\tmflr %s\n", reg_names[PIC_OFFSET_TABLE_REGNUM]); - - if (! info->lr_save_p) - asm_fprintf (file, "\tmtlr %s\n", reg_names[0]); - } + /* Restore fpr's if we need to do it without calling a function. */ + if (restoring_FPRs_inline) + for (i = 0; i < 64 - info->first_fp_reg_save; i++) + if ((regs_ever_live[info->first_fp_reg_save+i] + && ! call_used_regs[info->first_fp_reg_save+i])) + { + rtx addr, mem; + addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, + GEN_INT (info->fp_save_offset + + sp_offset + + 8*i)); + mem = gen_rtx_MEM (DFmode, addr); + MEM_ALIAS_SET (mem) = rs6000_sr_alias_set; + + emit_move_insn (gen_rtx_REG (DFmode, + info->first_fp_reg_save + i), + mem); + } - /* NT needs us to probe the stack frame every 4k pages for large frames, so - do it here. */ - if (DEFAULT_ABI == ABI_NT && info->total_size > 4096) + /* If we saved cr, restore it here. Just those that were used. */ + if (info->cr_save_p) { - if (info->total_size < 32768) + rtx r12_rtx = gen_rtx_REG (SImode, 12); + + if (using_mfcr_multiple) { - int probe_offset = 4096; - while (probe_offset < info->total_size) - { - asm_fprintf (file, "\t{l|lwz} %s,%d(%s)\n", reg_names[0], -probe_offset, reg_names[1]); - probe_offset += 4096; - } + rtvec p; + int mask = 0; + int count = 0; + + for (i = 0; i < 8; i++) + if (regs_ever_live[CR0_REGNO+i] && ! call_used_regs[CR0_REGNO+i]) + { + mask |= 1 << (7-i); + count++; + } + if (count == 0) + abort(); + + p = rtvec_alloc (count + 1); + + RTVEC_ELT (p, 0) = gen_rtx_USE (VOIDmode, GEN_INT (mask)); + count = 1; + for (i = 0; i < 8; i++) + if (regs_ever_live[CR0_REGNO+i] && ! call_used_regs[CR0_REGNO+i]) + { + rtvec r = rtvec_alloc (2); + RTVEC_ELT (r, 0) = r12_rtx; + RTVEC_ELT (r, 1) = GEN_INT (1 << (7-i)); + RTVEC_ELT (p, count) = + gen_rtx_SET (VOIDmode, gen_rtx_REG (CCmode, CR0_REGNO+i), + gen_rtx_UNSPEC (CCmode, r, 20)); + count++; + } + emit_insn (gen_rtx_PARALLEL (VOIDmode, p)); } else - { - int probe_iterations = info->total_size / 4096; - static int probe_labelno = 0; - char buf[256]; - - if (probe_iterations < 32768) - asm_fprintf (file, "\tli %s,%d\n", reg_names[12], probe_iterations); - else + for (i = 0; i < 8; i++) + if (regs_ever_live[CR0_REGNO+i] && ! call_used_regs[CR0_REGNO+i]) { - asm_fprintf (file, "\tlis %s,%d\n", reg_names[12], probe_iterations >> 16); - if (probe_iterations & 0xffff) - asm_fprintf (file, "\tori %s,%s,%d\n", reg_names[12], reg_names[12], - probe_iterations & 0xffff); + emit_insn (gen_movsi_to_cr_one (gen_rtx_REG (CCmode, + CR0_REGNO+i), + r12_rtx)); } - asm_fprintf (file, "\tmtctr %s\n", reg_names[12]); - asm_fprintf (file, "\tmr %s,%s\n", reg_names[12], reg_names[1]); - ASM_OUTPUT_INTERNAL_LABEL (file, "LCprobe", probe_labelno); - asm_fprintf (file, "\t{lu|lwzu} %s,-4096(%s)\n", reg_names[0], reg_names[12]); - ASM_GENERATE_INTERNAL_LABEL (buf, "LCprobe", probe_labelno++); - fputs ("\tbdnz ", file); - assemble_name (file, buf); - putc ('\n', file); - } } - /* Update stack and set back pointer unless this is V.4, which was done previously */ - if (info->push_p && DEFAULT_ABI != ABI_V4 && DEFAULT_ABI != ABI_SOLARIS) - rs6000_allocate_stack_space (file, info->total_size, FALSE); - - /* Set frame pointer, if needed. */ - if (frame_pointer_needed) - asm_fprintf (file, "\tmr %s,%s\n", reg_names[31], reg_names[1]); - -#ifdef NAME__MAIN - /* If we need to call a function to set things up for main, do so now - before dealing with the TOC. */ - if (info->main_p) + /* If this is V.4, unwind the stack pointer after all of the loads + have been done. We need to emit a block here so that sched + doesn't decide to move the sp change before the register restores + (which may not have any obvious dependency on the stack). This + doesn't hurt performance, because there is no scheduling that can + be done after this point. */ + if (DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS) { - const char *prefix = ""; + if (frame_reg_rtx != sp_reg_rtx) + rs6000_emit_stack_tie (); - switch (DEFAULT_ABI) + if (use_backchain_to_restore_sp) { - case ABI_AIX: prefix = "."; break; - case ABI_NT: prefix = ".."; break; + emit_move_insn (sp_reg_rtx, frame_reg_rtx); } - - fprintf (file, "\tbl %s%s\n", prefix, NAME__MAIN); -#ifdef RS6000_CALL_GLUE2 - fprintf (file, "\t%s%s%s\n", RS6000_CALL_GLUE2, prefix, NAME_MAIN); -#else -#ifdef RS6000_CALL_GLUE - if (DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_NT) + else if (sp_offset != 0) { - putc('\t', file); - asm_fprintf (file, RS6000_CALL_GLUE); - putc('\n', file); + emit_insn (Pmode == SImode + ? gen_addsi3 (sp_reg_rtx, sp_reg_rtx, + GEN_INT (sp_offset)) + : gen_adddi3 (sp_reg_rtx, sp_reg_rtx, + GEN_INT (sp_offset))); } -#endif -#endif + } - if (info->main_save_p) - { - int regno; - int loc; - int size = info->main_size; + if (!sibcall) + { + rtvec p; + if (! restoring_FPRs_inline) + p = rtvec_alloc (3 + 64 - info->first_fp_reg_save); + else + p = rtvec_alloc (2); - if (info->total_size < 32767) - { - loc = info->total_size + info->main_save_offset; - for (regno = 3; size > 0; regno++, size -= reg_size, loc += reg_size) - asm_fprintf (file, load_reg, reg_names[regno], loc, reg_names[1]); - } - else - { - int neg_size = info->main_save_offset - info->total_size; - loc = 0; - asm_fprintf (file, "\t{liu|lis} %s,0x%x\n\t{oril|ori} %s,%s,%d\n", - reg_names[0], (neg_size >> 16) & 0xffff, - reg_names[0], reg_names[0], neg_size & 0xffff); + RTVEC_ELT (p, 0) = gen_rtx_USE (VOIDmode, + gen_rtx_REG (Pmode, + LINK_REGISTER_REGNUM)); + RTVEC_ELT (p, 1) = gen_rtx_RETURN (VOIDmode); + + /* If we have to restore more than two FP registers, branch to the + restore function. It will return to our caller. */ + if (! restoring_FPRs_inline) + { + int i; + char rname[30]; + char *alloc_rname; - asm_fprintf (file, "\t{sf|subf} %s,%s,%s\n", reg_names[0], reg_names[0], - reg_names[1]); + sprintf (rname, "%s%d%s", RESTORE_FP_PREFIX, + info->first_fp_reg_save - 32, RESTORE_FP_SUFFIX); + alloc_rname = ggc_alloc_string (rname, -1); + RTVEC_ELT (p, 2) = gen_rtx_USE (VOIDmode, + gen_rtx_SYMBOL_REF (Pmode, + alloc_rname)); - for (regno = 3; size > 0; regno++, size -= reg_size, loc += reg_size) - asm_fprintf (file, load_reg, reg_names[regno], loc, reg_names[0]); + for (i = 0; i < 64 - info->first_fp_reg_save; i++) + { + rtx addr, mem; + addr = gen_rtx_PLUS (Pmode, sp_reg_rtx, + GEN_INT (info->fp_save_offset + 8*i)); + mem = gen_rtx_MEM (DFmode, addr); + MEM_ALIAS_SET (mem) = rs6000_sr_alias_set; + + RTVEC_ELT (p, i+3) = + gen_rtx_SET (VOIDmode, + gen_rtx_REG (DFmode, info->first_fp_reg_save + i), + mem); } } - } -#endif - - - /* If TARGET_MINIMAL_TOC, and the constant pool is needed, then load the - TOC_TABLE address into register 30. */ - if (TARGET_TOC && TARGET_MINIMAL_TOC && get_pool_size () != 0) - { -#ifdef USING_SVR4_H - if (! profile_flag) - rs6000_pic_func_labelno = rs6000_pic_labelno; -#endif - rs6000_output_load_toc_table (file, 30); - } - - if (DEFAULT_ABI == ABI_NT) - { - assemble_name (file, XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0)); - fputs (".b:\n", file); + + emit_jump_insn (gen_rtx_PARALLEL (VOIDmode, p)); } } @@ -4549,101 +5403,31 @@ output_epilog (file, size) int size ATTRIBUTE_UNUSED; { rs6000_stack_t *info = rs6000_stack_info (); - const char *load_reg = (TARGET_32BIT) ? "\t{l|lwz} %s,%d(%s)\n" : "\tld %s,%d(%s)\n"; - rtx insn = get_last_insn (); - int sp_reg = 1; - int sp_offset = 0; - /* If the last insn was a BARRIER, we don't have to write anything except - the trace table. */ - if (GET_CODE (insn) == NOTE) - insn = prev_nonnote_insn (insn); - if (insn == 0 || GET_CODE (insn) != BARRIER) + if (! HAVE_epilogue) { - /* If we have a frame pointer, a call to alloca, or a large stack - frame, restore the old stack pointer using the backchain. Otherwise, - we know what size to update it with. */ - if (frame_pointer_needed || current_function_calls_alloca - || info->total_size > 32767) + rtx insn = get_last_insn (); + /* If the last insn was a BARRIER, we don't have to write anything except + the trace table. */ + if (GET_CODE (insn) == NOTE) + insn = prev_nonnote_insn (insn); + if (insn == 0 || GET_CODE (insn) != BARRIER) { - /* Under V.4, don't reset the stack pointer until after we're done - loading the saved registers. */ - if (DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS) - sp_reg = 11; - - asm_fprintf (file, load_reg, reg_names[sp_reg], 0, reg_names[1]); + /* This is slightly ugly, but at least we don't have two + copies of the epilogue-emitting code. */ + start_sequence (); + + /* A NOTE_INSN_DELETED is supposed to be at the start + and end of the "toplevel" insn chain. */ + emit_note (0, NOTE_INSN_DELETED); + rs6000_emit_epilogue (FALSE); + emit_note (0, NOTE_INSN_DELETED); + + if (TARGET_DEBUG_STACK) + debug_rtx_list (get_insns(), 100); + final (get_insns(), file, FALSE, FALSE); + end_sequence (); } - else if (info->push_p) - { - if (DEFAULT_ABI == ABI_V4 || DEFAULT_ABI == ABI_SOLARIS) - sp_offset = info->total_size; - else - asm_fprintf (file, "\t{cal|la} %s,%d(%s)\n", - reg_names[1], info->total_size, reg_names[1]); - } - - /* Get the old lr if we saved it. */ - if (info->lr_save_p) - asm_fprintf (file, load_reg, reg_names[0], info->lr_save_offset + sp_offset, reg_names[sp_reg]); - - /* Get the old cr if we saved it. */ - if (info->cr_save_p) - asm_fprintf (file, load_reg, reg_names[12], info->cr_save_offset + sp_offset, reg_names[sp_reg]); - - /* Set LR here to try to overlap restores below. */ - if (info->lr_save_p) - asm_fprintf (file, "\tmtlr %s\n", reg_names[0]); - - /* Restore gpr's. */ - if (! TARGET_MULTIPLE || info->first_gp_reg_save == 31 || TARGET_64BIT) - { - int regno = info->first_gp_reg_save; - int loc = info->gp_save_offset + sp_offset; - int reg_size = (TARGET_32BIT) ? 4 : 8; - - for ( ; regno < 32; regno++, loc += reg_size) - asm_fprintf (file, load_reg, reg_names[regno], loc, reg_names[sp_reg]); - } - - else if (info->first_gp_reg_save != 32) - asm_fprintf (file, "\t{lm|lmw} %s,%d(%s)\n", - reg_names[info->first_gp_reg_save], - info->gp_save_offset + sp_offset, - reg_names[sp_reg]); - - /* Restore fpr's if we can do it without calling a function. */ - if (FP_SAVE_INLINE (info->first_fp_reg_save)) - { - int regno = info->first_fp_reg_save; - int loc = info->fp_save_offset + sp_offset; - - for ( ; regno < 64; regno++, loc += 8) - asm_fprintf (file, "\tlfd %s,%d(%s)\n", reg_names[regno], loc, reg_names[sp_reg]); - } - - /* If we saved cr, restore it here. Just those of cr2, cr3, and cr4 - that were used. */ - if (info->cr_save_p) - asm_fprintf (file, "\tmtcrf %d,%s\n", - (regs_ever_live[70] != 0) * 0x20 - + (regs_ever_live[71] != 0) * 0x10 - + (regs_ever_live[72] != 0) * 0x8, reg_names[12]); - - /* If this is V.4, unwind the stack pointer after all of the loads - have been done */ - if (sp_offset != 0) - asm_fprintf (file, "\t{cal|la} %s,%d(%s)\n", - reg_names[1], sp_offset, reg_names[1]); - else if (sp_reg != 1) - asm_fprintf (file, "\tmr %s,%s\n", reg_names[1], reg_names[sp_reg]); - - /* If we have to restore more than two FP registers, branch to the - restore function. It will return to our caller. */ - if (info->first_fp_reg_save != 64 && !FP_SAVE_INLINE (info->first_fp_reg_save)) - asm_fprintf (file, "\tb %s%d%s\n", RESTORE_FP_PREFIX, - info->first_fp_reg_save - 32, RESTORE_FP_SUFFIX); - else - asm_fprintf (file, "\t{br|blr}\n"); } /* Output a traceback table here. See /usr/include/sys/debug.h for info @@ -4820,9 +5604,17 @@ output_epilog (file, size) /* Offset from start of code to tb table. */ fputs ("\t.long ", file); ASM_OUTPUT_INTERNAL_LABEL_PREFIX (file, "LT"); +#if TARGET_AIX RS6000_OUTPUT_BASENAME (file, fname); +#else + assemble_name (file, fname); +#endif fputs ("-.", file); +#if TARGET_AIX RS6000_OUTPUT_BASENAME (file, fname); +#else + assemble_name (file, fname); +#endif putc ('\n', file); /* Interrupt handler mask. */ @@ -4846,14 +5638,6 @@ output_epilog (file, size) if (frame_pointer_needed) fputs ("\t.byte 31\n", file); } - - if (DEFAULT_ABI == ABI_NT) - { - RS6000_OUTPUT_BASENAME (file, XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0)); - fputs (".e:\nFE_MOT_RESVD..", file); - RS6000_OUTPUT_BASENAME (file, XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0)); - fputs (":\n", file); - } } /* A C compound statement that outputs the assembler code for a thunk function, @@ -4897,7 +5681,6 @@ output_mi_thunk (file, thunk_fndecl, delta, function) const char *prefix; const char *fname; const char *r0 = reg_names[0]; - const char *sp = reg_names[1]; const char *toc = reg_names[2]; const char *schain = reg_names[11]; const char *r12 = reg_names[12]; @@ -4959,10 +5742,6 @@ output_mi_thunk (file, thunk_fndecl, delta, function) case ABI_SOLARIS: prefix = ""; break; - - case ABI_NT: - prefix = ".."; - break; } /* If the function is compiled in this module, jump to it directly. @@ -4970,7 +5749,7 @@ output_mi_thunk (file, thunk_fndecl, delta, function) fname = XSTR (XEXP (DECL_RTL (function), 0), 0); - if (current_file_function_operand (XEXP (DECL_RTL (function), 0)) + if (current_file_function_operand (XEXP (DECL_RTL (function), 0), VOIDmode) && ! lookup_attribute ("longcall", TYPE_ATTRIBUTES (TREE_TYPE (function)))) { @@ -4985,7 +5764,6 @@ output_mi_thunk (file, thunk_fndecl, delta, function) switch (DEFAULT_ABI) { default: - case ABI_NT: abort (); case ABI_AIX: @@ -5022,71 +5800,192 @@ output_mi_thunk (file, thunk_fndecl, delta, function) asm_fprintf (file, "\tbctr\n"); break; + case ABI_AIX_NODESC: + case ABI_SOLARIS: case ABI_V4: fprintf (file, "\tb %s", prefix); assemble_name (file, fname); if (flag_pic) fputs ("@plt", file); putc ('\n', file); break; - - /* Don't use r11, that contains the static chain, just use r0/r12. */ - case ABI_AIX_NODESC: - case ABI_SOLARIS: - if (flag_pic == 1) - { - fprintf (file, "\tmflr %s\n", r0); - fputs ("\tbl _GLOBAL_OFFSET_TABLE_@local-4\n", file); - asm_fprintf (file, "\tmflr %s\n", r12); - asm_fprintf (file, "\tmtlr %s\n", r0); - asm_fprintf (file, "\t{l|lwz} %s,", r0); - assemble_name (file, fname); - asm_fprintf (file, "@got(%s)\n", r12); - asm_fprintf (file, "\tmtctr %s\n", r0); - asm_fprintf (file, "\tbctr\n"); - } -#if TARGET_ELF - else if (flag_pic > 1 || TARGET_RELOCATABLE) - { - ASM_GENERATE_INTERNAL_LABEL (buf, "Lthunk", labelno); - labelno++; - fprintf (file, "\tmflr %s\n", r0); - asm_fprintf (file, "\t{st|stw} %s,4(%s)\n", r0, sp); - rs6000_pic_func_labelno = rs6000_pic_labelno; - rs6000_output_load_toc_table (file, 12); - asm_fprintf (file, "\t{l|lwz} %s,", r0); - assemble_name (file, buf); - asm_fprintf (file, "(%s)\n", r12); - asm_fprintf (file, "\t{l|lwz} %s,4(%s)\n", r12, sp); - asm_fprintf (file, "\tmtlr %s\n", r12); - asm_fprintf (file, "\tmtctr %s\n", r0); - asm_fprintf (file, "\tbctr\n"); - asm_fprintf (file, "%s\n", MINIMAL_TOC_SECTION_ASM_OP); - assemble_name (file, buf); - fputs (" = .-.LCTOC1\n", file); - fputs ("\t.long ", file); - assemble_name (file, fname); - fputs ("\n\t.previous\n", file); - } -#endif /* TARGET_ELF */ + } + } +} - else - { - asm_fprintf (file, "\t{liu|lis} %s,", r12); - assemble_name (file, fname); - asm_fprintf (file, "@ha\n"); - asm_fprintf (file, "\t{cal|la} %s,", r12); - assemble_name (file, fname); - asm_fprintf (file, "@l(%s)\n", r12); - asm_fprintf (file, "\tmtctr %s\n", r12); - asm_fprintf (file, "\tbctr\n"); - } + +/* A quick summary of the various types of 'constant-pool tables' + under PowerPC: + + Target Flags Name One table per + AIX (none) AIX TOC object file + AIX -mfull-toc AIX TOC object file + AIX -mminimal-toc AIX minimal TOC translation unit + SVR4/EABI (none) SVR4 SDATA object file + SVR4/EABI -fpic SVR4 pic object file + SVR4/EABI -fPIC SVR4 PIC translation unit + SVR4/EABI -mrelocatable EABI TOC function + SVR4/EABI -maix AIX TOC object file + SVR4/EABI -maix -mminimal-toc + AIX minimal TOC translation unit + + Name Reg. Set by entries contains: + made by addrs? fp? sum? + + AIX TOC 2 crt0 as Y option option + AIX minimal TOC 30 prolog gcc Y Y option + SVR4 SDATA 13 crt0 gcc N Y N + SVR4 pic 30 prolog ld Y not yet N + SVR4 PIC 30 prolog gcc Y option option + EABI TOC 30 prolog gcc Y option option + +*/ + +/* Hash table stuff for keeping track of TOC entries. */ + +struct toc_hash_struct +{ + /* `key' will satisfy CONSTANT_P; in fact, it will satisfy + ASM_OUTPUT_SPECIAL_POOL_ENTRY_P. */ + rtx key; + int labelno; +}; + +static htab_t toc_hash_table; + +/* Hash functions for the hash table. */ + +static unsigned +rs6000_hash_constant (k) + rtx k; +{ + unsigned result = GET_CODE (k); + const char *format = GET_RTX_FORMAT (GET_CODE (k)); + int flen = strlen (format); + int fidx; + + if (GET_CODE (k) == LABEL_REF) + return result * 1231 + XINT (XEXP (k, 0), 3); + if (GET_CODE (k) == CONST_DOUBLE) + fidx = 2; + else if (GET_CODE (k) == CODE_LABEL) + fidx = 3; + else + fidx = 0; + + for (; fidx < flen; fidx++) + switch (format[fidx]) + { + case 's': + { + unsigned i, len; + const char *str = XSTR (k, fidx); + len = strlen (str); + result = result * 613 + len; + for (i = 0; i < len; i++) + result = result * 613 + (unsigned) str[i]; break; } + case 'u': + case 'e': + result = result * 1231 + rs6000_hash_constant (XEXP (k, fidx)); + break; + case 'i': + case 'n': + result = result * 613 + (unsigned) XINT (k, fidx); + break; + case 'w': + if (sizeof (unsigned) >= sizeof (HOST_WIDE_INT)) + result = result * 613 + (unsigned) XWINT (k, fidx); + else + { + size_t i; + for (i = 0; i < sizeof(HOST_WIDE_INT)/sizeof(unsigned); i++) + result = result * 613 + (unsigned) (XWINT (k, fidx) + >> CHAR_BIT * i); + } + break; + default: + abort(); + } + return result; +} + +static unsigned +toc_hash_function (hash_entry) + const void * hash_entry; +{ + return rs6000_hash_constant (((const struct toc_hash_struct *) + hash_entry)->key); +} + +/* Compare H1 and H2 for equivalence. */ + +static int +toc_hash_eq (h1, h2) + const void * h1; + const void * h2; +{ + rtx r1 = ((const struct toc_hash_struct *) h1)->key; + rtx r2 = ((const struct toc_hash_struct *) h2)->key; + + /* Gotcha: One of these const_doubles will be in memory. + The other may be on the constant-pool chain. + So rtx_equal_p will think they are different... */ + if (r1 == r2) + return 1; + if (GET_CODE (r1) != GET_CODE (r2) + || GET_MODE (r1) != GET_MODE (r2)) + return 0; + if (GET_CODE (r1) == CONST_DOUBLE) + { + int format_len = strlen (GET_RTX_FORMAT (CONST_DOUBLE)); + int i; + for (i = 2; i < format_len; i++) + if (XWINT (r1, i) != XWINT (r2, i)) + return 0; + + return 1; } + else if (GET_CODE (r1) == LABEL_REF) + return XINT (XEXP (r1, 0), 3) == XINT (XEXP (r2, 0), 3); + else + return rtx_equal_p (r1, r2); +} + +/* Mark the hash table-entry HASH_ENTRY. */ + +static int +toc_hash_mark_entry (hash_slot, unused) + void * hash_slot; + void * unused ATTRIBUTE_UNUSED; +{ + const struct toc_hash_struct * hash_entry = + *(const struct toc_hash_struct **) hash_slot; + rtx r = hash_entry->key; + ggc_set_mark (hash_entry); + /* For CODE_LABELS, we don't want to drag in the whole insn chain... */ + if (GET_CODE (r) == LABEL_REF) + { + ggc_set_mark (r); + ggc_set_mark (XEXP (r, 0)); + } + else + ggc_mark_rtx (r); + return 1; +} + +/* Mark all the elements of the TOC hash-table *HT. */ + +static void +toc_hash_mark_table (vht) + void *vht; +{ + htab_t *ht = vht; + + htab_traverse (*ht, toc_hash_mark_entry, (void *)0); } - /* Output a TOC entry. We derive the entry name from what is being written. */ @@ -5105,8 +6004,38 @@ output_toc (file, x, labelno) if (TARGET_NO_TOC) abort (); - /* if we're going to put a double constant in the TOC, make sure it's - aligned properly when strict alignment is on. */ + /* When the linker won't eliminate them, don't output duplicate + TOC entries (this happens on AIX if there is any kind of TOC, + and on SVR4 under -fPIC or -mrelocatable). + This won't work if we are not garbage collecting, so + we don't do it, sorry. */ + if (TARGET_TOC && ggc_p) + { + struct toc_hash_struct *h; + void * * found; + + h = ggc_alloc (sizeof (*h)); + h->key = x; + h->labelno = labelno; + + found = htab_find_slot (toc_hash_table, h, 1); + if (*found == NULL) + *found = h; + else /* This is indeed a duplicate. + Set this label equal to that label. */ + { + fputs ("\t.set ", file); + ASM_OUTPUT_INTERNAL_LABEL_PREFIX (file, "LC"); + fprintf (file, "%d,", labelno); + ASM_OUTPUT_INTERNAL_LABEL_PREFIX (file, "LC"); + fprintf (file, "%d\n", ((*(const struct toc_hash_struct **) + found)->labelno)); + return; + } + } + + /* If we're going to put a double constant in the TOC, make sure it's + aligned properly when strict alignment is on. */ if (GET_CODE (x) == CONST_DOUBLE && STRICT_ALIGNMENT && GET_MODE (x) == DFmode @@ -5114,16 +6043,7 @@ output_toc (file, x, labelno) ASM_OUTPUT_ALIGN (file, 3); } - - if (TARGET_ELF && TARGET_MINIMAL_TOC) - { - ASM_OUTPUT_INTERNAL_LABEL_PREFIX (file, "LC"); - fprintf (file, "%d = .-", labelno); - ASM_OUTPUT_INTERNAL_LABEL_PREFIX (file, "LCTOC"); - fputs ("1\n", file); - } - else - ASM_OUTPUT_INTERNAL_LABEL (file, "LC", labelno); + ASM_OUTPUT_INTERNAL_LABEL (file, "LC", labelno); /* Handle FP constants specially. Note that if we have a minimal TOC, things we put here aren't actually in the TOC, so we can allow @@ -5143,7 +6063,7 @@ output_toc (file, x, labelno) fprintf (file, "\t.llong 0x%lx%08lx\n", k[0], k[1]); else fprintf (file, "\t.tc FD_%lx_%lx[TC],0x%lx%08lx\n", - k[0], k[1], k[0] & 0xffffffff, k[1] & 0xffffffff); + k[0], k[1], k[0] & 0xffffffffu, k[1] & 0xffffffffu); return; } else @@ -5266,7 +6186,7 @@ output_toc (file, x, labelno) section. */ if (! strncmp ("_vt.", name, 4)) { - RS6000_OUTPUT_BASENAME (file, name); + assemble_name (file, name); if (offset < 0) fprintf (file, "%d", offset); else if (offset > 0) @@ -5344,7 +6264,7 @@ output_ascii (file, p, n) /* Now close the string if we have written one. Then end the line. */ if (to_close) - fprintf (file, to_close); + fputs (to_close, file); } /* Generate a unique section name for FILENAME for a section type @@ -5362,10 +6282,10 @@ output_ascii (file, p, n) void rs6000_gen_section_name (buf, filename, section_desc) char **buf; - char *filename; - char *section_desc; + const char *filename; + const char *section_desc; { - char *q, *after_last_slash, *last_period = 0; + const char *q, *after_last_slash, *last_period = 0; char *p; int len; @@ -5434,24 +6354,20 @@ output_function_profiler (file, labelno) assemble_name (file, buf); asm_fprintf (file, "@got(%s)\n", reg_names[12]); } -#if TARGET_ELF - else if (flag_pic > 1 || TARGET_RELOCATABLE) + else if (flag_pic > 1) { asm_fprintf (file, "\t{st|stw} %s,4(%s)\n", reg_names[0], reg_names[1]); - rs6000_pic_func_labelno = rs6000_pic_labelno; - rs6000_output_load_toc_table (file, 12); - asm_fprintf (file, "\t{l|lwz} %s,", reg_names[12]); - assemble_name (file, buf); - asm_fprintf (file, "X(%s)\n", reg_names[12]); - asm_fprintf (file, "%s\n", MINIMAL_TOC_SECTION_ASM_OP); - assemble_name (file, buf); - fputs ("X = .-.LCTOC1\n", file); - fputs ("\t.long ", file); + /* Now, we need to get the address of the label. */ + fputs ("\tbl 1f\n\t.long ", file); assemble_name (file, buf); - fputs ("\n\t.previous\n", file); + fputs ("-.\n1:", file); + asm_fprintf (file, "\tmflr %s\n", reg_names[11]); + asm_fprintf (file, "\t{l|lwz} %s,0(%s)\n", + reg_names[0], reg_names[11]); + asm_fprintf (file, "\t{cax|add} %s,%s,%s\n", + reg_names[0], reg_names[0], reg_names[11]); } -#endif else { asm_fprintf (file, "\t{liu|lis} %s,", reg_names[12]); @@ -5631,68 +6547,6 @@ int get_issue_rate() } -/* Output assembler code for a block containing the constant parts - of a trampoline, leaving space for the variable parts. - - The trampoline should set the static chain pointer to value placed - into the trampoline and should branch to the specified routine. */ - -void -rs6000_trampoline_template (file) - FILE *file; -{ - const char *sc = reg_names[STATIC_CHAIN_REGNUM]; - const char *r0 = reg_names[0]; - const char *r2 = reg_names[2]; - - switch (DEFAULT_ABI) - { - default: - abort (); - - /* Under AIX, this is not code at all, but merely a data area, - since that is the way all functions are called. The first word is - the address of the function, the second word is the TOC pointer (r2), - and the third word is the static chain value. */ - case ABI_AIX: - break; - - - /* V.4/eabi function pointers are just a single pointer, so we need to - do the full gory code to load up the static chain. */ - case ABI_V4: - case ABI_SOLARIS: - case ABI_AIX_NODESC: - break; - - /* NT function pointers point to a two word area (real address, TOC) - which unfortunately does not include a static chain field. So we - use the function field to point to ..LTRAMP1 and the toc field - to point to the whole table. */ - case ABI_NT: - if (STATIC_CHAIN_REGNUM == 0 - || STATIC_CHAIN_REGNUM == 2 - || TARGET_64BIT - || !TARGET_NEW_MNEMONICS) - abort (); - - fprintf (file, "\t.ualong 0\n"); /* offset 0 */ - fprintf (file, "\t.ualong 0\n"); /* offset 4 */ - fprintf (file, "\t.ualong 0\n"); /* offset 8 */ - fprintf (file, "\t.ualong 0\n"); /* offset 12 */ - fprintf (file, "\t.ualong 0\n"); /* offset 16 */ - fprintf (file, "..LTRAMP1..0:\n"); /* offset 20 */ - fprintf (file, "\tlwz %s,8(%s)\n", r0, r2); /* offset 24 */ - fprintf (file, "\tlwz %s,12(%s)\n", sc, r2); /* offset 28 */ - fprintf (file, "\tmtctr %s\n", r0); /* offset 32 */ - fprintf (file, "\tlwz %s,16(%s)\n", r2, r2); /* offset 36 */ - fprintf (file, "\tbctr\n"); /* offset 40 */ - break; - } - - return; -} - /* Length in units of the trampoline for entering a nested function. */ int @@ -5714,10 +6568,6 @@ rs6000_trampoline_size () case ABI_AIX_NODESC: ret = (TARGET_32BIT) ? 40 : 48; break; - - case ABI_NT: - ret = 20; - break; } return ret; @@ -5771,27 +6621,6 @@ rs6000_initialize_trampoline (addr, fnaddr, cxt) fnaddr, pmode, ctx_reg, pmode); break; - - /* Under NT, update the first word to point to the ..LTRAMP1..0 header, - the second word will point to the whole trampoline, third-fifth words - will then have the real address, static chain, and toc value. */ - case ABI_NT: - { - rtx tramp_reg = gen_reg_rtx (pmode); - rtx fn_reg = gen_reg_rtx (pmode); - rtx toc_reg = gen_reg_rtx (pmode); - - emit_move_insn (tramp_reg, gen_rtx_SYMBOL_REF (pmode, "..LTRAMP1..0")); - addr = force_reg (pmode, addr); - emit_move_insn (fn_reg, MEM_DEREF (fnaddr)); - emit_move_insn (toc_reg, MEM_PLUS (fnaddr, regsize)); - emit_move_insn (MEM_DEREF (addr), tramp_reg); - emit_move_insn (MEM_PLUS (addr, regsize), addr); - emit_move_insn (MEM_PLUS (addr, 2*regsize), fn_reg); - emit_move_insn (MEM_PLUS (addr, 3*regsize), ctx_reg); - emit_move_insn (MEM_PLUS (addr, 4*regsize), gen_rtx_REG (pmode, 2)); - } - break; } return; @@ -5833,51 +6662,6 @@ rs6000_valid_type_attribute_p (type, attributes, identifier, args) if (is_attribute_p ("longcall", identifier)) return (args == NULL_TREE); - if (DEFAULT_ABI == ABI_NT) - { - /* Stdcall attribute says callee is responsible for popping arguments - if they are not variable. */ - if (is_attribute_p ("stdcall", identifier)) - return (args == NULL_TREE); - - /* Cdecl attribute says the callee is a normal C declaration */ - if (is_attribute_p ("cdecl", identifier)) - return (args == NULL_TREE); - - /* Dllimport attribute says the caller is to call the function - indirectly through a __imp_<name> pointer. */ - if (is_attribute_p ("dllimport", identifier)) - return (args == NULL_TREE); - - /* Dllexport attribute says the callee is to create a __imp_<name> - pointer. */ - if (is_attribute_p ("dllexport", identifier)) - return (args == NULL_TREE); - - /* Exception attribute allows the user to specify 1-2 strings - or identifiers that will fill in the 3rd and 4th fields - of the structured exception table. */ - if (is_attribute_p ("exception", identifier)) - { - int i; - - if (args == NULL_TREE) - return 0; - - for (i = 0; i < 2 && args != NULL_TREE; i++) - { - tree this_arg = TREE_VALUE (args); - args = TREE_PURPOSE (args); - - if (TREE_CODE (this_arg) != STRING_CST - && TREE_CODE (this_arg) != IDENTIFIER_NODE) - return 0; - } - - return (args == NULL_TREE); - } - } - return 0; } @@ -5904,36 +6688,6 @@ rs6000_set_default_type_attributes (type) return; } -/* Return a dll import reference corresponding to a call's SYMBOL_REF */ -struct rtx_def * -rs6000_dll_import_ref (call_ref) - rtx call_ref; -{ - const char *call_name; - int len; - char *p; - rtx reg1, reg2; - tree node; - - if (GET_CODE (call_ref) != SYMBOL_REF) - abort (); - - call_name = XSTR (call_ref, 0); - len = sizeof ("__imp_") + strlen (call_name); - p = alloca (len); - reg2 = gen_reg_rtx (Pmode); - - strcpy (p, "__imp_"); - strcat (p, call_name); - node = get_identifier (p); - - reg1 = force_reg (Pmode, gen_rtx_SYMBOL_REF (VOIDmode, - IDENTIFIER_POINTER (node))); - emit_move_insn (reg2, gen_rtx_MEM (Pmode, reg1)); - - return reg2; -} - /* Return a reference suitable for calling a function with the longcall attribute. */ struct rtx_def * @@ -6039,7 +6793,7 @@ rs6000_select_section (decl, reloc) /* If we are referencing a function that is static or is known to be in this file, make the SYMBOL_REF special. We can use this to indicate that we can branch to this function without emitting a no-op after the - call. For real AIX and NT calling sequences, we also replace the + call. For real AIX calling sequences, we also replace the function name with the real name (1 or 2 leading .'s), rather than the function descriptor name. This saves a lot of overriding code to read the prefixes. */ @@ -6055,7 +6809,7 @@ rs6000_encode_section_info (decl) && ! DECL_WEAK (decl)) SYMBOL_REF_FLAG (sym_ref) = 1; - if (DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_NT) + if (DEFAULT_ABI == ABI_AIX) { size_t len1 = (DEFAULT_ABI == ABI_AIX) ? 1 : 2; size_t len2 = strlen (XSTR (sym_ref, 0)); @@ -6173,4 +6927,8 @@ rs6000_add_gc_roots () { ggc_add_rtx_root (&rs6000_compare_op0, 1); ggc_add_rtx_root (&rs6000_compare_op1, 1); + + toc_hash_table = htab_create (1021, toc_hash_function, toc_hash_eq, NULL); + ggc_add_root (&toc_hash_table, 1, sizeof (toc_hash_table), + toc_hash_mark_table); } |

