diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc
index f070218eca1130d5a2b8f3788ce2f2018eea22af..49caff66960f751d04e63294580d58b55678cf5d 100644
--- a/gcc/config/aarch64/aarch64.cc
+++ b/gcc/config/aarch64/aarch64.cc
@@ -14680,6 +14680,10 @@ cost_plus:
 	}
       return false;
 
+    case ROTATE:
+    case ROTATERT:
+    case LSHIFTRT:
+    case ASHIFTRT:
     case ASHIFT:
       op0 = XEXP (x, 0);
       op1 = XEXP (x, 1);
@@ -14695,8 +14699,8 @@ cost_plus:
 		}
 	      else
 		{
-		  /* LSL (immediate), UBMF, UBFIZ and friends.  These are all
-		     aliases.  */
+		  /* LSL (immediate), ASR (immediate), UBMF, UBFIZ and friends.
+		     These are all aliases.  */
 		  *cost += extra_cost->alu.shift;
 		}
 	    }
@@ -14720,9 +14724,13 @@ cost_plus:
 	  else
 	    {
 	      if (speed)
-		/* LSLV.  */
+		/* LSLV, ASRV.  */
 		*cost += extra_cost->alu.shift_reg;
 
+	       /* The register shift amount may be in a shorter mode expressed
+		  as a lowpart SUBREG.  For costing purposes just look inside.  */
+	      if (SUBREG_P (op1) && subreg_lowpart_p (op1))
+		op1 = SUBREG_REG (op1);
 	      if (GET_CODE (op1) == AND && REG_P (XEXP (op1, 0))
 		  && CONST_INT_P (XEXP (op1, 1))
 		  && known_eq (INTVAL (XEXP (op1, 1)),
@@ -14737,55 +14745,6 @@ cost_plus:
 	  return false;  /* All arguments need to be in registers.  */
         }
 
-    case ROTATE:
-    case ROTATERT:
-    case LSHIFTRT:
-    case ASHIFTRT:
-      op0 = XEXP (x, 0);
-      op1 = XEXP (x, 1);
-
-      if (CONST_INT_P (op1))
-	{
-	  /* ASR (immediate) and friends.  */
-	  if (speed)
-	    {
-	      if (VECTOR_MODE_P (mode))
-		*cost += extra_cost->vect.alu;
-	      else
-		*cost += extra_cost->alu.shift;
-	    }
-
-	  *cost += rtx_cost (op0, mode, (enum rtx_code) code, 0, speed);
-	  return true;
-	}
-      else
-	{
-	  if (VECTOR_MODE_P (mode))
-	    {
-	      if (speed)
-		/* Vector shift (register).  */
-		*cost += extra_cost->vect.alu;
-	    }
-	  else
-	    {
-	      if (speed)
-		/* ASR (register) and friends.  */
-		*cost += extra_cost->alu.shift_reg;
-
-	      if (GET_CODE (op1) == AND && REG_P (XEXP (op1, 0))
-		  && CONST_INT_P (XEXP (op1, 1))
-		  && known_eq (INTVAL (XEXP (op1, 1)),
-			       GET_MODE_BITSIZE (mode) - 1))
-		{
-		  *cost += rtx_cost (op0, mode, (rtx_code) code, 0, speed);
-		  /* We already demanded XEXP (op1, 0) to be REG_P, so
-		     don't recurse into it.  */
-		  return true;
-		}
-	    }
-	  return false;  /* All arguments need to be in registers.  */
-	}
-
     case SYMBOL_REF:
 
       if (aarch64_cmodel == AARCH64_CMODEL_LARGE
diff --git a/gcc/testsuite/gcc.target/aarch64/pr108840.c b/gcc/testsuite/gcc.target/aarch64/pr108840.c
new file mode 100644
index 0000000000000000000000000000000000000000..804c1cd915675ed3f4ad8e668548c885574fb18a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/pr108840.c
@@ -0,0 +1,38 @@
+/* PR target/108840.  Check that the explicit &31 is eliminated.  */
+/* { dg-do compile } */
+/* { dg-options "-O" } */
+
+int
+foo (int x, int y)
+{
+  return x << (y & 31);
+}
+
+void
+bar (int x[3], int y)
+{
+  x[0] <<= (y & 31);
+  x[1] <<= (y & 31);
+  x[2] <<= (y & 31);
+}
+
+void
+baz (int x[3], int y)
+{
+  y &= 31;
+  x[0] <<= y;
+  x[1] <<= y;
+  x[2] <<= y;
+}
+
+void corge (int, int, int);
+
+void
+qux (int x, int y, int z, int n)
+{
+  n &= 31;
+  corge (x << n, y << n, z >> n);
+}
+
+/* { dg-final { scan-assembler-not {and\tw[0-9]+, w[0-9]+, 31} } } */
+
