diff --git a/gcc/config/riscv/bitmanip.md b/gcc/config/riscv/bitmanip.md
index bb23ceb86d9..78fdf02c2ec 100644
--- a/gcc/config/riscv/bitmanip.md
+++ b/gcc/config/riscv/bitmanip.md
@@ -29,7 +29,7 @@
   [(set_attr "type" "bitmanip,load")
    (set_attr "mode" "DI")])
 
-(define_insn "*shNadd"
+(define_insn "riscv_shNadd<X:mode>"
   [(set (match_operand:X 0 "register_operand" "=r")
 	(plus:X (ashift:X (match_operand:X 1 "register_operand" "r")
 			  (match_operand:QI 2 "imm123_operand" "Ds3"))
diff --git a/gcc/config/riscv/predicates.md b/gcc/config/riscv/predicates.md
index 6772228e5b6..c56bfa99339 100644
--- a/gcc/config/riscv/predicates.md
+++ b/gcc/config/riscv/predicates.md
@@ -308,3 +308,31 @@
 		 (match_test "INTVAL (op) > 0")))
        (ior (match_test "SMALL_OPERAND (UINTVAL (op) & ~(HOST_WIDE_INT_1U << floor_log2 (UINTVAL (op))))")
 	    (match_test "popcount_hwi (UINTVAL (op)) == 2"))))
+
+;; A CONST_INT that can be shifted down by 1, 2 or 3 bits (i.e., has
+;; these bits clear) and will then form a SMALL_OPERAND.
+(define_predicate "const_arith_shifted123_operand"
+  (and (match_code "const_int")
+       (not (match_test "SMALL_OPERAND (INTVAL (op))")))
+{
+  HOST_WIDE_INT val = INTVAL (op);
+  int trailing = ctz_hwi (val);
+
+  /* Clamp to 3, as we have sh[123]add instructions only. */
+  if (trailing > 3)
+     trailing = 3;
+
+  return trailing > 0 && SMALL_OPERAND (val >> trailing);
+})
+
+;; A CONST_INT that can formed by adding two SMALL_OPERANDs together
+(define_predicate "const_arith_2simm12_operand"
+  (and (match_code "const_int")
+       (ior (match_test "SMALL_OPERAND(INTVAL (op) - ~(HOST_WIDE_INT_M1U << (IMM_BITS - 1)))")
+	    (match_test "SMALL_OPERAND(INTVAL (op) -  (HOST_WIDE_INT_M1U << (IMM_BITS - 1)))"))))
+
+(define_predicate "addi_operand"
+  (ior (match_operand 0 "arith_operand")
+       (match_operand 0 "const_arith_2simm12_operand")
+       (and (match_operand 0 "const_arith_shifted123_operand")
+	    (match_test "TARGET_ZBA"))))
diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index 5a632058003..8efe90cc0fa 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -4910,8 +4910,9 @@ riscv_adjust_libcall_cfi_prologue ()
       }
 
   /* Debug info for adjust sp.  */
-  adjust_sp_rtx = gen_add3_insn (stack_pointer_rtx,
-				 stack_pointer_rtx, GEN_INT (-saved_size));
+  adjust_sp_rtx = gen_rtx_SET (stack_pointer_rtx,
+			       gen_rtx_PLUS (Pmode, stack_pointer_rtx,
+					     GEN_INT (-saved_size)));
   dwarf = alloc_reg_note (REG_CFA_ADJUST_CFA, adjust_sp_rtx,
 			  dwarf);
   return dwarf;
@@ -5013,8 +5014,9 @@ riscv_adjust_libcall_cfi_epilogue ()
   int saved_size = cfun->machine->frame.save_libcall_adjustment;
 
   /* Debug info for adjust sp.  */
-  adjust_sp_rtx = gen_add3_insn (stack_pointer_rtx,
-				 stack_pointer_rtx, GEN_INT (saved_size));
+  adjust_sp_rtx = gen_rtx_SET (stack_pointer_rtx,
+			       gen_rtx_PLUS (Pmode, stack_pointer_rtx,
+					     GEN_INT (saved_size)));
   dwarf = alloc_reg_note (REG_CFA_ADJUST_CFA, adjust_sp_rtx,
 			  dwarf);
 
diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md
index 171a0cdced6..289ff7470c6 100644
--- a/gcc/config/riscv/riscv.md
+++ b/gcc/config/riscv/riscv.md
@@ -446,7 +446,7 @@
   [(set_attr "type" "fadd")
    (set_attr "mode" "<UNITMODE>")])
 
-(define_insn "addsi3"
+(define_insn "riscv_addsi3"
   [(set (match_operand:SI          0 "register_operand" "=r,r")
 	(plus:SI (match_operand:SI 1 "register_operand" " r,r")
 		 (match_operand:SI 2 "arith_operand"    " r,I")))]
@@ -455,7 +455,7 @@
   [(set_attr "type" "arith")
    (set_attr "mode" "SI")])
 
-(define_insn "adddi3"
+(define_insn "riscv_adddi3"
   [(set (match_operand:DI          0 "register_operand" "=r,r")
 	(plus:DI (match_operand:DI 1 "register_operand" " r,r")
 		 (match_operand:DI 2 "arith_operand"    " r,I")))]
@@ -464,6 +464,60 @@
   [(set_attr "type" "arith")
    (set_attr "mode" "DI")])
 
+(define_expand "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 "addi_operand"          " r,I")))]
+  ""
+{
+  if (arith_operand (operands[2], <MODE>mode))
+    emit_insn (gen_riscv_add<mode>3 (operands[0], operands[1], operands[2]));
+  else if (const_arith_2simm12_operand (operands[2], <MODE>mode))
+    {
+      /* Split into two immediates that add up to the desired value:
+       * e.g., break up "a + 2445" into:
+       *         addi	a0,a0,2047
+       *	 addi	a0,a0,398
+       */
+
+      HOST_WIDE_INT val = INTVAL (operands[2]);
+      HOST_WIDE_INT saturated = HOST_WIDE_INT_M1U << (IMM_BITS - 1);
+
+      if (val >= 0)
+	 saturated = ~saturated;
+
+      val -= saturated;
+
+      rtx tmp = gen_reg_rtx (<MODE>mode);
+      emit_insn (gen_riscv_add<mode>3 (tmp, operands[1], GEN_INT (saturated)));
+      emit_insn (gen_riscv_add<mode>3 (operands[0], tmp, GEN_INT (val)));
+    }
+  else if (<MODE>mode == word_mode
+	   && const_arith_shifted123_operand (operands[2], <MODE>mode))
+    {
+      /* Use a sh[123]add and an immediate shifted down by 1, 2, or 3. */
+
+      HOST_WIDE_INT val = INTVAL (operands[2]);
+      int shamt = ctz_hwi (val);
+
+      if (shamt > 3)
+	shamt = 3;
+
+      rtx tmp = gen_reg_rtx (<MODE>mode);
+      emit_insn (gen_rtx_SET (tmp, GEN_INT (val >> shamt)));
+
+      /* We don't use gen_riscv_shNadd here, as it will only exist for
+	 <X:mode>.  Instead we build up its canonical form directly.  */
+      rtx shifted_imm = gen_rtx_ASHIFT (<MODE>mode, tmp, GEN_INT (shamt));
+      rtx shNadd = gen_rtx_PLUS (<MODE>mode, shifted_imm, operands[1]);
+      emit_insn (gen_rtx_SET (operands[0], shNadd));
+    }
+  else
+    FAIL;
+
+  DONE;
+})
+
 (define_expand "addv<mode>4"
   [(set (match_operand:GPR           0 "register_operand" "=r,r")
 	(plus:GPR (match_operand:GPR 1 "register_operand" " r,r")
diff --git a/gcc/testsuite/gcc.target/riscv/addi.c b/gcc/testsuite/gcc.target/riscv/addi.c
new file mode 100644
index 00000000000..01339e44697
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/addi.c
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gc -mabi=lp64" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-Og" } } */
+
+long long f (long long a)
+{
+  //	addi	a0,a0,2047
+  //	addi	a0,a0,1
+
+  return a + 2048;
+}
+
+long long f2 (long long a)
+{
+  //	addi	a0,a0,2047
+  //	addi	a0,a0,398
+
+  return a + 2445;
+}
+
+long long f3 (long long a)
+{
+  //  	addi	a0,a0,-2048
+  //	addi	a0,a0,-397
+
+  return a - 2445;
+}
+
+long long f6 (long long a)
+{
+  //  	li	a5,1179648
+  //	add	a0,a0,a5
+
+  return a + (0x12 << 16);
+}
+
+/* { dg-final { scan-assembler-times "addi\t" 6 } } */
+/* { dg-final { scan-assembler-times "li\t" 1 } } */
+/* { dg-final { scan-assembler-times "add\t" 1 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/zba-shNadd-06.c b/gcc/testsuite/gcc.target/riscv/zba-shNadd-06.c
new file mode 100644
index 00000000000..c55f05ed1d2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zba-shNadd-06.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gc_zba -mabi=lp64" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-Og" } } */
+
+long long f (long long a)
+{
+  return a + 7680;
+}
+
+/* { dg-final { scan-assembler-times "li\t" 1 } } */
+/* { dg-final { scan-assembler-times "sh3add\t" 1 } } */
