--- gcc/cselib.cc.jj	2025-02-01 00:46:53.073275225 +0100
+++ gcc/cselib.cc	2025-02-03 13:16:16.381772989 +0100
@@ -248,8 +248,9 @@ static unsigned int *used_regs;
 static unsigned int n_used_regs;
 
 /* We pass this to cselib_invalidate_mem to invalidate all of
-   memory for a non-const call instruction.  */
-static GTY(()) rtx callmem;
+   memory for a non-const call instruction and memory below stack pointer
+   for const/pure calls.  */
+static GTY(()) rtx callmem[2];
 
 /* Set by discard_useless_locs if it deleted the last location of any
    value.  */
@@ -808,7 +809,7 @@ cselib_preserve_only_values (void)
   for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
     cselib_invalidate_regno (i, reg_raw_mode[i]);
 
-  cselib_invalidate_mem (callmem);
+  cselib_invalidate_mem (callmem[0]);
 
   remove_useless_values ();
 
@@ -2644,6 +2645,97 @@ cselib_invalidate_mem (rtx mem_rtx)
 	      p = &(*p)->next;
 	      continue;
 	    }
+
+	  /* When invalidating memory below the stack pointer for const/pure
+	     calls and alloca/VLAs aren't used, attempt to optimize.  Values
+	     stored into area below the stack pointer shouldn't be addressable
+	     and should be stored just through stack pointer derived
+	     expressions, so don't invalidate MEMs not using stack derived
+	     addresses, or if the MEMs clearly aren't below the stack
+	     pointer.  */
+	  if (mem_rtx == callmem[1]
+	      && num_mems < param_max_cselib_memory_locations
+	      && GET_CODE (XEXP (x, 0)) == VALUE
+	      && !cfun->calls_alloca)
+	    {
+	      cselib_val *v2 = CSELIB_VAL_PTR (XEXP (x, 0));
+	      rtx sp_derived_base = NULL_RTX;
+	      HOST_WIDE_INT sp_derived_off = 0;
+	      if (SP_DERIVED_VALUE_P (v2->val_rtx))
+		sp_derived_base = v2->val_rtx;
+	      else
+		for (struct elt_loc_list *l = v2->locs; l; l = l->next)
+		  if (GET_CODE (l->loc) == PLUS
+		      && GET_CODE (XEXP (l->loc, 0)) == VALUE
+		      && SP_DERIVED_VALUE_P (XEXP (l->loc, 0))
+		      && CONST_INT_P (XEXP (l->loc, 1)))
+		    {
+		      sp_derived_base = XEXP (l->loc, 0);
+		      sp_derived_off = INTVAL (XEXP (l->loc, 1));
+		      break;
+		    }
+	      if (sp_derived_base)
+		if (cselib_val *v3
+		    = cselib_lookup_1 (stack_pointer_rtx, Pmode, 0, VOIDmode))
+		  {
+		    bool ok = false;
+		    HOST_WIDE_INT off = 0;
+		    if (v3->val_rtx == sp_derived_base)
+		      ok = true;
+		    else
+		      for (struct elt_loc_list *l = v3->locs; l; l = l->next)
+			if (GET_CODE (l->loc) == PLUS
+			    && XEXP (l->loc, 0) == sp_derived_base
+			    && CONST_INT_P (XEXP (l->loc, 1)))
+			  {
+			    ok = true;
+			    off = INTVAL (XEXP (l->loc, 1));
+			    break;
+			  }
+		    if (ok)
+		      {
+			if (STACK_GROWS_DOWNWARD)
+			  {
+#ifdef STACK_ADDRESS_OFFSET
+			    /* On SPARC take stack pointer bias into account as
+			       well.  */
+			    off
+			      += (STACK_ADDRESS_OFFSET
+				  - FIRST_PARM_OFFSET (current_function_decl));
+#endif
+			    if (sp_derived_off >= off)
+			      /* x is at or above the current stack pointer,
+				 no need to invalidate it.  */
+			      sp_derived_base = NULL_RTX;
+			  }
+			else
+			  {
+			    HOST_WIDE_INT sz;
+			    enum machine_mode mode = GET_MODE (x);
+			    if ((MEM_SIZE_KNOWN_P (x)
+				 && MEM_SIZE (x).is_constant (&sz))
+				|| (mode != BLKmode
+				    && GET_MODE_SIZE (mode).is_constant (&sz)))
+			      if (sp_derived_off < off
+				  && ((HOST_WIDE_INT)
+				      ((unsigned HOST_WIDE_INT) sp_derived_off
+				       + sz) <= off))
+				/* x's end is below or at the current stack
+				   pointer in !STACK_GROWS_DOWNWARD target,
+				   no need to invalidate it.  */
+				sp_derived_base = NULL_RTX;
+			  }
+		      }
+		  }
+	      if (sp_derived_base == NULL_RTX)
+		{
+		  has_mem = true;
+		  num_mems++;
+		  p = &(*p)->next;
+		  continue;
+		}
+	    }
+
 	  if (num_mems < param_max_cselib_memory_locations
 	      && ! canon_anti_dependence (x, false, mem_rtx,
 					  GET_MODE (mem_rtx), mem_addr))
@@ -3196,14 +3288,20 @@ cselib_process_insn (rtx_insn *insn)
 	 as if they were regular functions.  */
       if (RTL_LOOPING_CONST_OR_PURE_CALL_P (insn)
 	  || !(RTL_CONST_OR_PURE_CALL_P (insn)))
-	cselib_invalidate_mem (callmem);
+	cselib_invalidate_mem (callmem[0]);
       else
-	/* For const/pure calls, invalidate any argument slots because
-	   they are owned by the callee.  */
-	for (x = CALL_INSN_FUNCTION_USAGE (insn); x; x = XEXP (x, 1))
-	  if (GET_CODE (XEXP (x, 0)) == USE
-	      && MEM_P (XEXP (XEXP (x, 0), 0)))
-	    cselib_invalidate_mem (XEXP (XEXP (x, 0), 0));
+	{
+	  /* For const/pure calls, invalidate any argument slots because
+	     they are owned by the callee.  */
+	  for (x = CALL_INSN_FUNCTION_USAGE (insn); x; x = XEXP (x, 1))
+	    if (GET_CODE (XEXP (x, 0)) == USE
+		&& MEM_P (XEXP (XEXP (x, 0), 0)))
+	      cselib_invalidate_mem (XEXP (XEXP (x, 0), 0));
+	  /* And invalidate memory below the stack (or above for
+	     !STACK_GROWS_DOWNWARD), as even const/pure call can invalidate
+	     that.  */
+	  cselib_invalidate_mem (callmem[1]);
+	}
     }
 
   cselib_record_sets (insn);
@@ -3256,8 +3354,27 @@ cselib_init (int record_what)
 
   /* (mem:BLK (scratch)) is a special mechanism to conflict with everything,
      see canon_true_dependence.  This is only created once.  */
-  if (! callmem)
-    callmem = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (VOIDmode));
+  if (! callmem[0])
+    {
+      callmem[0] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (VOIDmode));
+      /* Similarly create a MEM representing roughly everything below
+	 the stack for STACK_GROWS_DOWNWARD targets or everything above
+	 it otherwise.  */
+      if (STACK_GROWS_DOWNWARD)
+	{
+	  unsigned HOST_WIDE_INT off = -(GET_MODE_MASK (Pmode) >> 1);
+#ifdef STACK_ADDRESS_OFFSET
+	  /* On SPARC take stack pointer bias into account as well.  */
+	  off += (STACK_ADDRESS_OFFSET
+		  - FIRST_PARM_OFFSET (current_function_decl)));
+#endif
+	  callmem[1] = plus_constant (Pmode, stack_pointer_rtx, off);
+	}
+      else
+	callmem[1] = stack_pointer_rtx;
+      callmem[1] = gen_rtx_MEM (BLKmode, callmem[1]);
+      set_mem_size (callmem[1], GET_MODE_MASK (Pmode) >> 1);
+    }
 
   cselib_nregs = max_reg_num ();
 
--- gcc/testsuite/gcc.dg/pr117239.c.jj	2025-02-03 11:13:59.399159640 +0100
+++ gcc/testsuite/gcc.dg/pr117239.c	2025-02-03 11:13:59.399159640 +0100
@@ -0,0 +1,42 @@
+/* PR rtl-optimization/117239 */
+/* { dg-do run } */
+/* { dg-options "-fno-inline -O2" } */
+/* { dg-additional-options "-fschedule-insns" { target i?86-*-* x86_64-*-* } } */
+
+int a, b, c = 1, d;
+
+int
+foo (void)
+{
+  return a;
+}
+
+struct A {
+  int e, f, g, h;
+  short i;
+  int j;
+};
+
+void
+bar (int x, struct A y)
+{
+  if (y.j == 1)
+    c = 0;
+}
+
+int
+baz (struct A x)
+{
+  return b;
+}
+
+int
+main ()
+{
+  struct A k = { 0, 0, 0, 0, 0, 1 };
+  d = baz (k);
+  bar (foo (), k);
+  if (c != 0)
+    __builtin_abort ();
+  return 0;
+}
