summaryrefslogtreecommitdiff
path: root/gcc/config/rs6000
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/config/rs6000')
-rw-r--r--gcc/config/rs6000/aix43.h3
-rw-r--r--gcc/config/rs6000/aix51.h3
-rw-r--r--gcc/config/rs6000/aix52.h4
-rw-r--r--gcc/config/rs6000/aix53.h4
-rw-r--r--gcc/config/rs6000/aix61.h4
-rw-r--r--gcc/config/rs6000/darwin.h6
-rw-r--r--gcc/config/rs6000/dfp.md33
-rw-r--r--gcc/config/rs6000/linux.h15
-rw-r--r--gcc/config/rs6000/linux64.h15
-rw-r--r--gcc/config/rs6000/predicates.md96
-rw-r--r--gcc/config/rs6000/rs6000-modes.def4
-rw-r--r--gcc/config/rs6000/rs6000-protos.h3
-rw-r--r--gcc/config/rs6000/rs6000.c420
-rw-r--r--gcc/config/rs6000/rs6000.h15
-rw-r--r--gcc/config/rs6000/rs6000.md41
-rw-r--r--gcc/config/rs6000/rs6000.opt4
-rw-r--r--gcc/config/rs6000/rtems.h3
-rw-r--r--gcc/config/rs6000/t-linux6410
-rw-r--r--gcc/config/rs6000/t-linux64bele7
-rw-r--r--gcc/config/rs6000/t-linux64le3
-rw-r--r--gcc/config/rs6000/t-linux64lebe7
-rw-r--r--gcc/config/rs6000/vsx.md32
22 files changed, 659 insertions, 73 deletions
diff --git a/gcc/config/rs6000/aix43.h b/gcc/config/rs6000/aix43.h
index 70db7f7482f..b27c046021a 100644
--- a/gcc/config/rs6000/aix43.h
+++ b/gcc/config/rs6000/aix43.h
@@ -159,3 +159,6 @@ do { \
#define TARGET_USES_AIX64_OPT 1
#define TARGET_AIX_VERSION 43
+
+#undef TARGET_LIBC_HAS_FUNCTION
+#define TARGET_LIBC_HAS_FUNCTION no_c99_libc_has_function
diff --git a/gcc/config/rs6000/aix51.h b/gcc/config/rs6000/aix51.h
index 669dbbe03f3..3837bfdc0bb 100644
--- a/gcc/config/rs6000/aix51.h
+++ b/gcc/config/rs6000/aix51.h
@@ -163,3 +163,6 @@ do { \
#define TARGET_USE_JCR_SECTION 0
#define TARGET_AIX_VERSION 51
+
+#undef TARGET_LIBC_HAS_FUNCTION
+#define TARGET_LIBC_HAS_FUNCTION no_c99_libc_has_function
diff --git a/gcc/config/rs6000/aix52.h b/gcc/config/rs6000/aix52.h
index c57271a5a58..51954718b2e 100644
--- a/gcc/config/rs6000/aix52.h
+++ b/gcc/config/rs6000/aix52.h
@@ -166,10 +166,6 @@ do { \
#undef LD_INIT_SWITCH
#define LD_INIT_SWITCH "-binitfini"
-/* AIX 5.2 has the float and long double forms of math functions. */
-#undef TARGET_C99_FUNCTIONS
-#define TARGET_C99_FUNCTIONS 1
-
#ifndef _AIX52
extern long long int atoll(const char *);
#endif
diff --git a/gcc/config/rs6000/aix53.h b/gcc/config/rs6000/aix53.h
index b1b0759e7ff..b3bd73a6988 100644
--- a/gcc/config/rs6000/aix53.h
+++ b/gcc/config/rs6000/aix53.h
@@ -166,10 +166,6 @@ do { \
#undef LD_INIT_SWITCH
#define LD_INIT_SWITCH "-binitfini"
-/* AIX 5.2 has the float and long double forms of math functions. */
-#undef TARGET_C99_FUNCTIONS
-#define TARGET_C99_FUNCTIONS 1
-
#ifndef _AIX52
extern long long int atoll(const char *);
#endif
diff --git a/gcc/config/rs6000/aix61.h b/gcc/config/rs6000/aix61.h
index cd341b97eea..b0778143773 100644
--- a/gcc/config/rs6000/aix61.h
+++ b/gcc/config/rs6000/aix61.h
@@ -190,10 +190,6 @@ do { \
#undef LD_INIT_SWITCH
#define LD_INIT_SWITCH "-binitfini"
-/* AIX 5.2 has the float and long double forms of math functions. */
-#undef TARGET_C99_FUNCTIONS
-#define TARGET_C99_FUNCTIONS 1
-
#ifndef _AIX52
extern long long int atoll(const char *);
#endif
diff --git a/gcc/config/rs6000/darwin.h b/gcc/config/rs6000/darwin.h
index 0cf2f4c346d..d5919c4c71d 100644
--- a/gcc/config/rs6000/darwin.h
+++ b/gcc/config/rs6000/darwin.h
@@ -386,10 +386,8 @@ extern int darwin_emit_branch_islands;
#define OFFS_ASSIGNIVAR_FAST 0xFFFEFEC0
/* Old versions of Mac OS/Darwin don't have C99 functions available. */
-#undef TARGET_C99_FUNCTIONS
-#define TARGET_C99_FUNCTIONS \
- (TARGET_64BIT \
- || strverscmp (darwin_macosx_version_min, "10.3") >= 0)
+#undef TARGET_LIBC_HAS_FUNCTION
+#define TARGET_LIBC_HAS_FUNCTION darwin_libc_has_function
/* When generating kernel code or kexts, we don't use Altivec by
default, as kernel code doesn't save/restore those registers. */
diff --git a/gcc/config/rs6000/dfp.md b/gcc/config/rs6000/dfp.md
index 052ac482e0f..9a846239b04 100644
--- a/gcc/config/rs6000/dfp.md
+++ b/gcc/config/rs6000/dfp.md
@@ -132,11 +132,14 @@
"")
(define_insn "*negtd2_fpr"
- [(set (match_operand:TD 0 "gpc_reg_operand" "=d")
- (neg:TD (match_operand:TD 1 "gpc_reg_operand" "d")))]
+ [(set (match_operand:TD 0 "gpc_reg_operand" "=d,d")
+ (neg:TD (match_operand:TD 1 "gpc_reg_operand" "0,d")))]
"TARGET_HARD_FLOAT && TARGET_FPRS"
- "fneg %0,%1"
- [(set_attr "type" "fp")])
+ "@
+ fneg %0,%1
+ fneg %0,%1\;fmr %L0,%L1"
+ [(set_attr "type" "fp")
+ (set_attr "length" "4,8")])
(define_expand "abstd2"
[(set (match_operand:TD 0 "gpc_reg_operand" "")
@@ -145,18 +148,24 @@
"")
(define_insn "*abstd2_fpr"
- [(set (match_operand:TD 0 "gpc_reg_operand" "=d")
- (abs:TD (match_operand:TD 1 "gpc_reg_operand" "d")))]
+ [(set (match_operand:TD 0 "gpc_reg_operand" "=d,d")
+ (abs:TD (match_operand:TD 1 "gpc_reg_operand" "0,d")))]
"TARGET_HARD_FLOAT && TARGET_FPRS"
- "fabs %0,%1"
- [(set_attr "type" "fp")])
+ "@
+ fabs %0,%1
+ fabs %0,%1\;fmr %L0,%L1"
+ [(set_attr "type" "fp")
+ (set_attr "length" "4,8")])
(define_insn "*nabstd2_fpr"
- [(set (match_operand:TD 0 "gpc_reg_operand" "=d")
- (neg:TD (abs:TD (match_operand:TD 1 "gpc_reg_operand" "d"))))]
+ [(set (match_operand:TD 0 "gpc_reg_operand" "=d,d")
+ (neg:TD (abs:TD (match_operand:TD 1 "gpc_reg_operand" "0,d"))))]
"TARGET_HARD_FLOAT && TARGET_FPRS"
- "fnabs %0,%1"
- [(set_attr "type" "fp")])
+ "@
+ fnabs %0,%1
+ fnabs %0,%1\;fmr %L0,%L1"
+ [(set_attr "type" "fp")
+ (set_attr "length" "4,8")])
;; Hardware support for decimal floating point operations.
diff --git a/gcc/config/rs6000/linux.h b/gcc/config/rs6000/linux.h
index f7f2d80c4f2..2e5a56b3929 100644
--- a/gcc/config/rs6000/linux.h
+++ b/gcc/config/rs6000/linux.h
@@ -28,17 +28,18 @@
#ifdef SINGLE_LIBC
#define OPTION_GLIBC (DEFAULT_LIBC == LIBC_GLIBC)
+#define OPTION_UCLIBC (DEFAULT_LIBC == LIBC_UCLIBC)
+#define OPTION_BIONIC (DEFAULT_LIBC == LIBC_BIONIC)
#else
#define OPTION_GLIBC (linux_libc == LIBC_GLIBC)
+#define OPTION_UCLIBC (linux_libc == LIBC_UCLIBC)
+#define OPTION_BIONIC (linux_libc == LIBC_BIONIC)
#endif
-/* glibc has float and long double forms of math functions. */
-#undef TARGET_C99_FUNCTIONS
-#define TARGET_C99_FUNCTIONS (OPTION_GLIBC)
-
-/* Whether we have sincos that follows the GNU extension. */
-#undef TARGET_HAS_SINCOS
-#define TARGET_HAS_SINCOS (OPTION_GLIBC)
+/* Determine what functions are present at the runtime;
+ this includes full c99 runtime and sincos. */
+#undef TARGET_LIBC_HAS_FUNCTION
+#define TARGET_LIBC_HAS_FUNCTION linux_android_libc_has_function
#undef TARGET_OS_CPP_BUILTINS
#define TARGET_OS_CPP_BUILTINS() \
diff --git a/gcc/config/rs6000/linux64.h b/gcc/config/rs6000/linux64.h
index 79f0f0b5f00..439f53f2d23 100644
--- a/gcc/config/rs6000/linux64.h
+++ b/gcc/config/rs6000/linux64.h
@@ -288,17 +288,18 @@ extern int dot_symbols;
#ifdef SINGLE_LIBC
#define OPTION_GLIBC (DEFAULT_LIBC == LIBC_GLIBC)
+#define OPTION_UCLIBC (DEFAULT_LIBC == LIBC_UCLIBC)
+#define OPTION_BIONIC (DEFAULT_LIBC == LIBC_BIONIC)
#else
#define OPTION_GLIBC (linux_libc == LIBC_GLIBC)
+#define OPTION_UCLIBC (linux_libc == LIBC_UCLIBC)
+#define OPTION_BIONIC (linux_libc == LIBC_BIONIC)
#endif
-/* glibc has float and long double forms of math functions. */
-#undef TARGET_C99_FUNCTIONS
-#define TARGET_C99_FUNCTIONS (OPTION_GLIBC)
-
-/* Whether we have sincos that follows the GNU extension. */
-#undef TARGET_HAS_SINCOS
-#define TARGET_HAS_SINCOS (OPTION_GLIBC)
+/* Determine what functions are present at the runtime;
+ this includes full c99 runtime and sincos. */
+#undef TARGET_LIBC_HAS_FUNCTION
+#define TARGET_LIBC_HAS_FUNCTION linux_android_libc_has_function
#undef TARGET_OS_CPP_BUILTINS
#define TARGET_OS_CPP_BUILTINS() \
diff --git a/gcc/config/rs6000/predicates.md b/gcc/config/rs6000/predicates.md
index 18912f15a4a..7338e764c5c 100644
--- a/gcc/config/rs6000/predicates.md
+++ b/gcc/config/rs6000/predicates.md
@@ -1702,3 +1702,99 @@
return GET_CODE (op) == UNSPEC && XINT (op, 1) == UNSPEC_TOCREL;
})
+
+;; Match the first insn (addis) in fusing the combination of addis and loads to
+;; GPR registers on power8.
+(define_predicate "fusion_gpr_addis"
+ (match_code "const_int,high,plus")
+{
+ HOST_WIDE_INT value;
+ rtx int_const;
+
+ if (GET_CODE (op) == HIGH)
+ return 1;
+
+ if (CONST_INT_P (op))
+ int_const = op;
+
+ else if (GET_CODE (op) == PLUS
+ && base_reg_operand (XEXP (op, 0), Pmode)
+ && CONST_INT_P (XEXP (op, 1)))
+ int_const = XEXP (op, 1);
+
+ else
+ return 0;
+
+ /* Power8 currently will only do the fusion if the top 11 bits of the addis
+ value are all 1's or 0's. */
+ value = INTVAL (int_const);
+ if ((value & (HOST_WIDE_INT)0xffff) != 0)
+ return 0;
+
+ if ((value & (HOST_WIDE_INT)0xffff0000) == 0)
+ return 0;
+
+ return (IN_RANGE (value >> 16, -32, 31));
+})
+
+;; Match the second insn (lbz, lhz, lwz, ld) in fusing the combination of addis
+;; and loads to GPR registers on power8.
+(define_predicate "fusion_gpr_mem_load"
+ (match_code "mem,sign_extend,zero_extend")
+{
+ rtx addr;
+
+ /* Handle sign/zero extend. */
+ if (GET_CODE (op) == ZERO_EXTEND
+ || (TARGET_P8_FUSION_SIGN && GET_CODE (op) == SIGN_EXTEND))
+ {
+ op = XEXP (op, 0);
+ mode = GET_MODE (op);
+ }
+
+ if (!MEM_P (op))
+ return 0;
+
+ switch (mode)
+ {
+ case QImode:
+ case HImode:
+ case SImode:
+ break;
+
+ case DImode:
+ if (!TARGET_POWERPC64)
+ return 0;
+ break;
+
+ default:
+ return 0;
+ }
+
+ addr = XEXP (op, 0);
+ if (GET_CODE (addr) == PLUS)
+ {
+ rtx base = XEXP (addr, 0);
+ rtx offset = XEXP (addr, 1);
+
+ return (base_reg_operand (base, GET_MODE (base))
+ && satisfies_constraint_I (offset));
+ }
+
+ else if (GET_CODE (addr) == LO_SUM)
+ {
+ rtx base = XEXP (addr, 0);
+ rtx offset = XEXP (addr, 1);
+
+ if (!base_reg_operand (base, GET_MODE (base)))
+ return 0;
+
+ else if (TARGET_XCOFF || (TARGET_ELF && TARGET_POWERPC64))
+ return small_toc_ref (offset, GET_MODE (offset));
+
+ else if (TARGET_ELF && !TARGET_POWERPC64)
+ return CONSTANT_P (offset);
+ }
+
+ return 0;
+})
diff --git a/gcc/config/rs6000/rs6000-modes.def b/gcc/config/rs6000/rs6000-modes.def
index 54548be7038..5124e1665d4 100644
--- a/gcc/config/rs6000/rs6000-modes.def
+++ b/gcc/config/rs6000/rs6000-modes.def
@@ -42,5 +42,7 @@ VECTOR_MODES (FLOAT, 8); /* V4HF V2SF */
VECTOR_MODES (FLOAT, 16); /* V8HF V4SF V2DF */
VECTOR_MODES (FLOAT, 32); /* V16HF V8SF V4DF */
-/* Replacement for TImode that only is allowed in GPRs. */
+/* Replacement for TImode that only is allowed in GPRs. We also use PTImode
+ for quad memory atomic operations to force getting an even/odd register
+ combination. */
PARTIAL_INT_MODE (TI);
diff --git a/gcc/config/rs6000/rs6000-protos.h b/gcc/config/rs6000/rs6000-protos.h
index 3a7b37a8270..3ddabb81c39 100644
--- a/gcc/config/rs6000/rs6000-protos.h
+++ b/gcc/config/rs6000/rs6000-protos.h
@@ -73,6 +73,9 @@ extern int mems_ok_for_quad_peep (rtx, rtx);
extern bool gpr_or_gpr_p (rtx, rtx);
extern bool direct_move_p (rtx, rtx);
extern bool quad_load_store_p (rtx, rtx);
+extern bool fusion_gpr_load_p (rtx *, bool);
+extern void expand_fusion_gpr_load (rtx *);
+extern const char *emit_fusion_gpr_load (rtx *);
extern enum reg_class (*rs6000_preferred_reload_class_ptr) (rtx,
enum reg_class);
extern enum reg_class (*rs6000_secondary_reload_class_ptr) (enum reg_class,
diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c
index 8b939d8e826..c1acbd825ea 100644
--- a/gcc/config/rs6000/rs6000.c
+++ b/gcc/config/rs6000/rs6000.c
@@ -284,9 +284,6 @@ static struct
{ "rsqrtd", (RECIP_DF_RSQRT | RECIP_V2DF_RSQRT) },
};
-/* 2 argument gen function typedef. */
-typedef rtx (*gen_2arg_fn_t) (rtx, rtx, rtx);
-
/* Pointer to function (in rs6000-c.c) that can define or undefine target
macros that have changed. Languages that don't support the preprocessor
don't link in rs6000-c.c, so we can't call it directly. */
@@ -3074,6 +3071,21 @@ rs6000_option_override_internal (bool global_init_p)
rs6000_isa_flags &= ~OPTION_MASK_QUAD_MEMORY;
}
+ /* Enable power8 fusion if we are tuning for power8, even if we aren't
+ generating power8 instructions. */
+ if (!(rs6000_isa_flags_explicit & OPTION_MASK_P8_FUSION))
+ rs6000_isa_flags |= (processor_target_table[tune_index].target_enable
+ & OPTION_MASK_P8_FUSION);
+
+ /* Power8 does not fuse sign extended loads with the addis. If we are
+ optimizing at high levels for speed, convert a sign extended load into a
+ zero extending load, and an explicit sign extension. */
+ if (TARGET_P8_FUSION
+ && !(rs6000_isa_flags_explicit & OPTION_MASK_P8_FUSION_SIGN)
+ && optimize_function_for_speed_p (cfun)
+ && optimize >= 3)
+ rs6000_isa_flags |= OPTION_MASK_P8_FUSION_SIGN;
+
if (TARGET_DEBUG_REG || TARGET_DEBUG_TARGET)
rs6000_print_isa_options (stderr, 0, "after defaults", rs6000_isa_flags);
@@ -6918,9 +6930,7 @@ rs6000_legitimize_reload_address (rtx x, enum machine_mode mode,
&& GET_CODE (XEXP (x, 1)) == CONST_INT
&& reg_offset_p
&& !SPE_VECTOR_MODE (mode)
- && !(TARGET_E500_DOUBLE && (mode == DFmode || mode == TFmode
- || mode == DDmode || mode == TDmode
- || mode == DImode))
+ && !(TARGET_E500_DOUBLE && GET_MODE_SIZE (mode) > UNITS_PER_WORD)
&& (!VECTOR_MODE_P (mode) || VECTOR_MEM_NONE_P (mode)))
{
HOST_WIDE_INT val = INTVAL (XEXP (x, 1));
@@ -8329,8 +8339,8 @@ rs6000_function_arg_boundary (enum machine_mode mode, const_tree type)
|| (type && TREE_CODE (type) == VECTOR_TYPE
&& int_size_in_bytes (type) >= 16))
return 128;
- else if (TARGET_MACHO
- && rs6000_darwin64_abi
+ else if (((TARGET_MACHO && rs6000_darwin64_abi)
+ || (DEFAULT_ABI == ABI_AIX && !rs6000_compat_align_parm))
&& mode == BLKmode
&& type && TYPE_ALIGN (type) > 64)
return 128;
@@ -9878,8 +9888,9 @@ rs6000_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p,
We don't need to check for pass-by-reference because of the test above.
We can return a simplifed answer, since we know there's no offset to add. */
- if (TARGET_MACHO
- && rs6000_darwin64_abi
+ if (((TARGET_MACHO
+ && rs6000_darwin64_abi)
+ || (DEFAULT_ABI == ABI_AIX && !rs6000_compat_align_parm))
&& integer_zerop (TYPE_SIZE (type)))
{
unsigned HOST_WIDE_INT align, boundary;
@@ -11133,9 +11144,6 @@ htm_expand_builtin (tree exp, rtx target, bool * expandedp)
switch (nopnds)
{
- case 0:
- pat = GEN_FCN (icode) (NULL_RTX);
- break;
case 1:
pat = GEN_FCN (icode) (op[0]);
break;
@@ -21401,8 +21409,7 @@ rs6000_emit_prologue (void)
HOST_WIDE_INT offset;
if (!(strategy & SAVE_INLINE_GPRS))
- ool_adjust = 8 * (info->first_gp_reg_save
- - (FIRST_SAVRES_REGISTER + 1));
+ ool_adjust = 8 * (info->first_gp_reg_save - FIRST_SAVED_GP_REGNO);
offset = info->spe_gp_save_offset + frame_off - ool_adjust;
spe_save_area_ptr = gen_rtx_REG (Pmode, 11);
save_off = frame_off - offset;
@@ -22644,8 +22651,7 @@ rs6000_emit_epilogue (int sibcall)
anew to every function. */
if (!restoring_GPRs_inline)
- ool_adjust = 8 * (info->first_gp_reg_save
- - (FIRST_SAVRES_REGISTER + 1));
+ ool_adjust = 8 * (info->first_gp_reg_save - FIRST_SAVED_GP_REGNO);
frame_reg_rtx = gen_rtx_REG (Pmode, 11);
emit_insn (gen_addsi3 (frame_reg_rtx, old_frame_reg_rtx,
GEN_INT (info->spe_gp_save_offset
@@ -28127,7 +28133,7 @@ rs6000_emit_swdiv (rtx dst, rtx n, rtx d, bool note_p)
passes++;
enum insn_code code = optab_handler (smul_optab, mode);
- gen_2arg_fn_t gen_mul = (gen_2arg_fn_t) GEN_FCN (code);
+ insn_gen_fn gen_mul = GEN_FCN (code);
gcc_assert (code != CODE_FOR_nothing);
@@ -28205,7 +28211,7 @@ rs6000_emit_swrsqrt (rtx dst, rtx src)
int i;
rtx halfthree;
enum insn_code code = optab_handler (smul_optab, mode);
- gen_2arg_fn_t gen_mul = (gen_2arg_fn_t) GEN_FCN (code);
+ insn_gen_fn gen_mul = GEN_FCN (code);
gcc_assert (code != CODE_FOR_nothing);
@@ -30419,6 +30425,382 @@ rs6000_split_logical (rtx operands[3],
}
+/* Return true if the peephole2 can combine a load involving a combination of
+ an addis instruction and a load with an offset that can be fused together on
+ a power8.
+
+ The operands are:
+ operands[0] register set with addis
+ operands[1] value set via addis
+ operands[2] target register being loaded
+ operands[3] D-form memory reference using operands[0].
+
+ In addition, we are passed a boolean that is true if this is a peephole2,
+ and we can use see if the addis_reg is dead after the insn and can be
+ replaced by the target register. */
+
+bool
+fusion_gpr_load_p (rtx *operands, bool peep2_p)
+{
+ rtx addis_reg = operands[0];
+ rtx addis_value = operands[1];
+ rtx target = operands[2];
+ rtx mem = operands[3];
+ rtx addr;
+ rtx base_reg;
+
+ /* Validate arguments. */
+ if (!base_reg_operand (addis_reg, GET_MODE (addis_reg)))
+ return false;
+
+ if (!base_reg_operand (target, GET_MODE (target)))
+ return false;
+
+ if (!fusion_gpr_addis (addis_value, GET_MODE (addis_value)))
+ return false;
+
+ if (!fusion_gpr_mem_load (mem, GET_MODE (mem)))
+ return false;
+
+ /* Allow sign/zero extension. */
+ if (GET_CODE (mem) == ZERO_EXTEND
+ || (GET_CODE (mem) == SIGN_EXTEND && TARGET_P8_FUSION_SIGN))
+ mem = XEXP (mem, 0);
+
+ if (!MEM_P (mem))
+ return false;
+
+ addr = XEXP (mem, 0); /* either PLUS or LO_SUM. */
+ if (GET_CODE (addr) != PLUS && GET_CODE (addr) != LO_SUM)
+ return false;
+
+ /* Validate that the register used to load the high value is either the
+ register being loaded, or we can safely replace its use in a peephole2.
+
+ If this is a peephole2, we assume that there are 2 instructions in the
+ peephole (addis and load), so we want to check if the target register was
+ not used in the memory address and the register to hold the addis result
+ is dead after the peephole. */
+ if (REGNO (addis_reg) != REGNO (target))
+ {
+ if (!peep2_p)
+ return false;
+
+ if (reg_mentioned_p (target, mem))
+ return false;
+
+ if (!peep2_reg_dead_p (2, addis_reg))
+ return false;
+ }
+
+ base_reg = XEXP (addr, 0);
+ return REGNO (addis_reg) == REGNO (base_reg);
+}
+
+/* During the peephole2 pass, adjust and expand the insns for a load fusion
+ sequence. We adjust the addis register to use the target register. If the
+ load sign extends, we adjust the code to do the zero extending load, and an
+ explicit sign extension later since the fusion only covers zero extending
+ loads.
+
+ The operands are:
+ operands[0] register set with addis (to be replaced with target)
+ operands[1] value set via addis
+ operands[2] target register being loaded
+ operands[3] D-form memory reference using operands[0]. */
+
+void
+expand_fusion_gpr_load (rtx *operands)
+{
+ rtx addis_value = operands[1];
+ rtx target = operands[2];
+ rtx orig_mem = operands[3];
+ rtx new_addr, new_mem, orig_addr, offset;
+ enum rtx_code plus_or_lo_sum;
+ enum machine_mode target_mode = GET_MODE (target);
+ enum machine_mode extend_mode = target_mode;
+ enum machine_mode ptr_mode = Pmode;
+ enum rtx_code extend = UNKNOWN;
+ rtx addis_reg = ((ptr_mode == target_mode)
+ ? target
+ : simplify_subreg (ptr_mode, target, target_mode, 0));
+
+ if (GET_CODE (orig_mem) == ZERO_EXTEND
+ || (TARGET_P8_FUSION_SIGN && GET_CODE (orig_mem) == SIGN_EXTEND))
+ {
+ extend = GET_CODE (orig_mem);
+ orig_mem = XEXP (orig_mem, 0);
+ target_mode = GET_MODE (orig_mem);
+ }
+
+ gcc_assert (MEM_P (orig_mem));
+
+ orig_addr = XEXP (orig_mem, 0);
+ plus_or_lo_sum = GET_CODE (orig_addr);
+ gcc_assert (plus_or_lo_sum == PLUS || plus_or_lo_sum == LO_SUM);
+
+ offset = XEXP (orig_addr, 1);
+ new_addr = gen_rtx_fmt_ee (plus_or_lo_sum, ptr_mode, addis_reg, offset);
+ new_mem = change_address (orig_mem, target_mode, new_addr);
+
+ if (extend != UNKNOWN)
+ new_mem = gen_rtx_fmt_e (ZERO_EXTEND, extend_mode, new_mem);
+
+ emit_insn (gen_rtx_SET (VOIDmode, addis_reg, addis_value));
+ emit_insn (gen_rtx_SET (VOIDmode, target, new_mem));
+
+ if (extend == SIGN_EXTEND)
+ {
+ int sub_off = ((BYTES_BIG_ENDIAN)
+ ? GET_MODE_SIZE (extend_mode) - GET_MODE_SIZE (target_mode)
+ : 0);
+ rtx sign_reg
+ = simplify_subreg (target_mode, target, extend_mode, sub_off);
+
+ emit_insn (gen_rtx_SET (VOIDmode, target,
+ gen_rtx_SIGN_EXTEND (extend_mode, sign_reg)));
+ }
+
+ return;
+}
+
+/* Return a string to fuse an addis instruction with a gpr load to the same
+ register that we loaded up the addis instruction. The code is complicated,
+ so we call output_asm_insn directly, and just return "".
+
+ The operands are:
+ operands[0] register set with addis (must be same reg as target).
+ operands[1] value set via addis
+ operands[2] target register being loaded
+ operands[3] D-form memory reference using operands[0]. */
+
+const char *
+emit_fusion_gpr_load (rtx *operands)
+{
+ rtx addis_reg = operands[0];
+ rtx addis_value = operands[1];
+ rtx target = operands[2];
+ rtx mem = operands[3];
+ rtx fuse_ops[10];
+ rtx addr;
+ rtx load_offset;
+ const char *addis_str = NULL;
+ const char *load_str = NULL;
+ const char *extend_insn = NULL;
+ const char *mode_name = NULL;
+ char insn_template[80];
+ enum machine_mode mode;
+ const char *comment_str = ASM_COMMENT_START;
+ bool sign_p = false;
+
+ gcc_assert (REG_P (addis_reg) && REG_P (target));
+ gcc_assert (REGNO (addis_reg) == REGNO (target));
+
+ if (*comment_str == ' ')
+ comment_str++;
+
+ /* Allow sign/zero extension. */
+ if (GET_CODE (mem) == ZERO_EXTEND)
+ mem = XEXP (mem, 0);
+
+ else if (GET_CODE (mem) == SIGN_EXTEND && TARGET_P8_FUSION_SIGN)
+ {
+ sign_p = true;
+ mem = XEXP (mem, 0);
+ }
+
+ gcc_assert (MEM_P (mem));
+ addr = XEXP (mem, 0);
+ if (GET_CODE (addr) != PLUS && GET_CODE (addr) != LO_SUM)
+ gcc_unreachable ();
+
+ load_offset = XEXP (addr, 1);
+
+ /* Now emit the load instruction to the same register. */
+ mode = GET_MODE (mem);
+ switch (mode)
+ {
+ case QImode:
+ mode_name = "char";
+ load_str = "lbz";
+ extend_insn = "extsb %0,%0";
+ break;
+
+ case HImode:
+ mode_name = "short";
+ load_str = "lhz";
+ extend_insn = "extsh %0,%0";
+ break;
+
+ case SImode:
+ mode_name = "int";
+ load_str = "lwz";
+ extend_insn = "extsw %0,%0";
+ break;
+
+ case DImode:
+ if (TARGET_POWERPC64)
+ {
+ mode_name = "long";
+ load_str = "ld";
+ }
+ else
+ gcc_unreachable ();
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+
+ /* Emit the addis instruction. */
+ fuse_ops[0] = target;
+ if (satisfies_constraint_L (addis_value))
+ {
+ fuse_ops[1] = addis_value;
+ addis_str = "lis %0,%v1";
+ }
+
+ else if (GET_CODE (addis_value) == PLUS)
+ {
+ rtx op0 = XEXP (addis_value, 0);
+ rtx op1 = XEXP (addis_value, 1);
+
+ if (REG_P (op0) && CONST_INT_P (op1)
+ && satisfies_constraint_L (op1))
+ {
+ fuse_ops[1] = op0;
+ fuse_ops[2] = op1;
+ addis_str = "addis %0,%1,%v2";
+ }
+ }
+
+ else if (GET_CODE (addis_value) == HIGH)
+ {
+ rtx value = XEXP (addis_value, 0);
+ if (GET_CODE (value) == UNSPEC && XINT (value, 1) == UNSPEC_TOCREL)
+ {
+ fuse_ops[1] = XVECEXP (value, 0, 0); /* symbol ref. */
+ fuse_ops[2] = XVECEXP (value, 0, 1); /* TOC register. */
+ if (TARGET_ELF)
+ addis_str = "addis %0,%2,%1@toc@ha";
+
+ else if (TARGET_XCOFF)
+ addis_str = "addis %0,%1@u(%2)";
+
+ else
+ gcc_unreachable ();
+ }
+
+ else if (GET_CODE (value) == PLUS)
+ {
+ rtx op0 = XEXP (value, 0);
+ rtx op1 = XEXP (value, 1);
+
+ if (GET_CODE (op0) == UNSPEC
+ && XINT (op0, 1) == UNSPEC_TOCREL
+ && CONST_INT_P (op1))
+ {
+ fuse_ops[1] = XVECEXP (op0, 0, 0); /* symbol ref. */
+ fuse_ops[2] = XVECEXP (op0, 0, 1); /* TOC register. */
+ fuse_ops[3] = op1;
+ if (TARGET_ELF)
+ addis_str = "addis %0,%2,%1+%3@toc@ha";
+
+ else if (TARGET_XCOFF)
+ addis_str = "addis %0,%1+%3@u(%2)";
+
+ else
+ gcc_unreachable ();
+ }
+ }
+
+ else if (satisfies_constraint_L (value))
+ {
+ fuse_ops[1] = value;
+ addis_str = "lis %0,%v1";
+ }
+
+ else if (TARGET_ELF && !TARGET_POWERPC64 && CONSTANT_P (value))
+ {
+ fuse_ops[1] = value;
+ addis_str = "lis %0,%1@ha";
+ }
+ }
+
+ if (!addis_str)
+ fatal_insn ("Could not generate addis value for fusion", addis_value);
+
+ sprintf (insn_template, "%s\t\t%s gpr load fusion, type %s", addis_str,
+ comment_str, mode_name);
+ output_asm_insn (insn_template, fuse_ops);
+
+ /* Emit the D-form load instruction. */
+ if (CONST_INT_P (load_offset) && satisfies_constraint_I (load_offset))
+ {
+ sprintf (insn_template, "%s %%0,%%1(%%0)", load_str);
+ fuse_ops[1] = load_offset;
+ output_asm_insn (insn_template, fuse_ops);
+ }
+
+ else if (GET_CODE (load_offset) == UNSPEC
+ && XINT (load_offset, 1) == UNSPEC_TOCREL)
+ {
+ if (TARGET_ELF)
+ sprintf (insn_template, "%s %%0,%%1@toc@l(%%0)", load_str);
+
+ else if (TARGET_XCOFF)
+ sprintf (insn_template, "%s %%0,%%1@l(%%0)", load_str);
+
+ else
+ gcc_unreachable ();
+
+ fuse_ops[1] = XVECEXP (load_offset, 0, 0);
+ output_asm_insn (insn_template, fuse_ops);
+ }
+
+ else if (GET_CODE (load_offset) == PLUS
+ && GET_CODE (XEXP (load_offset, 0)) == UNSPEC
+ && XINT (XEXP (load_offset, 0), 1) == UNSPEC_TOCREL
+ && CONST_INT_P (XEXP (load_offset, 1)))
+ {
+ rtx tocrel_unspec = XEXP (load_offset, 0);
+ if (TARGET_ELF)
+ sprintf (insn_template, "%s %%0,%%1+%%2@toc@l(%%0)", load_str);
+
+ else if (TARGET_XCOFF)
+ sprintf (insn_template, "%s %%0,%%1+%%2@l(%%0)", load_str);
+
+ else
+ gcc_unreachable ();
+
+ fuse_ops[1] = XVECEXP (tocrel_unspec, 0, 0);
+ fuse_ops[2] = XEXP (load_offset, 1);
+ output_asm_insn (insn_template, fuse_ops);
+ }
+
+ else if (TARGET_ELF && !TARGET_POWERPC64 && CONSTANT_P (load_offset))
+ {
+ sprintf (insn_template, "%s %%0,%%1@l(%%0)", load_str);
+
+ fuse_ops[1] = load_offset;
+ output_asm_insn (insn_template, fuse_ops);
+ }
+
+ else
+ fatal_insn ("Unable to generate load offset for fusion", load_offset);
+
+ /* Handle sign extension. The peephole2 pass generates this as a separate
+ insn, but we handle it just in case it got reattached. */
+ if (sign_p)
+ {
+ gcc_assert (extend_insn != NULL);
+ output_asm_insn (extend_insn, fuse_ops);
+ }
+
+ return "";
+}
+
+
struct gcc_target targetm = TARGET_INITIALIZER;
#include "gt-rs6000.h"
diff --git a/gcc/config/rs6000/rs6000.h b/gcc/config/rs6000/rs6000.h
index e5a6abd6d0d..a5a7a859426 100644
--- a/gcc/config/rs6000/rs6000.h
+++ b/gcc/config/rs6000/rs6000.h
@@ -1498,7 +1498,8 @@ extern enum reg_class rs6000_constraints[RS6000_CONSTRAINT_MAX];
On the RS/6000, we grow upwards, from the area after the outgoing
arguments. */
-#define FRAME_GROWS_DOWNWARD (flag_stack_protect != 0 || flag_asan != 0)
+#define FRAME_GROWS_DOWNWARD (flag_stack_protect != 0 \
+ || (flag_sanitize & SANITIZE_ADDRESS) != 0)
/* Size of the outgoing register save area */
#define RS6000_REG_SAVE ((DEFAULT_ABI == ABI_AIX \
@@ -2138,9 +2139,15 @@ extern int toc_initialized;
} \
else if (TARGET_XCOFF) \
{ \
- fputs ("\t.lglobl\t.", FILE); \
- RS6000_OUTPUT_BASENAME (FILE, alias); \
- putc ('\n', FILE); \
+ if (!RS6000_WEAK || !DECL_WEAK (DECL)) \
+ { \
+ fputs ("\t.lglobl\t.", FILE); \
+ RS6000_OUTPUT_BASENAME (FILE, alias); \
+ putc ('\n', FILE); \
+ fputs ("\t.lglobl\t", FILE); \
+ RS6000_OUTPUT_BASENAME (FILE, alias); \
+ putc ('\n', FILE); \
+ } \
} \
fputs ("\t.set\t.", FILE); \
RS6000_OUTPUT_BASENAME (FILE, alias); \
diff --git a/gcc/config/rs6000/rs6000.md b/gcc/config/rs6000/rs6000.md
index 064a51da608..3880f9175a2 100644
--- a/gcc/config/rs6000/rs6000.md
+++ b/gcc/config/rs6000/rs6000.md
@@ -136,7 +136,6 @@
UNSPEC_P8V_MTVSRD
UNSPEC_P8V_XXPERMDI
UNSPEC_P8V_RELOAD_FROM_VSX
- UNSPEC_FUSION_GPR
])
;;
@@ -15757,7 +15756,8 @@
"cmpw %2,%L0,%1\;"
"bne- %2,$-16";
}
-})
+}
+ [(set_attr "length" "20")])
(define_insn "rs6000_mftb_<mode>"
[(set (match_operand:P 0 "gpc_reg_operand" "=r")
@@ -15771,6 +15771,43 @@
})
+;; Power8 fusion support for fusing an addis instruction with a D-form load of
+;; a GPR. The addis instruction must be adjacent to the load, and use the same
+;; register that is being loaded. The fused ops must be physically adjacent.
+
+;; We use define_peephole for the actual addis/load, and the register used to
+;; hold the addis value must be the same as the register being loaded. We use
+;; define_peephole2 to change the register used for addis to be the register
+;; being loaded, since we can look at whether it is dead after the load insn.
+
+(define_peephole
+ [(set (match_operand:P 0 "base_reg_operand" "")
+ (match_operand:P 1 "fusion_gpr_addis" ""))
+ (set (match_operand:INT1 2 "base_reg_operand" "")
+ (match_operand:INT1 3 "fusion_gpr_mem_load" ""))]
+ "TARGET_P8_FUSION && fusion_gpr_load_p (operands, false)"
+{
+ return emit_fusion_gpr_load (operands);
+}
+ [(set_attr "type" "load")
+ (set_attr "length" "8")])
+
+(define_peephole2
+ [(set (match_operand:P 0 "base_reg_operand" "")
+ (match_operand:P 1 "fusion_gpr_addis" ""))
+ (set (match_operand:INT1 2 "base_reg_operand" "")
+ (match_operand:INT1 3 "fusion_gpr_mem_load" ""))]
+ "TARGET_P8_FUSION
+ && (REGNO (operands[0]) != REGNO (operands[2])
+ || GET_CODE (operands[3]) == SIGN_EXTEND)
+ && fusion_gpr_load_p (operands, true)"
+ [(const_int 0)]
+{
+ expand_fusion_gpr_load (operands);
+ DONE;
+})
+
+
(include "sync.md")
(include "vector.md")
diff --git a/gcc/config/rs6000/rs6000.opt b/gcc/config/rs6000/rs6000.opt
index f36e4758031..cd83cb2d206 100644
--- a/gcc/config/rs6000/rs6000.opt
+++ b/gcc/config/rs6000/rs6000.opt
@@ -546,3 +546,7 @@ Use ISA 2.07 transactional memory (HTM) instructions
mquad-memory
Target Report Mask(QUAD_MEMORY) Var(rs6000_isa_flags)
Generate the quad word memory instructions (lq/stq/lqarx/stqcx).
+
+mcompat-align-parm
+Target Report Var(rs6000_compat_align_parm) Init(0) Save
+Generate aggregate parameter passing code with at most 64-bit alignment.
diff --git a/gcc/config/rs6000/rtems.h b/gcc/config/rs6000/rtems.h
index b910b5ec5a2..fb22be1e8bb 100644
--- a/gcc/config/rs6000/rtems.h
+++ b/gcc/config/rs6000/rtems.h
@@ -34,6 +34,9 @@
} \
while (0)
+#undef TARGET_LIBGCC_SDATA_SECTION
+#define TARGET_LIBGCC_SDATA_SECTION ".sdata"
+
#undef CPP_OS_DEFAULT_SPEC
#define CPP_OS_DEFAULT_SPEC "%(cpp_os_rtems)"
diff --git a/gcc/config/rs6000/t-linux64 b/gcc/config/rs6000/t-linux64
index 9175de2ffe3..70e928dd7cd 100644
--- a/gcc/config/rs6000/t-linux64
+++ b/gcc/config/rs6000/t-linux64
@@ -25,8 +25,8 @@
# it doesn't tell anything about the 32bit libraries on those systems. Set
# MULTILIB_OSDIRNAMES according to what is found on the target.
-MULTILIB_OPTIONS = m64/m32
-MULTILIB_DIRNAMES = 64 32
-MULTILIB_EXTRA_OPTS = fPIC
-MULTILIB_OSDIRNAMES = ../lib64$(call if_multiarch,:powerpc64-linux-gnu)
-MULTILIB_OSDIRNAMES += $(if $(wildcard $(shell echo $(SYSTEM_HEADER_DIR))/../../usr/lib32),../lib32,../lib)$(call if_multiarch,:powerpc-linux-gnu)
+MULTILIB_OPTIONS := m64/m32
+MULTILIB_DIRNAMES := 64 32
+MULTILIB_EXTRA_OPTS :=
+MULTILIB_OSDIRNAMES := m64=../lib64$(call if_multiarch,:powerpc64-linux-gnu)
+MULTILIB_OSDIRNAMES += m32=$(if $(wildcard $(shell echo $(SYSTEM_HEADER_DIR))/../../usr/lib32),../lib32,../lib)$(call if_multiarch,:powerpc-linux-gnu)
diff --git a/gcc/config/rs6000/t-linux64bele b/gcc/config/rs6000/t-linux64bele
new file mode 100644
index 00000000000..97c1ee6fb4d
--- /dev/null
+++ b/gcc/config/rs6000/t-linux64bele
@@ -0,0 +1,7 @@
+#rs6000/t-linux64end
+
+MULTILIB_OPTIONS += mlittle
+MULTILIB_DIRNAMES += le
+MULTILIB_OSDIRNAMES += $(subst =,.mlittle=,$(subst lible32,lib32le,$(subst lible64,lib64le,$(subst lib,lible,$(subst -linux,le-linux,$(MULTILIB_OSDIRNAMES))))))
+MULTILIB_OSDIRNAMES += $(subst $(if $(findstring 64,$(target)),m64,m32).,,$(filter $(if $(findstring 64,$(target)),m64,m32).mlittle%,$(MULTILIB_OSDIRNAMES)))
+MULTILIB_MATCHES := ${MULTILIB_MATCHES_ENDIAN}
diff --git a/gcc/config/rs6000/t-linux64le b/gcc/config/rs6000/t-linux64le
new file mode 100644
index 00000000000..0cf38e1523a
--- /dev/null
+++ b/gcc/config/rs6000/t-linux64le
@@ -0,0 +1,3 @@
+#rs6000/t-linux64le
+
+MULTILIB_OSDIRNAMES := $(subst -linux,le-linux,$(MULTILIB_OSDIRNAMES))
diff --git a/gcc/config/rs6000/t-linux64lebe b/gcc/config/rs6000/t-linux64lebe
new file mode 100644
index 00000000000..2e63bdb9fc9
--- /dev/null
+++ b/gcc/config/rs6000/t-linux64lebe
@@ -0,0 +1,7 @@
+#rs6000/t-linux64leend
+
+MULTILIB_OPTIONS += mbig
+MULTILIB_DIRNAMES += be
+MULTILIB_OSDIRNAMES += $(subst =,.mbig=,$(subst libbe32,lib32be,$(subst libbe64,lib64be,$(subst lib,libbe,$(subst le-linux,-linux,$(MULTILIB_OSDIRNAMES))))))
+MULTILIB_OSDIRNAMES += $(subst $(if $(findstring 64,$(target)),m64,m32).,,$(filter $(if $(findstring 64,$(target)),m64,m32).mbig%,$(MULTILIB_OSDIRNAMES)))
+MULTILIB_MATCHES := ${MULTILIB_MATCHES_ENDIAN}
diff --git a/gcc/config/rs6000/vsx.md b/gcc/config/rs6000/vsx.md
index 5e6f397031c..11d6b8bb4d0 100644
--- a/gcc/config/rs6000/vsx.md
+++ b/gcc/config/rs6000/vsx.md
@@ -40,6 +40,14 @@
;; it to use gprs as well as vsx registers.
(define_mode_iterator VSX_M [V16QI V8HI V4SI V2DI V4SF V2DF])
+(define_mode_iterator VSX_M2 [V16QI
+ V8HI
+ V4SI
+ V2DI
+ V4SF
+ V2DF
+ (TI "TARGET_VSX_TIMODE")])
+
;; Map into the appropriate load/store name based on the type
(define_mode_attr VSm [(V16QI "vw4")
(V8HI "vw4")
@@ -1446,3 +1454,27 @@
}"
[(set_attr "length" "20")
(set_attr "type" "veccomplex")])
+
+
+;; Power8 Vector fusion. The fused ops must be physically adjacent.
+(define_peephole
+ [(set (match_operand:P 0 "base_reg_operand" "")
+ (match_operand:P 1 "short_cint_operand" ""))
+ (set (match_operand:VSX_M2 2 "vsx_register_operand" "")
+ (mem:VSX_M2 (plus:P (match_dup 0)
+ (match_operand:P 3 "int_reg_operand" ""))))]
+ "TARGET_P8_FUSION"
+ "li %0,%1\t\t\t# vector load fusion\;lx<VSX_M2:VSm>x %x2,%0,%3"
+ [(set_attr "length" "8")
+ (set_attr "type" "vecload")])
+
+(define_peephole
+ [(set (match_operand:P 0 "base_reg_operand" "")
+ (match_operand:P 1 "short_cint_operand" ""))
+ (set (match_operand:VSX_M2 2 "vsx_register_operand" "")
+ (mem:VSX_M2 (plus:P (match_operand:P 3 "int_reg_operand" "")
+ (match_dup 0))))]
+ "TARGET_P8_FUSION"
+ "li %0,%1\t\t\t# vector load fusion\;lx<VSX_M2:VSm>x %x2,%0,%3"
+ [(set_attr "length" "8")
+ (set_attr "type" "vecload")])