Add GCC support for the Zibi[1] extension, which provides BEQI and BNEI instructions for comparing a register with a small immediate in conditional branches.
The supported compare immediate range is -1 and 1..31. Comparisons against zero continue to use the existing beq/bne rs1,zero form. This patch adds the Zibi extension entry, the branch-immediate constraint and predicate, preserves Zibi immediates during integer conditional branch expansion, and adds a new branch pattern for BEQI/BNEI code generation. [1] https://github.com/riscv/zibi gcc/ChangeLog: * config/riscv/constraints.md (zibi): New constraint. * config/riscv/predicates.md (zibi_cimm_operand): New predicate. * config/riscv/riscv-ext.def (zibi): New extension. * config/riscv/riscv-ext.opt (ZIBI): New mask. * config/riscv/riscv.cc (riscv_zibi_cimm_operand_p): New function. (riscv_emit_int_compare): Add allow_zibi_branch_imm_p parameter and preserve Zibi branch immediates when allowed. (riscv_expand_conditional_branch): Allow Zibi branch immediates for integer conditional branches. * config/riscv/riscv.md (*branch_zibi<mode>): New pattern. * doc/riscv-ext.texi: Document zibi. gcc/testsuite/ChangeLog: * gcc.target/riscv/zibi.c: New test. --- gcc/config/riscv/constraints.md | 5 +++ gcc/config/riscv/predicates.md | 4 ++ gcc/config/riscv/riscv-ext.def | 13 ++++++ gcc/config/riscv/riscv-ext.opt | 2 + gcc/config/riscv/riscv.cc | 31 ++++++++++++-- gcc/config/riscv/riscv.md | 26 ++++++++++++ gcc/doc/riscv-ext.texi | 4 ++ gcc/testsuite/gcc.target/riscv/zibi.c | 59 +++++++++++++++++++++++++++ 8 files changed, 140 insertions(+), 4 deletions(-) create mode 100644 gcc/testsuite/gcc.target/riscv/zibi.c diff --git a/gcc/config/riscv/constraints.md b/gcc/config/riscv/constraints.md index 1f285a342ba..19184254ddd 100644 --- a/gcc/config/riscv/constraints.md +++ b/gcc/config/riscv/constraints.md @@ -133,6 +133,11 @@ (and (match_code "const_int") (match_test "SINGLE_BIT_MASK_OPERAND (~ival)"))) +(define_constraint "zibi" + "A constant suitable for Zibi branch-immediate instructions." + (and (match_code "const_int") + (match_test "INTVAL (op) == -1 || IN_RANGE (INTVAL (op), 1, 31)"))) + ;; Floating-point constant +0.0, used for FCVT-based moves when FMV is ;; not available in RV32. (define_constraint "G" diff --git a/gcc/config/riscv/predicates.md b/gcc/config/riscv/predicates.md index df1a76049f4..948864003ab 100644 --- a/gcc/config/riscv/predicates.md +++ b/gcc/config/riscv/predicates.md @@ -790,3 +790,7 @@ (define_predicate "ads_extract_size_imm_di" (and (match_code "const_int") (match_test "IN_RANGE (INTVAL (op), 1, 64)"))) + +(define_predicate "zibi_cimm_operand" + (and (match_code "const_int") + (match_test "INTVAL (op) == -1 || IN_RANGE (INTVAL (op), 1, 31)"))) diff --git a/gcc/config/riscv/riscv-ext.def b/gcc/config/riscv/riscv-ext.def index b9ef0c5ea05..ef8d0400f5d 100644 --- a/gcc/config/riscv/riscv-ext.def +++ b/gcc/config/riscv/riscv-ext.def @@ -220,6 +220,19 @@ DEFINE_RISCV_EXT( /* BITMASK_BIT_POSITION*/ 7, /* EXTRA_EXTENSION_FLAGS */ 0) +DEFINE_RISCV_EXT( + /* NAME */ zibi, + /* UPPERCASE_NAME */ ZIBI, + /* FULL_NAME */ "Branch with immediate", + /* DESC */ "", + /* URL */ , + /* DEP_EXTS */ ({}), + /* SUPPORTED_VERSIONS */ ({{1, 0}}), + /* FLAG_GROUP */ zi, + /* BITMASK_GROUP_ID */ BITMASK_NOT_YET_ALLOCATED, + /* BITMASK_BIT_POSITION*/ BITMASK_NOT_YET_ALLOCATED, + /* EXTRA_EXTENSION_FLAGS */ 0) + DEFINE_RISCV_EXT( /* NAME */ zic64b, /* UPPERCASE_NAME */ ZIC64B, diff --git a/gcc/config/riscv/riscv-ext.opt b/gcc/config/riscv/riscv-ext.opt index 802c9eb4170..c9229263e4f 100644 --- a/gcc/config/riscv/riscv-ext.opt +++ b/gcc/config/riscv/riscv-ext.opt @@ -126,6 +126,8 @@ Mask(RVV) Var(riscv_base_subext) Mask(RVH) Var(riscv_base_subext) +Mask(ZIBI) Var(riscv_zi_subext) + Mask(ZIC64B) Var(riscv_zi_subext) Mask(ZICBOM) Var(riscv_zi_subext) diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc index 238f64d6411..d7c58664893 100644 --- a/gcc/config/riscv/riscv.cc +++ b/gcc/config/riscv/riscv.cc @@ -5703,6 +5703,21 @@ riscv_extend_comparands (rtx_code code, rtx *op0, rtx *op1) } } +/* Return true if X is a constant that can be used as a Zibi branch + immediate. Zibi encodes cimm == 0 as -1, and cimm != 0 as a + zero-extended positive 5-bit value. Comparisons against zero should + continue to use beq/bne against x0. */ + +static bool +riscv_zibi_cimm_operand_p (rtx x) +{ + if (!CONST_INT_P (x)) + return false; + + HOST_WIDE_INT value = INTVAL (x); + return value == -1 || (value >= 1 && value <= 31); +} + /* Convert a comparison into something that can be used in a branch or conditional move. On entry, *OP0 and *OP1 are the values being compared and *CODE is the code used to compare them. @@ -5713,7 +5728,8 @@ riscv_extend_comparands (rtx_code code, rtx *op0, rtx *op1) static void riscv_emit_int_compare (enum rtx_code *code, rtx *op0, rtx *op1, - bool need_eq_ne_p = false) + bool need_eq_ne_p = false, + bool allow_zibi_branch_imm_p = false) { if (need_eq_ne_p) { @@ -5728,7 +5744,14 @@ riscv_emit_int_compare (enum rtx_code *code, rtx *op0, rtx *op1, gcc_unreachable (); } - if (splittable_const_int_operand (*op1, VOIDmode)) + bool zibi_branch_imm_p + = (allow_zibi_branch_imm_p + && TARGET_ZIBI + && (*code == EQ || *code == NE) + && riscv_zibi_cimm_operand_p (*op1)); + + if (!zibi_branch_imm_p + && splittable_const_int_operand (*op1, VOIDmode)) { HOST_WIDE_INT rhs = INTVAL (*op1); @@ -5773,7 +5796,7 @@ riscv_emit_int_compare (enum rtx_code *code, rtx *op0, rtx *op1, riscv_extend_comparands (*code, op0, op1); *op0 = force_reg (word_mode, *op0); - if (*op1 != const0_rtx) + if (*op1 != const0_rtx && !zibi_branch_imm_p) *op1 = force_reg (word_mode, *op1); } @@ -5940,7 +5963,7 @@ riscv_expand_conditional_branch (rtx label, rtx_code code, rtx op0, rtx op1) if (FLOAT_MODE_P (GET_MODE (op1))) riscv_emit_float_compare (&code, &op0, &op1); else - riscv_emit_int_compare (&code, &op0, &op1); + riscv_emit_int_compare (&code, &op0, &op1, false, true); if (FLOAT_MODE_P (GET_MODE (op0))) { diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md index 2055e5c4a9d..38fd5d9e9ac 100644 --- a/gcc/config/riscv/riscv.md +++ b/gcc/config/riscv/riscv.md @@ -3435,6 +3435,32 @@ [(set_attr "type" "branch") (set_attr "mode" "none")]) +(define_insn "*branch_zibi<mode>" + [(set (pc) + (if_then_else + (match_operator 1 "equality_operator" + [(match_operand:X 2 "register_operand" "r") + (match_operand:X 3 "zibi_cimm_operand" "zibi")]) + (label_ref (match_operand 0 "" "")) + (pc)))] + "TARGET_ZIBI" +{ + if (get_attr_length (insn) == 12) + { + if (GET_CODE (operands[1]) == EQ) + return "bnei\t%2,%3,1f; jump\t%l0,ra; 1:"; + else + return "beqi\t%2,%3,1f; jump\t%l0,ra; 1:"; + } + + if (GET_CODE (operands[1]) == EQ) + return "beqi\t%2,%3,%l0"; + else + return "bnei\t%2,%3,%l0"; +} + [(set_attr "type" "branch") + (set_attr "mode" "none")]) + ;; Conditional move and add patterns. (define_expand "mov<mode>cc" diff --git a/gcc/doc/riscv-ext.texi b/gcc/doc/riscv-ext.texi index 728cde737a7..6d12233c06d 100644 --- a/gcc/doc/riscv-ext.texi +++ b/gcc/doc/riscv-ext.texi @@ -58,6 +58,10 @@ @tab 1.0 @tab Hypervisor extension +@item @samp{zibi} +@tab 1.0 +@tab Branch with immediate + @item @samp{zic64b} @tab 1.0 @tab Cache block size is 64 bytes diff --git a/gcc/testsuite/gcc.target/riscv/zibi.c b/gcc/testsuite/gcc.target/riscv/zibi.c new file mode 100644 index 00000000000..babae5e6c2e --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/zibi.c @@ -0,0 +1,59 @@ +/* { dg-do compile { target { ! riscv_abi_e } } } */ +/* { dg-require-effective-target rv64 } */ +/* { dg-options "-march=rv64gc_zibi -mabi=lp64d -O2" } */ + +extern void foo (void); + +void +beqi_5 (long x) +{ + if (x == 5) + foo (); +} + +void +bnei_5 (long x) +{ + if (x != 5) + foo (); +} + +void +beq_0 (long x) +{ + if (x == 0) + foo (); +} + +void +beqi_31 (long x) +{ + if (x == 31) + foo (); +} + +void +not_zibi_32 (long x) +{ + if (x == 32) + foo (); +} + +void +beqi_m1 (long x) +{ + if (x == -1) + foo (); +} + +void +bnei_m1 (long x) +{ + if (x != -1) + foo (); +} + +/* { dg-final { scan-assembler-times "\tbeqi\t" 3 } } */ +/* { dg-final { scan-assembler-times "\tbnei\t" 2 } } */ +/* { dg-final { scan-assembler-times "\tbeq\t.*zero," 1 } } */ +/* { dg-final { scan-assembler-times "\tli\t.*,32" 1 } } */ -- 2.43.0
