diff --git a/gcc/config/host-darwin.c b/gcc/config/host-darwin.c
index 14a01fe71f2..559b919f2ee 100644
--- a/gcc/config/host-darwin.c
+++ b/gcc/config/host-darwin.c
@@ -20,62 +20,177 @@
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
+#include "options.h"
 #include "diagnostic-core.h"
 #include "config/host-darwin.h"
-
-/* Yes, this is really supposed to work.  */
-/* This allows for a pagesize of 16384, which we have on Darwin20, but should
-   continue to work OK for pagesize 4096 which we have on earlier versions.
-   The size is 1 (binary) Gb.  */
-static char pch_address_space[65536*16384] __attribute__((aligned (16384)));
-
-/* Return the address of the PCH address space, if the PCH will fit in it.  */
+#include <errno.h>
+
+/* For Darwin (macOS only) platforms, without ASLR (PIE) enabled on the
+   binaries, the following VM addresses are expected to be available.
+   NOTE, that for aarch64, ASLR is always enabled - but the VM address
+   mentioned below is available (at least on Darwin20).
+
+   The spaces should all have 512Mb available c.f. PCH files for large
+   C++ or Objective-C in the range of 150Mb for 64b hosts.
+
+   We also try to steer clear of places already used for sanitizers.  */
+
+#define PAGE_SZ 4096
+#if defined(__x86_64) && defined(__LP64__)
+# define TRY_EMPTY_VM_SPACE	0x180000000000ULL
+# define SAFE_ALLOC_SIZE	0x20000000
+#elif defined(__x86_64)
+# define TRY_EMPTY_VM_SPACE	0x00006fe00000ULL
+# define SAFE_ALLOC_SIZE	0x20000000
+#elif defined(__i386)
+# define TRY_EMPTY_VM_SPACE	0x00006fe00000ULL
+# define SAFE_ALLOC_SIZE	0x20000000
+#elif defined(__POWERPC__) && defined(__LP64__)
+# define TRY_EMPTY_VM_SPACE	0x180000000000ULL
+# define SAFE_ALLOC_SIZE	0x20000000
+#elif defined(__POWERPC__)
+# define TRY_EMPTY_VM_SPACE	0x00006fe00000ULL
+# define SAFE_ALLOC_SIZE	0x20000000
+#elif defined(__aarch64__)
+# undef PAGE_SZ
+# define PAGE_SZ 16384
+# define TRY_EMPTY_VM_SPACE	0x180000000000ULL
+# define SAFE_ALLOC_SIZE	0x20000000
+#else
+# error "unknown Darwin target"
+#endif
+
+/* Try to map a known position in the VM.  The current PCH implementation
+   can adjust values at write-time, but not at read-time thus we need to
+   pick up the same position when reading as we got at write-time.  */
 
 void *
-darwin_gt_pch_get_address (size_t sz, int fd ATTRIBUTE_UNUSED)
+darwin_gt_pch_get_address (size_t sz, int fd)
 {
-  if (sz <= sizeof (pch_address_space))
-    return pch_address_space;
-  else
-    return NULL;
+  if (sz > SAFE_ALLOC_SIZE)
+    {
+      error ("PCH memory request exceeds the available space");
+      return NULL;
+    }
+
+  /* Now try with the constraint that we really want this address...  */
+  void *addr = mmap ((void *)TRY_EMPTY_VM_SPACE, sz, PROT_READ | PROT_WRITE,
+		     MAP_PRIVATE | MAP_FIXED, fd, 0);
+
+  if (addr != (void *) MAP_FAILED)
+    munmap (addr, sz);
+
+  /* This ought to be the only alternative to failure, but there are comments
+     that suggest some versions of mmap can be buggy and return a different
+     value.  */
+  if (addr == (void *) TRY_EMPTY_VM_SPACE)
+    return addr;
+
+  warning (OPT_Winvalid_pch, "PCH memory [fixed at %p] is not available %m",
+	   (void *) TRY_EMPTY_VM_SPACE);
+
+  /* OK try to find a space without the constraint.  */
+  addr = mmap ((void *) TRY_EMPTY_VM_SPACE, sz, PROT_READ | PROT_WRITE,
+	       MAP_PRIVATE, fd, 0);
+
+  /* If we failed this time, that means there is *no* large enough free
+     space.  */
+  if (addr == (void *) MAP_FAILED)
+    {
+      error ("no memory is available for PCH : %m");
+      return NULL;
+    }
+
+  /* Unmap the area before returning.  */
+  munmap (addr, sz);
+
+  /* If we got the exact area we requested, then that's great.  */
+  if (TRY_EMPTY_VM_SPACE && addr == (void *) TRY_EMPTY_VM_SPACE)
+    return addr;
+
+  warning (OPT_Winvalid_pch, "PCH memory at %p is not available",
+	  (void *) TRY_EMPTY_VM_SPACE);
+
+  /* Otherwise, we need to try again but put some buffer space first.  */
+  size_t buffer_size = 32 * 1024 * 1024;
+  void *buffer = mmap (0, buffer_size, PROT_NONE,
+		       MAP_PRIVATE | MAP_ANON, -1, 0);
+  addr = mmap ((void *)TRY_EMPTY_VM_SPACE, sz, PROT_READ | PROT_WRITE,
+		MAP_PRIVATE, fd, 0);
+  if (buffer != (void *) MAP_FAILED)
+    munmap (buffer, buffer_size);
+
+  if (addr == (void *) MAP_FAILED)
+    {
+      error ("PCH memory not available %m");
+      return NULL;
+    }
+
+  warning (OPT_Winvalid_pch, "PCH memory at %p used instead", addr);
+  munmap (addr, sz);
+  return addr;
 }
 
-/* Check ADDR and SZ for validity, and deallocate (using munmap) that part of
-   pch_address_space beyond SZ.  */
+/* Try to mmap the PCH file at ADDR for SZ bytes at OFF offset in the file.
+   If we succeed return 1, if we cannot mmap the desired address, then we
+   fail with -1.  */
 
 int
 darwin_gt_pch_use_address (void *addr, size_t sz, int fd, size_t off)
 {
-  const size_t pagesize = getpagesize();
-  void *mmap_result;
-  int ret;
+  void *mapped_addr;
+
+  /* We're called with size == 0 if we're not planning to load a PCH
+     file at all.  This allows the hook to free any static space that
+     we might have allocated at link time.  */
+  if (sz == 0)
+    return -1;
 
-  gcc_assert ((size_t)pch_address_space % pagesize == 0
-	      && sizeof (pch_address_space) % pagesize == 0);
-  
-  ret = (addr == pch_address_space && sz <= sizeof (pch_address_space));
-  if (! ret)
-    sz = 0;
+  gcc_checking_assert (!(off % PAGE_SZ));
+  if (addr != (void *) TRY_EMPTY_VM_SPACE)
+    warning (OPT_Winvalid_pch, "PCH at %p does not use the default position",
+	     addr);
 
-  /* Round the size to a whole page size.  Normally this is a no-op.  */
-  sz = (sz + pagesize - 1) / pagesize * pagesize;
+  /* Try to map the file with MAP_PRIVATE and FIXED.  */
+  mapped_addr = mmap (addr, sz, PROT_READ | PROT_WRITE,
+		      MAP_PRIVATE | MAP_FIXED, fd, (off_t) off);
 
-  if (munmap (pch_address_space + sz, sizeof (pch_address_space) - sz) != 0)
-    fatal_error (input_location,
-		 "could not unmap %<pch_address_space%>: %m");
+  /* Hopefully, we succeed.  */
+  if (mapped_addr == addr)
+    return 1;
 
-  if (ret)
+  warning (OPT_Winvalid_pch, "PCH private mmap of written position (%p)"
+	   " failed [errno %d] %m", addr, errno);
+
+  if (mapped_addr != (void *) MAP_FAILED)
+    munmap (mapped_addr, sz);
+
+  /* Try to make an anonymous private mmap at the desired location.  */
+  mapped_addr = mmap (addr, sz, PROT_READ | PROT_WRITE,
+		      MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, (off_t)0);
+
+  if (mapped_addr != addr)
     {
-      mmap_result = mmap (addr, sz,
-			  PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED,
-			  fd, off);
+      warning (OPT_Winvalid_pch, "PCH anon mmap at written position (%p)"
+	       " failed [errno %d] %m", addr, errno);
+      if (mapped_addr != (void *) MAP_FAILED)
+	    munmap (mapped_addr, sz);
+      return -1;
+    }
+
+  if (lseek (fd, off, SEEK_SET) == (off_t) -1)
+    return -1;
 
-      /* The file might not be mmap-able.  */
-      ret = mmap_result != (void *) MAP_FAILED;
+  while (sz)
+    {
+      ssize_t nbytes;
 
-      /* Sanity check for broken MAP_FIXED.  */
-      gcc_assert (!ret || mmap_result == addr);
+      nbytes = read (fd, addr, MIN (sz, (size_t) -1 >> 1));
+      if (nbytes <= 0)
+	return -1;
+      addr = (char *) addr + nbytes;
+      sz -= nbytes;
     }
 
-  return ret;
+  return 1;
 }
