[GCC14,v2] LoongArch: Optimize additions with immediates
Checks
Commit Message
1. Use addu16i.d for TARGET_64BIT and suitable immediates.
2. Split one addition with immediate into two addu16i.d or addi.{d/w}
instructions if possible. This can avoid using a temp register w/o
increase the count of instructions.
Inspired by https://reviews.llvm.org/D143710 and
https://reviews.llvm.org/D147222.
Stack check CFA tests are updated because this change somehow hides
PR109035, causing a smaller stack frame.
Bootstrapped and regtested on loongarch64-linux-gnu. Ok for GCC 14?
gcc/ChangeLog:
* config/loongarch/loongarch-protos.h
(loongarch_addu16i_imm12_operand_p): New function prototype.
(loongarch_split_plus_constant): Likewise.
* config/loongarch/loongarch.cc
(loongarch_addu16i_imm12_operand_p): New function.
(loongarch_split_plus_constant): Likewise.
* config/loongarch/loongarch.h (ADDU16I_OPERAND): New macro.
(DUAL_IMM12_OPERAND): Likewise.
(DUAL_ADDU16I_OPERAND): Likewise.
* config/loongarch/constraints.md (La, Lb, Lc, Ld, Le): New
constraint.
* config/loongarch/predicates.md (const_dual_imm12_operand): New
predicate.
(const_addu16i_operand): Likewise.
(const_addu16i_imm12_di_operand): Likewise.
(const_addu16i_imm12_si_operand): Likewise.
(plus_di_operand): Likewise.
(plus_si_operand): Likewise.
(plus_si_extend_operand): Likewise.
* config/loongarch/loongarch.md (add<mode>3): Convert to
define_insn_and_split. Use plus_<mode>_operand predicate
instead of arith_operand. Add alternatives for La, Lb, Lc, Ld,
and Le constraints.
(*addsi3_extended): Convert to define_insn_and_split. Use
plus_si_extend_operand instead of arith_operand. Add
alternatives for La and Le alternatives.
gcc/testsuite/ChangeLog:
* gcc.target/loongarch/add-const.c: New test.
* gcc.target/loongarch/stack-check-cfa-1.c: Adjust for stack
frame size change.
* gcc.target/loongarch/stack-check-cfa-2.c: Likewise.
---
gcc/config/loongarch/constraints.md | 46 ++++++++++++-
gcc/config/loongarch/loongarch-protos.h | 2 +
gcc/config/loongarch/loongarch.cc | 44 +++++++++++++
gcc/config/loongarch/loongarch.h | 19 ++++++
gcc/config/loongarch/loongarch.md | 66 +++++++++++++++----
gcc/config/loongarch/predicates.md | 36 ++++++++++
.../gcc.target/loongarch/add-const.c | 45 +++++++++++++
.../gcc.target/loongarch/stack-check-cfa-1.c | 2 +-
.../gcc.target/loongarch/stack-check-cfa-2.c | 2 +-
9 files changed, 246 insertions(+), 16 deletions(-)
create mode 100644 gcc/testsuite/gcc.target/loongarch/add-const.c
Comments
在 2023/4/4 下午4:38, Xi Ruoyao 写道:
> 1. Use addu16i.d for TARGET_64BIT and suitable immediates.
> 2. Split one addition with immediate into two addu16i.d or addi.{d/w}
> instructions if possible. This can avoid using a temp register w/o
> increase the count of instructions.
>
> Inspired by https://reviews.llvm.org/D143710 and
> https://reviews.llvm.org/D147222.
>
> Stack check CFA tests are updated because this change somehow hides
> PR109035, causing a smaller stack frame.
>
> Bootstrapped and regtested on loongarch64-linux-gnu. Ok for GCC 14?
I think there is no problem can be integrated into gcc14.
Thanks!
> gcc/ChangeLog:
>
> * config/loongarch/loongarch-protos.h
> (loongarch_addu16i_imm12_operand_p): New function prototype.
> (loongarch_split_plus_constant): Likewise.
> * config/loongarch/loongarch.cc
> (loongarch_addu16i_imm12_operand_p): New function.
> (loongarch_split_plus_constant): Likewise.
> * config/loongarch/loongarch.h (ADDU16I_OPERAND): New macro.
> (DUAL_IMM12_OPERAND): Likewise.
> (DUAL_ADDU16I_OPERAND): Likewise.
> * config/loongarch/constraints.md (La, Lb, Lc, Ld, Le): New
> constraint.
> * config/loongarch/predicates.md (const_dual_imm12_operand): New
> predicate.
> (const_addu16i_operand): Likewise.
> (const_addu16i_imm12_di_operand): Likewise.
> (const_addu16i_imm12_si_operand): Likewise.
> (plus_di_operand): Likewise.
> (plus_si_operand): Likewise.
> (plus_si_extend_operand): Likewise.
> * config/loongarch/loongarch.md (add<mode>3): Convert to
> define_insn_and_split. Use plus_<mode>_operand predicate
> instead of arith_operand. Add alternatives for La, Lb, Lc, Ld,
> and Le constraints.
> (*addsi3_extended): Convert to define_insn_and_split. Use
> plus_si_extend_operand instead of arith_operand. Add
> alternatives for La and Le alternatives.
>
> gcc/testsuite/ChangeLog:
>
> * gcc.target/loongarch/add-const.c: New test.
> * gcc.target/loongarch/stack-check-cfa-1.c: Adjust for stack
> frame size change.
> * gcc.target/loongarch/stack-check-cfa-2.c: Likewise.
> ---
> gcc/config/loongarch/constraints.md | 46 ++++++++++++-
> gcc/config/loongarch/loongarch-protos.h | 2 +
> gcc/config/loongarch/loongarch.cc | 44 +++++++++++++
> gcc/config/loongarch/loongarch.h | 19 ++++++
> gcc/config/loongarch/loongarch.md | 66 +++++++++++++++----
> gcc/config/loongarch/predicates.md | 36 ++++++++++
> .../gcc.target/loongarch/add-const.c | 45 +++++++++++++
> .../gcc.target/loongarch/stack-check-cfa-1.c | 2 +-
> .../gcc.target/loongarch/stack-check-cfa-2.c | 2 +-
> 9 files changed, 246 insertions(+), 16 deletions(-)
> create mode 100644 gcc/testsuite/gcc.target/loongarch/add-const.c
>
> diff --git a/gcc/config/loongarch/constraints.md b/gcc/config/loongarch/constraints.md
> index cb7fa688ceb..7a38cd07ae9 100644
> --- a/gcc/config/loongarch/constraints.md
> +++ b/gcc/config/loongarch/constraints.md
> @@ -60,7 +60,22 @@
> ;; "I" "A signed 12-bit constant (for arithmetic instructions)."
> ;; "J" "Integer zero."
> ;; "K" "An unsigned 12-bit constant (for logic instructions)."
> -;; "L" <-----unused
> +;; "L" -
> +;; "La"
> +;; "A signed constant in [-4096, 2048) or (2047, 4094]."
> +;; "Lb"
> +;; "A signed 32-bit constant and low 16-bit is zero, which can be
> +;; added onto a register with addu16i.d. It matches nothing if
> +;; the addu16i.d instruction is not available."
> +;; "Lc"
> +;; "A signed 64-bit constant can be expressed as Lb + I, but not a
> +;; single Lb or I."
> +;; "Ld"
> +;; "A signed 64-bit constant can be expressed as Lb + Lb, but not a
> +;; single Lb."
> +;; "Le"
> +;; "A signed 32-bit constant can be expressed as Lb + I, but not a
> +;; single Lb or I."
> ;; "M" <-----unused
> ;; "N" <-----unused
> ;; "O" <-----unused
> @@ -170,6 +185,35 @@ (define_constraint "K"
> (and (match_code "const_int")
> (match_test "IMM12_OPERAND_UNSIGNED (ival)")))
>
> +(define_constraint "La"
> + "A signed constant in [-4096, 2048) or (2047, 4094]."
> + (and (match_code "const_int")
> + (match_test "DUAL_IMM12_OPERAND (ival)")))
> +
> +(define_constraint "Lb"
> + "A signed 32-bit constant and low 16-bit is zero, which can be added
> + onto a register with addu16i.d."
> + (and (match_code "const_int")
> + (match_test "ADDU16I_OPERAND (ival)")))
> +
> +(define_constraint "Lc"
> + "A signed 64-bit constant can be expressed as Lb + I, but not a single Lb
> + or I."
> + (and (match_code "const_int")
> + (match_test "loongarch_addu16i_imm12_operand_p (ival, DImode)")))
> +
> +(define_constraint "Ld"
> + "A signed 64-bit constant can be expressed as Lb + Lb, but not a single
> + Lb."
> + (and (match_code "const_int")
> + (match_test "DUAL_ADDU16I_OPERAND (ival)")))
> +
> +(define_constraint "Le"
> + "A signed 32-bit constant can be expressed as Lb + I, but not a single Lb
> + or I."
> + (and (match_code "const_int")
> + (match_test "loongarch_addu16i_imm12_operand_p (ival, SImode)")))
> +
> (define_constraint "Yd"
> "@internal
> A constant @code{move_operand} that can be safely loaded using
> diff --git a/gcc/config/loongarch/loongarch-protos.h b/gcc/config/loongarch/loongarch-protos.h
> index 35cc77c7367..83df489c7a5 100644
> --- a/gcc/config/loongarch/loongarch-protos.h
> +++ b/gcc/config/loongarch/loongarch-protos.h
> @@ -83,6 +83,8 @@ extern rtx loongarch_legitimize_call_address (rtx);
> extern rtx loongarch_subword (rtx, bool);
> extern bool loongarch_split_move_p (rtx, rtx);
> extern void loongarch_split_move (rtx, rtx, rtx);
> +extern bool loongarch_addu16i_imm12_operand_p (HOST_WIDE_INT, machine_mode);
> +extern void loongarch_split_plus_constant (rtx *, machine_mode);
> extern const char *loongarch_output_move (rtx, rtx);
> extern bool loongarch_cfun_has_cprestore_slot_p (void);
> #ifdef RTX_CODE
> diff --git a/gcc/config/loongarch/loongarch.cc b/gcc/config/loongarch/loongarch.cc
> index 0ecb91ca997..dfb731fca9d 100644
> --- a/gcc/config/loongarch/loongarch.cc
> +++ b/gcc/config/loongarch/loongarch.cc
> @@ -3756,6 +3756,50 @@ loongarch_split_move (rtx dest, rtx src, rtx insn_)
> }
> }
>
> +/* Check if adding an integer constant value for a specific mode can be
> + performed with an addu16i.d instruction and an addi.{w/d}
> + instruction. */
> +
> +bool
> +loongarch_addu16i_imm12_operand_p (HOST_WIDE_INT value, machine_mode mode)
> +{
> + /* Not necessary, but avoid unnecessary calculation if !TARGET_64BIT. */
> + if (!TARGET_64BIT)
> + return false;
> +
> + if ((value & 0xffff) == 0)
> + return false;
> +
> + if (IMM12_OPERAND (value))
> + return false;
> +
> + value = (value & ~HWIT_UC_0xFFF) + ((value & 0x800) << 1);
> + return ADDU16I_OPERAND (trunc_int_for_mode (value, mode));
> +}
> +
> +/* Split one integer constant op[0] into two (op[1] and op[2]) for constant
> + plus operation in a specific mode. The splitted constants can be added
> + onto a register with a single instruction (addi.{d/w} or addu16i.d). */
> +
> +void
> +loongarch_split_plus_constant (rtx *op, machine_mode mode)
> +{
> + HOST_WIDE_INT v = INTVAL (op[0]), a;
> +
> + if (DUAL_IMM12_OPERAND (v))
> + a = (v > 0 ? 2047 : -2048);
> + else if (loongarch_addu16i_imm12_operand_p (v, mode))
> + a = (v & ~HWIT_UC_0xFFF) + ((v & 0x800) << 1);
> + else if (mode == DImode && DUAL_ADDU16I_OPERAND (v))
> + a = (v > 0 ? 0x7fff : -0x8000) << 16;
> + else
> + gcc_unreachable ();
> +
> + op[1] = gen_int_mode (a, mode);
> + v = v - (unsigned HOST_WIDE_INT) a;
> + op[2] = gen_int_mode (v, mode);
> +}
> +
> /* Return true if a move from SRC to DEST in INSN should be split. */
>
> static bool
> diff --git a/gcc/config/loongarch/loongarch.h b/gcc/config/loongarch/loongarch.h
> index f8167875646..7151d5cabb3 100644
> --- a/gcc/config/loongarch/loongarch.h
> +++ b/gcc/config/loongarch/loongarch.h
> @@ -612,6 +612,25 @@ enum reg_class
>
> #define CONST_LOW_PART(VALUE) ((VALUE) - CONST_HIGH_PART (VALUE))
>
> +/* True if VALUE can be added onto a register with one addu16i.d
> + instruction. */
> +
> +#define ADDU16I_OPERAND(VALUE) \
> + (TARGET_64BIT && (((VALUE) & 0xffff) == 0 \
> + && IMM16_OPERAND ((HOST_WIDE_INT) (VALUE) / 65536)))
> +
> +/* True if VALUE can be added onto a register with two addi.{d/w}
> + instructions, but not one addi.{d/w} instruction. */
> +#define DUAL_IMM12_OPERAND(VALUE) \
> + (IN_RANGE ((VALUE), -4096, 4094) && !IMM12_OPERAND (VALUE))
> +
> +/* True if VALUE can be added onto a register with two addu16i.d
> + instruction, but not one addu16i.d instruction. */
> +#define DUAL_ADDU16I_OPERAND(VALUE) \
> + (TARGET_64BIT && (((VALUE) & 0xffff) == 0 \
> + && !ADDU16I_OPERAND (VALUE) \
> + && IN_RANGE ((VALUE) / 65536, -0x10000, 0xfffe)))
> +
> #define IMM12_INT(X) IMM12_OPERAND (INTVAL (X))
> #define IMM12_INT_UNSIGNED(X) IMM12_OPERAND_UNSIGNED (INTVAL (X))
> #define LU12I_INT(X) LU12I_OPERAND (INTVAL (X))
> diff --git a/gcc/config/loongarch/loongarch.md b/gcc/config/loongarch/loongarch.md
> index 3509c3c21c1..628ecc78088 100644
> --- a/gcc/config/loongarch/loongarch.md
> +++ b/gcc/config/loongarch/loongarch.md
> @@ -598,24 +598,64 @@ (define_insn "add<mode>3"
> [(set_attr "type" "fadd")
> (set_attr "mode" "<UNITMODE>")])
>
> -(define_insn "add<mode>3"
> - [(set (match_operand:GPR 0 "register_operand" "=r,r")
> - (plus:GPR (match_operand:GPR 1 "register_operand" "r,r")
> - (match_operand:GPR 2 "arith_operand" "r,I")))]
> +(define_insn_and_split "add<mode>3"
> + [(set (match_operand:GPR 0 "register_operand" "=r,r,r,r,r,r,r")
> + (plus:GPR (match_operand:GPR 1 "register_operand" "r,r,r,r,r,r,r")
> + (match_operand:GPR 2 "plus_<mode>_operand"
> + "r,I,La,Lb,Lc,Ld,Le")))]
> ""
> - "add%i2.<d>\t%0,%1,%2";
> + "@
> + add.<d>\t%0,%1,%2
> + addi.<d>\t%0,%1,%2
> + #
> + * operands[2] = GEN_INT (INTVAL (operands[2]) / 65536); \
> + return \"addu16i.d\t%0,%1,%2\";
> + #
> + #
> + #"
> + "CONST_INT_P (operands[2]) && !IMM12_INT (operands[2]) \
> + && !ADDU16I_OPERAND (INTVAL (operands[2]))"
> + [(set (match_dup 0) (plus:GPR (match_dup 1) (match_dup 3)))
> + (set (match_dup 0) (plus:GPR (match_dup 0) (match_dup 4)))]
> + {
> + loongarch_split_plus_constant (&operands[2], <MODE>mode);
> + }
> [(set_attr "alu_type" "add")
> - (set_attr "mode" "<MODE>")])
> -
> -(define_insn "*addsi3_extended"
> - [(set (match_operand:DI 0 "register_operand" "=r,r")
> + (set_attr "mode" "<MODE>")
> + (set_attr "insn_count" "1,1,2,1,2,2,2")
> + (set (attr "enabled")
> + (cond
> + [(match_test "<MODE>mode != DImode && which_alternative == 4")
> + (const_string "no")
> + (match_test "<MODE>mode != DImode && which_alternative == 5")
> + (const_string "no")
> + (match_test "<MODE>mode != SImode && which_alternative == 6")
> + (const_string "no")]
> + (const_string "yes")))])
> +
> +(define_insn_and_split "*addsi3_extended"
> + [(set (match_operand:DI 0 "register_operand" "=r,r,r,r")
> (sign_extend:DI
> - (plus:SI (match_operand:SI 1 "register_operand" "r,r")
> - (match_operand:SI 2 "arith_operand" "r,I"))))]
> + (plus:SI (match_operand:SI 1 "register_operand" "r,r,r,r")
> + (match_operand:SI 2 "plus_si_extend_operand"
> + "r,I,La,Le"))))]
> "TARGET_64BIT"
> - "add%i2.w\t%0,%1,%2"
> + "@
> + add.w\t%0,%1,%2
> + addi.w\t%0,%1,%2
> + #
> + #"
> + "CONST_INT_P (operands[2]) && !IMM12_INT (operands[2])"
> + [(set (subreg:SI (match_dup 0) 0) (plus:SI (match_dup 1) (match_dup 3)))
> + (set (match_dup 0)
> + (sign_extend:DI (plus:SI (subreg:SI (match_dup 0) 0)
> + (match_dup 4))))]
> + {
> + loongarch_split_plus_constant (&operands[2], SImode);
> + }
> [(set_attr "alu_type" "add")
> - (set_attr "mode" "SI")])
> + (set_attr "mode" "SI")
> + (set_attr "insn_count" "1,1,2,2")])
>
>
> ;;
> diff --git a/gcc/config/loongarch/predicates.md b/gcc/config/loongarch/predicates.md
> index 95140280f1e..510973aa339 100644
> --- a/gcc/config/loongarch/predicates.md
> +++ b/gcc/config/loongarch/predicates.md
> @@ -39,14 +39,50 @@ (define_predicate "const_arith_operand"
> (and (match_code "const_int")
> (match_test "IMM12_OPERAND (INTVAL (op))")))
>
> +(define_predicate "const_dual_imm12_operand"
> + (and (match_code "const_int")
> + (match_test "DUAL_IMM12_OPERAND (INTVAL (op))")))
> +
> (define_predicate "const_imm16_operand"
> (and (match_code "const_int")
> (match_test "IMM16_OPERAND (INTVAL (op))")))
>
> +(define_predicate "const_addu16i_operand"
> + (and (match_code "const_int")
> + (match_test "ADDU16I_OPERAND (INTVAL (op))")))
> +
> +(define_predicate "const_addu16i_imm12_di_operand"
> + (and (match_code "const_int")
> + (match_test "loongarch_addu16i_imm12_operand_p (INTVAL (op), DImode)")))
> +
> +(define_predicate "const_addu16i_imm12_si_operand"
> + (and (match_code "const_int")
> + (match_test "loongarch_addu16i_imm12_operand_p (INTVAL (op), SImode)")))
> +
> +(define_predicate "const_dual_addu16i_operand"
> + (and (match_code "const_int")
> + (match_test "DUAL_ADDU16I_OPERAND (INTVAL (op))")))
> +
> (define_predicate "arith_operand"
> (ior (match_operand 0 "const_arith_operand")
> (match_operand 0 "register_operand")))
>
> +(define_predicate "plus_di_operand"
> + (ior (match_operand 0 "arith_operand")
> + (match_operand 0 "const_dual_imm12_operand")
> + (match_operand 0 "const_addu16i_operand")
> + (match_operand 0 "const_addu16i_imm12_di_operand")
> + (match_operand 0 "const_dual_addu16i_operand")))
> +
> +(define_predicate "plus_si_extend_operand"
> + (ior (match_operand 0 "arith_operand")
> + (match_operand 0 "const_dual_imm12_operand")
> + (match_operand 0 "const_addu16i_imm12_si_operand")))
> +
> +(define_predicate "plus_si_operand"
> + (ior (match_operand 0 "plus_si_extend_operand")
> + (match_operand 0 "const_addu16i_operand")))
> +
> (define_predicate "const_immalsl_operand"
> (and (match_code "const_int")
> (match_test "IN_RANGE (INTVAL (op), 1, 4)")))
> diff --git a/gcc/testsuite/gcc.target/loongarch/add-const.c b/gcc/testsuite/gcc.target/loongarch/add-const.c
> new file mode 100644
> index 00000000000..7b6a7cb92aa
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/loongarch/add-const.c
> @@ -0,0 +1,45 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O -mabi=lp64d" } */
> +
> +/* None of these functions should load the const operand into a temp
> + register. */
> +
> +/* { dg-final { scan-assembler-not "add\\.[dw]" } } */
> +
> +unsigned long f01 (unsigned long x) { return x + 1; }
> +unsigned long f02 (unsigned long x) { return x - 1; }
> +unsigned long f03 (unsigned long x) { return x + 2047; }
> +unsigned long f04 (unsigned long x) { return x + 4094; }
> +unsigned long f05 (unsigned long x) { return x - 2048; }
> +unsigned long f06 (unsigned long x) { return x - 4096; }
> +unsigned long f07 (unsigned long x) { return x + 0x7fff0000; }
> +unsigned long f08 (unsigned long x) { return x - 0x80000000l; }
> +unsigned long f09 (unsigned long x) { return x + 0x7fff0000l * 2; }
> +unsigned long f10 (unsigned long x) { return x - 0x80000000l * 2; }
> +unsigned long f11 (unsigned long x) { return x + 0x7fff0000 + 0x1; }
> +unsigned long f12 (unsigned long x) { return x + 0x7fff0000 - 0x1; }
> +unsigned long f13 (unsigned long x) { return x + 0x7fff0000 + 0x7ff; }
> +unsigned long f14 (unsigned long x) { return x + 0x7fff0000 - 0x800; }
> +unsigned long f15 (unsigned long x) { return x - 0x80000000l - 1; }
> +unsigned long f16 (unsigned long x) { return x - 0x80000000l + 1; }
> +unsigned long f17 (unsigned long x) { return x - 0x80000000l - 0x800; }
> +unsigned long f18 (unsigned long x) { return x - 0x80000000l + 0x7ff; }
> +
> +unsigned int g01 (unsigned int x) { return x + 1; }
> +unsigned int g02 (unsigned int x) { return x - 1; }
> +unsigned int g03 (unsigned int x) { return x + 2047; }
> +unsigned int g04 (unsigned int x) { return x + 4094; }
> +unsigned int g05 (unsigned int x) { return x - 2048; }
> +unsigned int g06 (unsigned int x) { return x - 4096; }
> +unsigned int g07 (unsigned int x) { return x + 0x7fff0000; }
> +unsigned int g08 (unsigned int x) { return x - 0x80000000l; }
> +unsigned int g09 (unsigned int x) { return x + 0x7fff0000l * 2; }
> +unsigned int g10 (unsigned int x) { return x - 0x80000000l * 2; }
> +unsigned int g11 (unsigned int x) { return x + 0x7fff0000 + 0x1; }
> +unsigned int g12 (unsigned int x) { return x + 0x7fff0000 - 0x1; }
> +unsigned int g13 (unsigned int x) { return x + 0x7fff0000 + 0x7ff; }
> +unsigned int g14 (unsigned int x) { return x + 0x7fff0000 - 0x800; }
> +unsigned int g15 (unsigned int x) { return x - 0x80000000l - 1; }
> +unsigned int g16 (unsigned int x) { return x - 0x80000000l + 1; }
> +unsigned int g17 (unsigned int x) { return x - 0x80000000l - 0x800; }
> +unsigned int g18 (unsigned int x) { return x - 0x80000000l + 0x7ff; }
> diff --git a/gcc/testsuite/gcc.target/loongarch/stack-check-cfa-1.c b/gcc/testsuite/gcc.target/loongarch/stack-check-cfa-1.c
> index 3533fe7b685..cd72154f46c 100644
> --- a/gcc/testsuite/gcc.target/loongarch/stack-check-cfa-1.c
> +++ b/gcc/testsuite/gcc.target/loongarch/stack-check-cfa-1.c
> @@ -6,7 +6,7 @@
> #define SIZE 128*1024
> #include "stack-check-prologue.h"
>
> -/* { dg-final { scan-assembler-times {\.cfi_def_cfa_offset 131088} 1 } } */
> +/* { dg-final { scan-assembler-times {\.cfi_def_cfa_offset 131072} 1 } } */
> /* { dg-final { scan-assembler-times {\.cfi_def_cfa_offset 0} 1 } } */
>
> /* Checks that the CFA notes are correct for every sp adjustment. */
> diff --git a/gcc/testsuite/gcc.target/loongarch/stack-check-cfa-2.c b/gcc/testsuite/gcc.target/loongarch/stack-check-cfa-2.c
> index e5e711105ac..3e5ca05b2da 100644
> --- a/gcc/testsuite/gcc.target/loongarch/stack-check-cfa-2.c
> +++ b/gcc/testsuite/gcc.target/loongarch/stack-check-cfa-2.c
> @@ -6,7 +6,7 @@
> #define SIZE 1280*1024 + 512
> #include "stack-check-prologue.h"
>
> -/* { dg-final { scan-assembler-times {\.cfi_def_cfa_offset 1311248} 1 } } */
> +/* { dg-final { scan-assembler-times {\.cfi_def_cfa_offset 1311232} 1 } } */
> /* { dg-final { scan-assembler-times {\.cfi_def_cfa_offset 0} 1 } } */
>
> /* Checks that the CFA notes are correct for every sp adjustment. */
Pushed r14-19.
On Tue, 2023-04-04 at 17:09 +0800, Lulu Cheng wrote:
>
> 在 2023/4/4 下午4:38, Xi Ruoyao 写道:
> > 1. Use addu16i.d for TARGET_64BIT and suitable immediates.
> > 2. Split one addition with immediate into two addu16i.d or
> > addi.{d/w}
> > instructions if possible. This can avoid using a temp register
> > w/o
> > increase the count of instructions.
> >
> > Inspired by https://reviews.llvm.org/D143710 and
> > https://reviews.llvm.org/D147222.
> >
> > Stack check CFA tests are updated because this change somehow hides
> > PR109035, causing a smaller stack frame.
> >
> > Bootstrapped and regtested on loongarch64-linux-gnu. Ok for GCC 14?
>
> I think there is no problem can be integrated into gcc14.
>
> Thanks!
>
> > gcc/ChangeLog:
> >
> > * config/loongarch/loongarch-protos.h
> > (loongarch_addu16i_imm12_operand_p): New function prototype.
> > (loongarch_split_plus_constant): Likewise.
> > * config/loongarch/loongarch.cc
> > (loongarch_addu16i_imm12_operand_p): New function.
> > (loongarch_split_plus_constant): Likewise.
> > * config/loongarch/loongarch.h (ADDU16I_OPERAND): New macro.
> > (DUAL_IMM12_OPERAND): Likewise.
> > (DUAL_ADDU16I_OPERAND): Likewise.
> > * config/loongarch/constraints.md (La, Lb, Lc, Ld, Le): New
> > constraint.
> > * config/loongarch/predicates.md (const_dual_imm12_operand):
> > New
> > predicate.
> > (const_addu16i_operand): Likewise.
> > (const_addu16i_imm12_di_operand): Likewise.
> > (const_addu16i_imm12_si_operand): Likewise.
> > (plus_di_operand): Likewise.
> > (plus_si_operand): Likewise.
> > (plus_si_extend_operand): Likewise.
> > * config/loongarch/loongarch.md (add<mode>3): Convert to
> > define_insn_and_split. Use plus_<mode>_operand predicate
> > instead of arith_operand. Add alternatives for La, Lb, Lc,
> > Ld,
> > and Le constraints.
> > (*addsi3_extended): Convert to define_insn_and_split. Use
> > plus_si_extend_operand instead of arith_operand. Add
> > alternatives for La and Le alternatives.
> >
> > gcc/testsuite/ChangeLog:
> >
> > * gcc.target/loongarch/add-const.c: New test.
> > * gcc.target/loongarch/stack-check-cfa-1.c: Adjust for stack
> > frame size change.
> > * gcc.target/loongarch/stack-check-cfa-2.c: Likewise.
> > ---
> > gcc/config/loongarch/constraints.md | 46 ++++++++++++-
> > gcc/config/loongarch/loongarch-protos.h | 2 +
> > gcc/config/loongarch/loongarch.cc | 44 +++++++++++++
> > gcc/config/loongarch/loongarch.h | 19 ++++++
> > gcc/config/loongarch/loongarch.md | 66
> > +++++++++++++++----
> > gcc/config/loongarch/predicates.md | 36 ++++++++++
> > .../gcc.target/loongarch/add-const.c | 45 +++++++++++++
> > .../gcc.target/loongarch/stack-check-cfa-1.c | 2 +-
> > .../gcc.target/loongarch/stack-check-cfa-2.c | 2 +-
> > 9 files changed, 246 insertions(+), 16 deletions(-)
> > create mode 100644 gcc/testsuite/gcc.target/loongarch/add-const.c
> >
> > diff --git a/gcc/config/loongarch/constraints.md
> > b/gcc/config/loongarch/constraints.md
> > index cb7fa688ceb..7a38cd07ae9 100644
> > --- a/gcc/config/loongarch/constraints.md
> > +++ b/gcc/config/loongarch/constraints.md
> > @@ -60,7 +60,22 @@
> > ;; "I" "A signed 12-bit constant (for arithmetic instructions)."
> > ;; "J" "Integer zero."
> > ;; "K" "An unsigned 12-bit constant (for logic instructions)."
> > -;; "L" <-----unused
> > +;; "L" -
> > +;; "La"
> > +;; "A signed constant in [-4096, 2048) or (2047, 4094]."
> > +;; "Lb"
> > +;; "A signed 32-bit constant and low 16-bit is zero, which can
> > be
> > +;; added onto a register with addu16i.d. It matches nothing
> > if
> > +;; the addu16i.d instruction is not available."
> > +;; "Lc"
> > +;; "A signed 64-bit constant can be expressed as Lb + I, but
> > not a
> > +;; single Lb or I."
> > +;; "Ld"
> > +;; "A signed 64-bit constant can be expressed as Lb + Lb, but
> > not a
> > +;; single Lb."
> > +;; "Le"
> > +;; "A signed 32-bit constant can be expressed as Lb + I, but
> > not a
> > +;; single Lb or I."
> > ;; "M" <-----unused
> > ;; "N" <-----unused
> > ;; "O" <-----unused
> > @@ -170,6 +185,35 @@ (define_constraint "K"
> > (and (match_code "const_int")
> > (match_test "IMM12_OPERAND_UNSIGNED (ival)")))
> >
> > +(define_constraint "La"
> > + "A signed constant in [-4096, 2048) or (2047, 4094]."
> > + (and (match_code "const_int")
> > + (match_test "DUAL_IMM12_OPERAND (ival)")))
> > +
> > +(define_constraint "Lb"
> > + "A signed 32-bit constant and low 16-bit is zero, which can be
> > added
> > + onto a register with addu16i.d."
> > + (and (match_code "const_int")
> > + (match_test "ADDU16I_OPERAND (ival)")))
> > +
> > +(define_constraint "Lc"
> > + "A signed 64-bit constant can be expressed as Lb + I, but not a
> > single Lb
> > + or I."
> > + (and (match_code "const_int")
> > + (match_test "loongarch_addu16i_imm12_operand_p (ival,
> > DImode)")))
> > +
> > +(define_constraint "Ld"
> > + "A signed 64-bit constant can be expressed as Lb + Lb, but not a
> > single
> > + Lb."
> > + (and (match_code "const_int")
> > + (match_test "DUAL_ADDU16I_OPERAND (ival)")))
> > +
> > +(define_constraint "Le"
> > + "A signed 32-bit constant can be expressed as Lb + I, but not a
> > single Lb
> > + or I."
> > + (and (match_code "const_int")
> > + (match_test "loongarch_addu16i_imm12_operand_p (ival,
> > SImode)")))
> > +
> > (define_constraint "Yd"
> > "@internal
> > A constant @code{move_operand} that can be safely loaded using
> > diff --git a/gcc/config/loongarch/loongarch-protos.h
> > b/gcc/config/loongarch/loongarch-protos.h
> > index 35cc77c7367..83df489c7a5 100644
> > --- a/gcc/config/loongarch/loongarch-protos.h
> > +++ b/gcc/config/loongarch/loongarch-protos.h
> > @@ -83,6 +83,8 @@ extern rtx loongarch_legitimize_call_address
> > (rtx);
> > extern rtx loongarch_subword (rtx, bool);
> > extern bool loongarch_split_move_p (rtx, rtx);
> > extern void loongarch_split_move (rtx, rtx, rtx);
> > +extern bool loongarch_addu16i_imm12_operand_p (HOST_WIDE_INT,
> > machine_mode);
> > +extern void loongarch_split_plus_constant (rtx *, machine_mode);
> > extern const char *loongarch_output_move (rtx, rtx);
> > extern bool loongarch_cfun_has_cprestore_slot_p (void);
> > #ifdef RTX_CODE
> > diff --git a/gcc/config/loongarch/loongarch.cc
> > b/gcc/config/loongarch/loongarch.cc
> > index 0ecb91ca997..dfb731fca9d 100644
> > --- a/gcc/config/loongarch/loongarch.cc
> > +++ b/gcc/config/loongarch/loongarch.cc
> > @@ -3756,6 +3756,50 @@ loongarch_split_move (rtx dest, rtx src, rtx
> > insn_)
> > }
> > }
> >
> > +/* Check if adding an integer constant value for a specific mode
> > can be
> > + performed with an addu16i.d instruction and an addi.{w/d}
> > + instruction. */
> > +
> > +bool
> > +loongarch_addu16i_imm12_operand_p (HOST_WIDE_INT value,
> > machine_mode mode)
> > +{
> > + /* Not necessary, but avoid unnecessary calculation if
> > !TARGET_64BIT. */
> > + if (!TARGET_64BIT)
> > + return false;
> > +
> > + if ((value & 0xffff) == 0)
> > + return false;
> > +
> > + if (IMM12_OPERAND (value))
> > + return false;
> > +
> > + value = (value & ~HWIT_UC_0xFFF) + ((value & 0x800) << 1);
> > + return ADDU16I_OPERAND (trunc_int_for_mode (value, mode));
> > +}
> > +
> > +/* Split one integer constant op[0] into two (op[1] and op[2]) for
> > constant
> > + plus operation in a specific mode. The splitted constants can
> > be added
> > + onto a register with a single instruction (addi.{d/w} or
> > addu16i.d). */
> > +
> > +void
> > +loongarch_split_plus_constant (rtx *op, machine_mode mode)
> > +{
> > + HOST_WIDE_INT v = INTVAL (op[0]), a;
> > +
> > + if (DUAL_IMM12_OPERAND (v))
> > + a = (v > 0 ? 2047 : -2048);
> > + else if (loongarch_addu16i_imm12_operand_p (v, mode))
> > + a = (v & ~HWIT_UC_0xFFF) + ((v & 0x800) << 1);
> > + else if (mode == DImode && DUAL_ADDU16I_OPERAND (v))
> > + a = (v > 0 ? 0x7fff : -0x8000) << 16;
> > + else
> > + gcc_unreachable ();
> > +
> > + op[1] = gen_int_mode (a, mode);
> > + v = v - (unsigned HOST_WIDE_INT) a;
> > + op[2] = gen_int_mode (v, mode);
> > +}
> > +
> > /* Return true if a move from SRC to DEST in INSN should be
> > split. */
> >
> > static bool
> > diff --git a/gcc/config/loongarch/loongarch.h
> > b/gcc/config/loongarch/loongarch.h
> > index f8167875646..7151d5cabb3 100644
> > --- a/gcc/config/loongarch/loongarch.h
> > +++ b/gcc/config/loongarch/loongarch.h
> > @@ -612,6 +612,25 @@ enum reg_class
> >
> > #define CONST_LOW_PART(VALUE) ((VALUE) - CONST_HIGH_PART (VALUE))
> >
> > +/* True if VALUE can be added onto a register with one addu16i.d
> > + instruction. */
> > +
> > +#define ADDU16I_OPERAND(VALUE) \
> > + (TARGET_64BIT && (((VALUE) & 0xffff) == 0 \
> > + && IMM16_OPERAND ((HOST_WIDE_INT) (VALUE) / 65536)))
> > +
> > +/* True if VALUE can be added onto a register with two addi.{d/w}
> > + instructions, but not one addi.{d/w} instruction. */
> > +#define DUAL_IMM12_OPERAND(VALUE) \
> > + (IN_RANGE ((VALUE), -4096, 4094) && !IMM12_OPERAND (VALUE))
> > +
> > +/* True if VALUE can be added onto a register with two addu16i.d
> > + instruction, but not one addu16i.d instruction. */
> > +#define DUAL_ADDU16I_OPERAND(VALUE) \
> > + (TARGET_64BIT && (((VALUE) & 0xffff) == 0 \
> > + && !ADDU16I_OPERAND (VALUE) \
> > + && IN_RANGE ((VALUE) / 65536, -0x10000, 0xfffe)))
> > +
> > #define IMM12_INT(X) IMM12_OPERAND (INTVAL (X))
> > #define IMM12_INT_UNSIGNED(X) IMM12_OPERAND_UNSIGNED (INTVAL (X))
> > #define LU12I_INT(X) LU12I_OPERAND (INTVAL (X))
> > diff --git a/gcc/config/loongarch/loongarch.md
> > b/gcc/config/loongarch/loongarch.md
> > index 3509c3c21c1..628ecc78088 100644
> > --- a/gcc/config/loongarch/loongarch.md
> > +++ b/gcc/config/loongarch/loongarch.md
> > @@ -598,24 +598,64 @@ (define_insn "add<mode>3"
> > [(set_attr "type" "fadd")
> > (set_attr "mode" "<UNITMODE>")])
> >
> > -(define_insn "add<mode>3"
> > - [(set (match_operand:GPR 0 "register_operand" "=r,r")
> > - (plus:GPR (match_operand:GPR 1 "register_operand" "r,r")
> > - (match_operand:GPR 2 "arith_operand" "r,I")))]
> > +(define_insn_and_split "add<mode>3"
> > + [(set (match_operand:GPR 0 "register_operand" "=r,r,r,r,r,r,r")
> > + (plus:GPR (match_operand:GPR 1 "register_operand"
> > "r,r,r,r,r,r,r")
> > + (match_operand:GPR 2 "plus_<mode>_operand"
> > + "r,I,La,Lb,Lc,Ld,Le")))]
> > ""
> > - "add%i2.<d>\t%0,%1,%2";
> > + "@
> > + add.<d>\t%0,%1,%2
> > + addi.<d>\t%0,%1,%2
> > + #
> > + * operands[2] = GEN_INT (INTVAL (operands[2]) / 65536); \
> > + return \"addu16i.d\t%0,%1,%2\";
> > + #
> > + #
> > + #"
> > + "CONST_INT_P (operands[2]) && !IMM12_INT (operands[2]) \
> > + && !ADDU16I_OPERAND (INTVAL (operands[2]))"
> > + [(set (match_dup 0) (plus:GPR (match_dup 1) (match_dup 3)))
> > + (set (match_dup 0) (plus:GPR (match_dup 0) (match_dup 4)))]
> > + {
> > + loongarch_split_plus_constant (&operands[2], <MODE>mode);
> > + }
> > [(set_attr "alu_type" "add")
> > - (set_attr "mode" "<MODE>")])
> > -
> > -(define_insn "*addsi3_extended"
> > - [(set (match_operand:DI 0 "register_operand" "=r,r")
> > + (set_attr "mode" "<MODE>")
> > + (set_attr "insn_count" "1,1,2,1,2,2,2")
> > + (set (attr "enabled")
> > + (cond
> > + [(match_test "<MODE>mode != DImode && which_alternative ==
> > 4")
> > + (const_string "no")
> > + (match_test "<MODE>mode != DImode && which_alternative ==
> > 5")
> > + (const_string "no")
> > + (match_test "<MODE>mode != SImode && which_alternative ==
> > 6")
> > + (const_string "no")]
> > + (const_string "yes")))])
> > +
> > +(define_insn_and_split "*addsi3_extended"
> > + [(set (match_operand:DI 0 "register_operand" "=r,r,r,r")
> > (sign_extend:DI
> > - (plus:SI (match_operand:SI 1 "register_operand" "r,r")
> > - (match_operand:SI 2 "arith_operand" "r,I"))))]
> > + (plus:SI (match_operand:SI 1 "register_operand"
> > "r,r,r,r")
> > + (match_operand:SI 2 "plus_si_extend_operand"
> > + "r,I,La,Le"))))]
> > "TARGET_64BIT"
> > - "add%i2.w\t%0,%1,%2"
> > + "@
> > + add.w\t%0,%1,%2
> > + addi.w\t%0,%1,%2
> > + #
> > + #"
> > + "CONST_INT_P (operands[2]) && !IMM12_INT (operands[2])"
> > + [(set (subreg:SI (match_dup 0) 0) (plus:SI (match_dup 1)
> > (match_dup 3)))
> > + (set (match_dup 0)
> > + (sign_extend:DI (plus:SI (subreg:SI (match_dup 0) 0)
> > + (match_dup 4))))]
> > + {
> > + loongarch_split_plus_constant (&operands[2], SImode);
> > + }
> > [(set_attr "alu_type" "add")
> > - (set_attr "mode" "SI")])
> > + (set_attr "mode" "SI")
> > + (set_attr "insn_count" "1,1,2,2")])
> >
> >
> > ;;
> > diff --git a/gcc/config/loongarch/predicates.md
> > b/gcc/config/loongarch/predicates.md
> > index 95140280f1e..510973aa339 100644
> > --- a/gcc/config/loongarch/predicates.md
> > +++ b/gcc/config/loongarch/predicates.md
> > @@ -39,14 +39,50 @@ (define_predicate "const_arith_operand"
> > (and (match_code "const_int")
> > (match_test "IMM12_OPERAND (INTVAL (op))")))
> >
> > +(define_predicate "const_dual_imm12_operand"
> > + (and (match_code "const_int")
> > + (match_test "DUAL_IMM12_OPERAND (INTVAL (op))")))
> > +
> > (define_predicate "const_imm16_operand"
> > (and (match_code "const_int")
> > (match_test "IMM16_OPERAND (INTVAL (op))")))
> >
> > +(define_predicate "const_addu16i_operand"
> > + (and (match_code "const_int")
> > + (match_test "ADDU16I_OPERAND (INTVAL (op))")))
> > +
> > +(define_predicate "const_addu16i_imm12_di_operand"
> > + (and (match_code "const_int")
> > + (match_test "loongarch_addu16i_imm12_operand_p (INTVAL (op),
> > DImode)")))
> > +
> > +(define_predicate "const_addu16i_imm12_si_operand"
> > + (and (match_code "const_int")
> > + (match_test "loongarch_addu16i_imm12_operand_p (INTVAL (op),
> > SImode)")))
> > +
> > +(define_predicate "const_dual_addu16i_operand"
> > + (and (match_code "const_int")
> > + (match_test "DUAL_ADDU16I_OPERAND (INTVAL (op))")))
> > +
> > (define_predicate "arith_operand"
> > (ior (match_operand 0 "const_arith_operand")
> > (match_operand 0 "register_operand")))
> >
> > +(define_predicate "plus_di_operand"
> > + (ior (match_operand 0 "arith_operand")
> > + (match_operand 0 "const_dual_imm12_operand")
> > + (match_operand 0 "const_addu16i_operand")
> > + (match_operand 0 "const_addu16i_imm12_di_operand")
> > + (match_operand 0 "const_dual_addu16i_operand")))
> > +
> > +(define_predicate "plus_si_extend_operand"
> > + (ior (match_operand 0 "arith_operand")
> > + (match_operand 0 "const_dual_imm12_operand")
> > + (match_operand 0 "const_addu16i_imm12_si_operand")))
> > +
> > +(define_predicate "plus_si_operand"
> > + (ior (match_operand 0 "plus_si_extend_operand")
> > + (match_operand 0 "const_addu16i_operand")))
> > +
> > (define_predicate "const_immalsl_operand"
> > (and (match_code "const_int")
> > (match_test "IN_RANGE (INTVAL (op), 1, 4)")))
> > diff --git a/gcc/testsuite/gcc.target/loongarch/add-const.c
> > b/gcc/testsuite/gcc.target/loongarch/add-const.c
> > new file mode 100644
> > index 00000000000..7b6a7cb92aa
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/loongarch/add-const.c
> > @@ -0,0 +1,45 @@
> > +/* { dg-do compile } */
> > +/* { dg-options "-O -mabi=lp64d" } */
> > +
> > +/* None of these functions should load the const operand into a
> > temp
> > + register. */
> > +
> > +/* { dg-final { scan-assembler-not "add\\.[dw]" } } */
> > +
> > +unsigned long f01 (unsigned long x) { return x + 1; }
> > +unsigned long f02 (unsigned long x) { return x - 1; }
> > +unsigned long f03 (unsigned long x) { return x + 2047; }
> > +unsigned long f04 (unsigned long x) { return x + 4094; }
> > +unsigned long f05 (unsigned long x) { return x - 2048; }
> > +unsigned long f06 (unsigned long x) { return x - 4096; }
> > +unsigned long f07 (unsigned long x) { return x + 0x7fff0000; }
> > +unsigned long f08 (unsigned long x) { return x - 0x80000000l; }
> > +unsigned long f09 (unsigned long x) { return x + 0x7fff0000l * 2; }
> > +unsigned long f10 (unsigned long x) { return x - 0x80000000l * 2; }
> > +unsigned long f11 (unsigned long x) { return x + 0x7fff0000 + 0x1;
> > }
> > +unsigned long f12 (unsigned long x) { return x + 0x7fff0000 - 0x1;
> > }
> > +unsigned long f13 (unsigned long x) { return x + 0x7fff0000 +
> > 0x7ff; }
> > +unsigned long f14 (unsigned long x) { return x + 0x7fff0000 -
> > 0x800; }
> > +unsigned long f15 (unsigned long x) { return x - 0x80000000l - 1; }
> > +unsigned long f16 (unsigned long x) { return x - 0x80000000l + 1; }
> > +unsigned long f17 (unsigned long x) { return x - 0x80000000l -
> > 0x800; }
> > +unsigned long f18 (unsigned long x) { return x - 0x80000000l +
> > 0x7ff; }
> > +
> > +unsigned int g01 (unsigned int x) { return x + 1; }
> > +unsigned int g02 (unsigned int x) { return x - 1; }
> > +unsigned int g03 (unsigned int x) { return x + 2047; }
> > +unsigned int g04 (unsigned int x) { return x + 4094; }
> > +unsigned int g05 (unsigned int x) { return x - 2048; }
> > +unsigned int g06 (unsigned int x) { return x - 4096; }
> > +unsigned int g07 (unsigned int x) { return x + 0x7fff0000; }
> > +unsigned int g08 (unsigned int x) { return x - 0x80000000l; }
> > +unsigned int g09 (unsigned int x) { return x + 0x7fff0000l * 2; }
> > +unsigned int g10 (unsigned int x) { return x - 0x80000000l * 2; }
> > +unsigned int g11 (unsigned int x) { return x + 0x7fff0000 + 0x1; }
> > +unsigned int g12 (unsigned int x) { return x + 0x7fff0000 - 0x1; }
> > +unsigned int g13 (unsigned int x) { return x + 0x7fff0000 + 0x7ff;
> > }
> > +unsigned int g14 (unsigned int x) { return x + 0x7fff0000 - 0x800;
> > }
> > +unsigned int g15 (unsigned int x) { return x - 0x80000000l - 1; }
> > +unsigned int g16 (unsigned int x) { return x - 0x80000000l + 1; }
> > +unsigned int g17 (unsigned int x) { return x - 0x80000000l - 0x800;
> > }
> > +unsigned int g18 (unsigned int x) { return x - 0x80000000l + 0x7ff;
> > }
> > diff --git a/gcc/testsuite/gcc.target/loongarch/stack-check-cfa-1.c
> > b/gcc/testsuite/gcc.target/loongarch/stack-check-cfa-1.c
> > index 3533fe7b685..cd72154f46c 100644
> > --- a/gcc/testsuite/gcc.target/loongarch/stack-check-cfa-1.c
> > +++ b/gcc/testsuite/gcc.target/loongarch/stack-check-cfa-1.c
> > @@ -6,7 +6,7 @@
> > #define SIZE 128*1024
> > #include "stack-check-prologue.h"
> >
> > -/* { dg-final { scan-assembler-times {\.cfi_def_cfa_offset 131088}
> > 1 } } */
> > +/* { dg-final { scan-assembler-times {\.cfi_def_cfa_offset 131072}
> > 1 } } */
> > /* { dg-final { scan-assembler-times {\.cfi_def_cfa_offset 0} 1 }
> > } */
> >
> > /* Checks that the CFA notes are correct for every sp adjustment.
> > */
> > diff --git a/gcc/testsuite/gcc.target/loongarch/stack-check-cfa-2.c
> > b/gcc/testsuite/gcc.target/loongarch/stack-check-cfa-2.c
> > index e5e711105ac..3e5ca05b2da 100644
> > --- a/gcc/testsuite/gcc.target/loongarch/stack-check-cfa-2.c
> > +++ b/gcc/testsuite/gcc.target/loongarch/stack-check-cfa-2.c
> > @@ -6,7 +6,7 @@
> > #define SIZE 1280*1024 + 512
> > #include "stack-check-prologue.h"
> >
> > -/* { dg-final { scan-assembler-times {\.cfi_def_cfa_offset 1311248}
> > 1 } } */
> > +/* { dg-final { scan-assembler-times {\.cfi_def_cfa_offset 1311232}
> > 1 } } */
> > /* { dg-final { scan-assembler-times {\.cfi_def_cfa_offset 0} 1 }
> > } */
> >
> > /* Checks that the CFA notes are correct for every sp adjustment.
> > */
>
@@ -60,7 +60,22 @@
;; "I" "A signed 12-bit constant (for arithmetic instructions)."
;; "J" "Integer zero."
;; "K" "An unsigned 12-bit constant (for logic instructions)."
-;; "L" <-----unused
+;; "L" -
+;; "La"
+;; "A signed constant in [-4096, 2048) or (2047, 4094]."
+;; "Lb"
+;; "A signed 32-bit constant and low 16-bit is zero, which can be
+;; added onto a register with addu16i.d. It matches nothing if
+;; the addu16i.d instruction is not available."
+;; "Lc"
+;; "A signed 64-bit constant can be expressed as Lb + I, but not a
+;; single Lb or I."
+;; "Ld"
+;; "A signed 64-bit constant can be expressed as Lb + Lb, but not a
+;; single Lb."
+;; "Le"
+;; "A signed 32-bit constant can be expressed as Lb + I, but not a
+;; single Lb or I."
;; "M" <-----unused
;; "N" <-----unused
;; "O" <-----unused
@@ -170,6 +185,35 @@ (define_constraint "K"
(and (match_code "const_int")
(match_test "IMM12_OPERAND_UNSIGNED (ival)")))
+(define_constraint "La"
+ "A signed constant in [-4096, 2048) or (2047, 4094]."
+ (and (match_code "const_int")
+ (match_test "DUAL_IMM12_OPERAND (ival)")))
+
+(define_constraint "Lb"
+ "A signed 32-bit constant and low 16-bit is zero, which can be added
+ onto a register with addu16i.d."
+ (and (match_code "const_int")
+ (match_test "ADDU16I_OPERAND (ival)")))
+
+(define_constraint "Lc"
+ "A signed 64-bit constant can be expressed as Lb + I, but not a single Lb
+ or I."
+ (and (match_code "const_int")
+ (match_test "loongarch_addu16i_imm12_operand_p (ival, DImode)")))
+
+(define_constraint "Ld"
+ "A signed 64-bit constant can be expressed as Lb + Lb, but not a single
+ Lb."
+ (and (match_code "const_int")
+ (match_test "DUAL_ADDU16I_OPERAND (ival)")))
+
+(define_constraint "Le"
+ "A signed 32-bit constant can be expressed as Lb + I, but not a single Lb
+ or I."
+ (and (match_code "const_int")
+ (match_test "loongarch_addu16i_imm12_operand_p (ival, SImode)")))
+
(define_constraint "Yd"
"@internal
A constant @code{move_operand} that can be safely loaded using
@@ -83,6 +83,8 @@ extern rtx loongarch_legitimize_call_address (rtx);
extern rtx loongarch_subword (rtx, bool);
extern bool loongarch_split_move_p (rtx, rtx);
extern void loongarch_split_move (rtx, rtx, rtx);
+extern bool loongarch_addu16i_imm12_operand_p (HOST_WIDE_INT, machine_mode);
+extern void loongarch_split_plus_constant (rtx *, machine_mode);
extern const char *loongarch_output_move (rtx, rtx);
extern bool loongarch_cfun_has_cprestore_slot_p (void);
#ifdef RTX_CODE
@@ -3756,6 +3756,50 @@ loongarch_split_move (rtx dest, rtx src, rtx insn_)
}
}
+/* Check if adding an integer constant value for a specific mode can be
+ performed with an addu16i.d instruction and an addi.{w/d}
+ instruction. */
+
+bool
+loongarch_addu16i_imm12_operand_p (HOST_WIDE_INT value, machine_mode mode)
+{
+ /* Not necessary, but avoid unnecessary calculation if !TARGET_64BIT. */
+ if (!TARGET_64BIT)
+ return false;
+
+ if ((value & 0xffff) == 0)
+ return false;
+
+ if (IMM12_OPERAND (value))
+ return false;
+
+ value = (value & ~HWIT_UC_0xFFF) + ((value & 0x800) << 1);
+ return ADDU16I_OPERAND (trunc_int_for_mode (value, mode));
+}
+
+/* Split one integer constant op[0] into two (op[1] and op[2]) for constant
+ plus operation in a specific mode. The splitted constants can be added
+ onto a register with a single instruction (addi.{d/w} or addu16i.d). */
+
+void
+loongarch_split_plus_constant (rtx *op, machine_mode mode)
+{
+ HOST_WIDE_INT v = INTVAL (op[0]), a;
+
+ if (DUAL_IMM12_OPERAND (v))
+ a = (v > 0 ? 2047 : -2048);
+ else if (loongarch_addu16i_imm12_operand_p (v, mode))
+ a = (v & ~HWIT_UC_0xFFF) + ((v & 0x800) << 1);
+ else if (mode == DImode && DUAL_ADDU16I_OPERAND (v))
+ a = (v > 0 ? 0x7fff : -0x8000) << 16;
+ else
+ gcc_unreachable ();
+
+ op[1] = gen_int_mode (a, mode);
+ v = v - (unsigned HOST_WIDE_INT) a;
+ op[2] = gen_int_mode (v, mode);
+}
+
/* Return true if a move from SRC to DEST in INSN should be split. */
static bool
@@ -612,6 +612,25 @@ enum reg_class
#define CONST_LOW_PART(VALUE) ((VALUE) - CONST_HIGH_PART (VALUE))
+/* True if VALUE can be added onto a register with one addu16i.d
+ instruction. */
+
+#define ADDU16I_OPERAND(VALUE) \
+ (TARGET_64BIT && (((VALUE) & 0xffff) == 0 \
+ && IMM16_OPERAND ((HOST_WIDE_INT) (VALUE) / 65536)))
+
+/* True if VALUE can be added onto a register with two addi.{d/w}
+ instructions, but not one addi.{d/w} instruction. */
+#define DUAL_IMM12_OPERAND(VALUE) \
+ (IN_RANGE ((VALUE), -4096, 4094) && !IMM12_OPERAND (VALUE))
+
+/* True if VALUE can be added onto a register with two addu16i.d
+ instruction, but not one addu16i.d instruction. */
+#define DUAL_ADDU16I_OPERAND(VALUE) \
+ (TARGET_64BIT && (((VALUE) & 0xffff) == 0 \
+ && !ADDU16I_OPERAND (VALUE) \
+ && IN_RANGE ((VALUE) / 65536, -0x10000, 0xfffe)))
+
#define IMM12_INT(X) IMM12_OPERAND (INTVAL (X))
#define IMM12_INT_UNSIGNED(X) IMM12_OPERAND_UNSIGNED (INTVAL (X))
#define LU12I_INT(X) LU12I_OPERAND (INTVAL (X))
@@ -598,24 +598,64 @@ (define_insn "add<mode>3"
[(set_attr "type" "fadd")
(set_attr "mode" "<UNITMODE>")])
-(define_insn "add<mode>3"
- [(set (match_operand:GPR 0 "register_operand" "=r,r")
- (plus:GPR (match_operand:GPR 1 "register_operand" "r,r")
- (match_operand:GPR 2 "arith_operand" "r,I")))]
+(define_insn_and_split "add<mode>3"
+ [(set (match_operand:GPR 0 "register_operand" "=r,r,r,r,r,r,r")
+ (plus:GPR (match_operand:GPR 1 "register_operand" "r,r,r,r,r,r,r")
+ (match_operand:GPR 2 "plus_<mode>_operand"
+ "r,I,La,Lb,Lc,Ld,Le")))]
""
- "add%i2.<d>\t%0,%1,%2";
+ "@
+ add.<d>\t%0,%1,%2
+ addi.<d>\t%0,%1,%2
+ #
+ * operands[2] = GEN_INT (INTVAL (operands[2]) / 65536); \
+ return \"addu16i.d\t%0,%1,%2\";
+ #
+ #
+ #"
+ "CONST_INT_P (operands[2]) && !IMM12_INT (operands[2]) \
+ && !ADDU16I_OPERAND (INTVAL (operands[2]))"
+ [(set (match_dup 0) (plus:GPR (match_dup 1) (match_dup 3)))
+ (set (match_dup 0) (plus:GPR (match_dup 0) (match_dup 4)))]
+ {
+ loongarch_split_plus_constant (&operands[2], <MODE>mode);
+ }
[(set_attr "alu_type" "add")
- (set_attr "mode" "<MODE>")])
-
-(define_insn "*addsi3_extended"
- [(set (match_operand:DI 0 "register_operand" "=r,r")
+ (set_attr "mode" "<MODE>")
+ (set_attr "insn_count" "1,1,2,1,2,2,2")
+ (set (attr "enabled")
+ (cond
+ [(match_test "<MODE>mode != DImode && which_alternative == 4")
+ (const_string "no")
+ (match_test "<MODE>mode != DImode && which_alternative == 5")
+ (const_string "no")
+ (match_test "<MODE>mode != SImode && which_alternative == 6")
+ (const_string "no")]
+ (const_string "yes")))])
+
+(define_insn_and_split "*addsi3_extended"
+ [(set (match_operand:DI 0 "register_operand" "=r,r,r,r")
(sign_extend:DI
- (plus:SI (match_operand:SI 1 "register_operand" "r,r")
- (match_operand:SI 2 "arith_operand" "r,I"))))]
+ (plus:SI (match_operand:SI 1 "register_operand" "r,r,r,r")
+ (match_operand:SI 2 "plus_si_extend_operand"
+ "r,I,La,Le"))))]
"TARGET_64BIT"
- "add%i2.w\t%0,%1,%2"
+ "@
+ add.w\t%0,%1,%2
+ addi.w\t%0,%1,%2
+ #
+ #"
+ "CONST_INT_P (operands[2]) && !IMM12_INT (operands[2])"
+ [(set (subreg:SI (match_dup 0) 0) (plus:SI (match_dup 1) (match_dup 3)))
+ (set (match_dup 0)
+ (sign_extend:DI (plus:SI (subreg:SI (match_dup 0) 0)
+ (match_dup 4))))]
+ {
+ loongarch_split_plus_constant (&operands[2], SImode);
+ }
[(set_attr "alu_type" "add")
- (set_attr "mode" "SI")])
+ (set_attr "mode" "SI")
+ (set_attr "insn_count" "1,1,2,2")])
;;
@@ -39,14 +39,50 @@ (define_predicate "const_arith_operand"
(and (match_code "const_int")
(match_test "IMM12_OPERAND (INTVAL (op))")))
+(define_predicate "const_dual_imm12_operand"
+ (and (match_code "const_int")
+ (match_test "DUAL_IMM12_OPERAND (INTVAL (op))")))
+
(define_predicate "const_imm16_operand"
(and (match_code "const_int")
(match_test "IMM16_OPERAND (INTVAL (op))")))
+(define_predicate "const_addu16i_operand"
+ (and (match_code "const_int")
+ (match_test "ADDU16I_OPERAND (INTVAL (op))")))
+
+(define_predicate "const_addu16i_imm12_di_operand"
+ (and (match_code "const_int")
+ (match_test "loongarch_addu16i_imm12_operand_p (INTVAL (op), DImode)")))
+
+(define_predicate "const_addu16i_imm12_si_operand"
+ (and (match_code "const_int")
+ (match_test "loongarch_addu16i_imm12_operand_p (INTVAL (op), SImode)")))
+
+(define_predicate "const_dual_addu16i_operand"
+ (and (match_code "const_int")
+ (match_test "DUAL_ADDU16I_OPERAND (INTVAL (op))")))
+
(define_predicate "arith_operand"
(ior (match_operand 0 "const_arith_operand")
(match_operand 0 "register_operand")))
+(define_predicate "plus_di_operand"
+ (ior (match_operand 0 "arith_operand")
+ (match_operand 0 "const_dual_imm12_operand")
+ (match_operand 0 "const_addu16i_operand")
+ (match_operand 0 "const_addu16i_imm12_di_operand")
+ (match_operand 0 "const_dual_addu16i_operand")))
+
+(define_predicate "plus_si_extend_operand"
+ (ior (match_operand 0 "arith_operand")
+ (match_operand 0 "const_dual_imm12_operand")
+ (match_operand 0 "const_addu16i_imm12_si_operand")))
+
+(define_predicate "plus_si_operand"
+ (ior (match_operand 0 "plus_si_extend_operand")
+ (match_operand 0 "const_addu16i_operand")))
+
(define_predicate "const_immalsl_operand"
(and (match_code "const_int")
(match_test "IN_RANGE (INTVAL (op), 1, 4)")))
new file mode 100644
@@ -0,0 +1,45 @@
+/* { dg-do compile } */
+/* { dg-options "-O -mabi=lp64d" } */
+
+/* None of these functions should load the const operand into a temp
+ register. */
+
+/* { dg-final { scan-assembler-not "add\\.[dw]" } } */
+
+unsigned long f01 (unsigned long x) { return x + 1; }
+unsigned long f02 (unsigned long x) { return x - 1; }
+unsigned long f03 (unsigned long x) { return x + 2047; }
+unsigned long f04 (unsigned long x) { return x + 4094; }
+unsigned long f05 (unsigned long x) { return x - 2048; }
+unsigned long f06 (unsigned long x) { return x - 4096; }
+unsigned long f07 (unsigned long x) { return x + 0x7fff0000; }
+unsigned long f08 (unsigned long x) { return x - 0x80000000l; }
+unsigned long f09 (unsigned long x) { return x + 0x7fff0000l * 2; }
+unsigned long f10 (unsigned long x) { return x - 0x80000000l * 2; }
+unsigned long f11 (unsigned long x) { return x + 0x7fff0000 + 0x1; }
+unsigned long f12 (unsigned long x) { return x + 0x7fff0000 - 0x1; }
+unsigned long f13 (unsigned long x) { return x + 0x7fff0000 + 0x7ff; }
+unsigned long f14 (unsigned long x) { return x + 0x7fff0000 - 0x800; }
+unsigned long f15 (unsigned long x) { return x - 0x80000000l - 1; }
+unsigned long f16 (unsigned long x) { return x - 0x80000000l + 1; }
+unsigned long f17 (unsigned long x) { return x - 0x80000000l - 0x800; }
+unsigned long f18 (unsigned long x) { return x - 0x80000000l + 0x7ff; }
+
+unsigned int g01 (unsigned int x) { return x + 1; }
+unsigned int g02 (unsigned int x) { return x - 1; }
+unsigned int g03 (unsigned int x) { return x + 2047; }
+unsigned int g04 (unsigned int x) { return x + 4094; }
+unsigned int g05 (unsigned int x) { return x - 2048; }
+unsigned int g06 (unsigned int x) { return x - 4096; }
+unsigned int g07 (unsigned int x) { return x + 0x7fff0000; }
+unsigned int g08 (unsigned int x) { return x - 0x80000000l; }
+unsigned int g09 (unsigned int x) { return x + 0x7fff0000l * 2; }
+unsigned int g10 (unsigned int x) { return x - 0x80000000l * 2; }
+unsigned int g11 (unsigned int x) { return x + 0x7fff0000 + 0x1; }
+unsigned int g12 (unsigned int x) { return x + 0x7fff0000 - 0x1; }
+unsigned int g13 (unsigned int x) { return x + 0x7fff0000 + 0x7ff; }
+unsigned int g14 (unsigned int x) { return x + 0x7fff0000 - 0x800; }
+unsigned int g15 (unsigned int x) { return x - 0x80000000l - 1; }
+unsigned int g16 (unsigned int x) { return x - 0x80000000l + 1; }
+unsigned int g17 (unsigned int x) { return x - 0x80000000l - 0x800; }
+unsigned int g18 (unsigned int x) { return x - 0x80000000l + 0x7ff; }
@@ -6,7 +6,7 @@
#define SIZE 128*1024
#include "stack-check-prologue.h"
-/* { dg-final { scan-assembler-times {\.cfi_def_cfa_offset 131088} 1 } } */
+/* { dg-final { scan-assembler-times {\.cfi_def_cfa_offset 131072} 1 } } */
/* { dg-final { scan-assembler-times {\.cfi_def_cfa_offset 0} 1 } } */
/* Checks that the CFA notes are correct for every sp adjustment. */
@@ -6,7 +6,7 @@
#define SIZE 1280*1024 + 512
#include "stack-check-prologue.h"
-/* { dg-final { scan-assembler-times {\.cfi_def_cfa_offset 1311248} 1 } } */
+/* { dg-final { scan-assembler-times {\.cfi_def_cfa_offset 1311232} 1 } } */
/* { dg-final { scan-assembler-times {\.cfi_def_cfa_offset 0} 1 } } */
/* Checks that the CFA notes are correct for every sp adjustment. */