diff --git a/gcc/config/mips/mips-protos.h b/gcc/config/mips/mips-protos.h index b48e04f..244eb8d 100644 --- a/gcc/config/mips/mips-protos.h +++ b/gcc/config/mips/mips-protos.h @@ -360,6 +360,7 @@ extern bool mips_epilogue_uses (unsigned int); extern void mips_final_prescan_insn (rtx_insn *, rtx *, int); extern int mips_trampoline_code_size (void); extern void mips_function_profiler (FILE *); +extern bool mips_load_store_bonding_p (rtx *, machine_mode, bool); typedef rtx (*mulsidi3_gen_fn) (rtx, rtx, rtx); #ifdef RTX_CODE diff --git a/gcc/config/mips/mips.c b/gcc/config/mips/mips.c index bf69850..4fc15c4 100644 --- a/gcc/config/mips/mips.c +++ b/gcc/config/mips/mips.c @@ -18241,6 +18241,66 @@ umips_load_store_pair_p_1 (bool load_p, bool swap_p, return true; } +bool +mips_load_store_bonding_p (rtx *operands, machine_mode mode, bool load_p) +{ + rtx reg1, reg2, mem1, mem2, base1, base2; + enum reg_class rc1, rc2; + HOST_WIDE_INT offset1, offset2; + + if (load_p) + { + reg1 = operands[0]; + reg2 = operands[2]; + mem1 = operands[1]; + mem2 = operands[3]; + } + else + { + reg1 = operands[1]; + reg2 = operands[3]; + mem1 = operands[0]; + mem2 = operands[2]; + } + + if (mips_address_insns (XEXP (mem1, 0), mode, false) == 0 + || mips_address_insns (XEXP (mem2, 0), mode, false) == 0) + return false; + + mips_split_plus (XEXP (mem1, 0), &base1, &offset1); + mips_split_plus (XEXP (mem2, 0), &base2, &offset2); + + /* Base regs do not match. */ + if (!REG_P (base1) || !rtx_equal_p (base1, base2)) + return false; + + /* Either of the loads is clobbering base register. It is legitimate to bond + loads if second load clobbers base register. However, hardware does not + support such bonding. */ + if (load_p + && (REGNO (reg1) == REGNO (base1) + || (REGNO (reg2) == REGNO (base1)))) + return false; + + /* Loading in same registers. */ + if (load_p + && REGNO (reg1) == REGNO (reg2)) + return false; + + /* The loads/stores are not of same type. */ + rc1 = REGNO_REG_CLASS (REGNO (reg1)); + rc2 = REGNO_REG_CLASS (REGNO (reg2)); + if (rc1 != rc2 + && !reg_class_subset_p (rc1, rc2) + && !reg_class_subset_p (rc2, rc1)) + return false; + + if (abs (offset1 - offset2) != GET_MODE_SIZE (mode)) + return false; + + return true; +} + /* OPERANDS describes the operands to a pair of SETs, in the order dest1, src1, dest2, src2. Return true if the operands can be used in an LWP or SWP instruction; LOAD_P says which. */ diff --git a/gcc/config/mips/mips.h b/gcc/config/mips/mips.h index 4bd83f5..c9accd1 100644 --- a/gcc/config/mips/mips.h +++ b/gcc/config/mips/mips.h @@ -3162,3 +3162,10 @@ extern GTY(()) struct target_globals *mips16_globals; #define STANDARD_STARTFILE_PREFIX_1 "/lib64/" #define STANDARD_STARTFILE_PREFIX_2 "/usr/lib64/" #endif + +/* Load store bonding is not supported by micromips and fix_24k. The + performance can be degraded for those targets. Hence, do not bond for + micromips or fix_24k. */ +#define ENABLE_LD_ST_PAIRS \ + (TARGET_LOAD_STORE_PAIRS && TUNE_P5600 \ + && !TARGET_MICROMIPS && !TARGET_FIX_24K) diff --git a/gcc/config/mips/mips.md b/gcc/config/mips/mips.md index ed4c0ba..0e2b172 100644 --- a/gcc/config/mips/mips.md +++ b/gcc/config/mips/mips.md @@ -754,6 +754,11 @@ (define_mode_iterator MOVEP1 [SI SF]) (define_mode_iterator MOVEP2 [SI SF]) +(define_mode_iterator JOIN_MODE [HI + SI + (SF "TARGET_HARD_FLOAT") + (DF "TARGET_HARD_FLOAT + && TARGET_DOUBLE_FLOAT")]) ;; This mode iterator allows :HILO to be used as the mode of the ;; concatenated HI and LO registers. @@ -7407,6 +7412,112 @@ { return MIPS_CALL ("jal", operands, 0, -1); } [(set_attr "type" "call") (set_attr "insn_count" "3")]) + +;; Match paired HI/SI/SF/DFmode load/stores. +(define_insn "*join2_load_store" + [(set (match_operand:JOIN_MODE 0 "nonimmediate_operand" "=d,f,m,m") + (match_operand:JOIN_MODE 1 "nonimmediate_operand" "m,m,d,f")) + (set (match_operand:JOIN_MODE 2 "nonimmediate_operand" "=d,f,m,m") + (match_operand:JOIN_MODE 3 "nonimmediate_operand" "m,m,d,f"))] + "ENABLE_LD_ST_PAIRS && reload_completed" + { + bool load_p = (which_alternative == 0 || which_alternative == 1); + /* Reg-renaming pass reuses base register if it is dead after bonded loads. + Hardware does not bond those loads, even when they are consecutive. + However, order of the loads need to be checked for correctness. */ + if (!load_p || !reg_overlap_mentioned_p (operands[0], operands[1])) + { + output_asm_insn (mips_output_move (operands[0], operands[1]), + operands); + output_asm_insn (mips_output_move (operands[2], operands[3]), + &operands[2]); + } + else + { + output_asm_insn (mips_output_move (operands[2], operands[3]), + &operands[2]); + output_asm_insn (mips_output_move (operands[0], operands[1]), + operands); + } + return ""; + } + [(set_attr "move_type" "load,fpload,store,fpstore") + (set_attr "insn_count" "2,2,2,2")]) + +;; 2 HI/SI/SF/DF loads are joined. +;; P5600 does not support bonding of two LBs, hence QI mode is not included. +;; The loads must be non-volatile as they might be reordered at the time of asm +;; generation. +(define_peephole2 + [(set (match_operand:JOIN_MODE 0 "register_operand") + (match_operand:JOIN_MODE 1 "non_volatile_mem_operand")) + (set (match_operand:JOIN_MODE 2 "register_operand") + (match_operand:JOIN_MODE 3 "non_volatile_mem_operand"))] + "ENABLE_LD_ST_PAIRS + && mips_load_store_bonding_p (operands, mode, true)" + [(parallel [(set (match_dup 0) + (match_dup 1)) + (set (match_dup 2) + (match_dup 3))])] + "") + +;; 2 HI/SI/SF/DF stores are joined. +;; P5600 does not support bonding of two SBs, hence QI mode is not included. +(define_peephole2 + [(set (match_operand:JOIN_MODE 0 "memory_operand") + (match_operand:JOIN_MODE 1 "register_operand")) + (set (match_operand:JOIN_MODE 2 "memory_operand") + (match_operand:JOIN_MODE 3 "register_operand"))] + "ENABLE_LD_ST_PAIRS + && mips_load_store_bonding_p (operands, mode, false)" + [(parallel [(set (match_dup 0) + (match_dup 1)) + (set (match_dup 2) + (match_dup 3))])] + "") + +;; Match paired HImode loads. +(define_insn "*join2_loadhi" + [(set (match_operand:SI 0 "register_operand" "=r") + (any_extend:SI (match_operand:HI 1 "non_volatile_mem_operand" "m"))) + (set (match_operand:SI 2 "register_operand" "=r") + (any_extend:SI (match_operand:HI 3 "non_volatile_mem_operand" "m")))] + "ENABLE_LD_ST_PAIRS && reload_completed" + { + /* Reg-renaming pass reuses base register if it is dead after bonded loads. + Hardware does not bond those loads, even when they are consecutive. + However, order of the loads need to be checked for correctness. */ + if (!reg_overlap_mentioned_p (operands[0], operands[1])) + { + output_asm_insn ("lh\t%0,%1", operands); + output_asm_insn ("lh\t%2,%3", operands); + } + else + { + output_asm_insn ("lh\t%2,%3", operands); + output_asm_insn ("lh\t%0,%1", operands); + } + + return ""; + } + [(set_attr "move_type" "load") + (set_attr "insn_count" "2")]) + + +;; 2 HI loads are joined. +(define_peephole2 + [(set (match_operand:SI 0 "register_operand") + (any_extend:SI (match_operand:HI 1 "non_volatile_mem_operand"))) + (set (match_operand:SI 2 "register_operand") + (any_extend:SI (match_operand:HI 3 "non_volatile_mem_operand")))] + "ENABLE_LD_ST_PAIRS + && mips_load_store_bonding_p (operands, HImode, true)" + [(parallel [(set (match_dup 0) + (any_extend:SI (match_dup 1))) + (set (match_dup 2) + (any_extend:SI (match_dup 3)))])] + "") + ;; Synchronization instructions. diff --git a/gcc/config/mips/mips.opt b/gcc/config/mips/mips.opt index 9e89aa9..a9baebe 100644 --- a/gcc/config/mips/mips.opt +++ b/gcc/config/mips/mips.opt @@ -418,3 +418,7 @@ Enable use of odd-numbered single-precision registers noasmopt Driver + +mload-store-pairs +Target Report Var(TARGET_LOAD_STORE_PAIRS) Init(1) +Enable load/store bonding. diff --git a/gcc/testsuite/gcc.target/mips/p5600-bonding.c b/gcc/testsuite/gcc.target/mips/p5600-bonding.c new file mode 100644 index 0000000..122b9f8 --- /dev/null +++ b/gcc/testsuite/gcc.target/mips/p5600-bonding.c @@ -0,0 +1,19 @@ +/* { dg-do compile } */ +/* { dg-options "-dp -mtune=p5600 -mno-micromips -mno-mips16" } */ +/* { dg-skip-if "Bonding needs peephole optimization." { *-*-* } { "-O0" "-O1" } { "" } } */ +typedef int VINT32 __attribute__ ((vector_size((16)))); + +void memory_operation_fun2_si(void * __restrict src, void * __restrict dest, int num) +{ + VINT32 *vsrc = (VINT32 *)src; + VINT32 *vdest = (VINT32 *)dest; + int i; + + for (i = 0; i < num - 1; i+=2) + { + vdest[i] = (vdest[i] + vsrc[i]); + vdest[i + 1] = vdest[i + 1] + vsrc[i + 1]; + } +} +/* { dg-final { scan-assembler "join2_" } } */ +