Merge from trunk revision 3a39a31b8ae9c6465434aefa657f7fcc86f905c0.
diff --git a/ChangeLog b/ChangeLog
index 1965414..96a4056 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2023-06-25  Lehua Ding  <lehua.ding@rivai.ai>
+
+	* MAINTAINERS: Add Lehua Ding to write after approval
+
+2023-06-25  Iain Sandoe  <iain@sandoe.co.uk>
+
+	* Makefile.def: Pass the enable-host-pie value to GCC configure.
+	* Makefile.in: Regenerate.
+	* configure: Regenerate.
+	* configure.ac: Adjust the logic for shared and PIE host flags to
+	ensure that PIE is passed for hosts that require it.
+
 2023-06-16  YunQiang Su  <yunqiang.su@cipunited.com>
 
 	* MAINTAINERS (Write After Approval): move Matthew Fortune
diff --git a/MAINTAINERS b/MAINTAINERS
index 4825ee4..bac773a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -394,6 +394,7 @@
 Sameera Deshpande				<sameera.deshpande@linaro.org>
 Wilco Dijkstra					<wdijkstr@arm.com>
 Benoit Dupont de Dinechin			<benoit.dupont-de-dinechin@st.com>
+Lehua Ding					<lehua.ding@rivai.ai>
 Jason Eckhardt					<jle@rice.edu>
 Bernd Edlinger					<bernd.edlinger@hotmail.de>
 Phil Edwards					<pme@gcc.gnu.org>
diff --git a/Makefile.def b/Makefile.def
index 35e994e..9b4a8a2 100644
--- a/Makefile.def
+++ b/Makefile.def
@@ -47,7 +47,8 @@
 host_modules= { module= flex; no_check_cross= true; };
 host_modules= { module= gas; bootstrap=true; };
 host_modules= { module= gcc; bootstrap=true; 
-		extra_make_flags="$(EXTRA_GCC_FLAGS)"; };
+		extra_make_flags="$(EXTRA_GCC_FLAGS)";
+		extra_configure_flags='@gcc_host_pie@'; };
 host_modules= { module= gmp; lib_path=.libs; bootstrap=true;
 		// Work around in-tree gmp configure bug with missing flex.
 		extra_configure_flags='--disable-shared LEX="touch lex.yy.c" @host_libs_picflag@';
diff --git a/Makefile.in b/Makefile.in
index b559454..45f09f9 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -12021,7 +12021,7 @@
 	  $$s/$$module_srcdir/configure \
 	  --srcdir=$${topdir}/$$module_srcdir \
 	  $(HOST_CONFIGARGS) --build=${build_alias} --host=${host_alias} \
-	  --target=${target_alias}  \
+	  --target=${target_alias} @gcc_host_pie@ \
 	  || exit 1
 @endif gcc
 
@@ -12056,7 +12056,8 @@
 	  $(HOST_CONFIGARGS) --build=${build_alias} --host=${host_alias} \
 	  --target=${target_alias} \
 	   \
-	  $(STAGE1_CONFIGURE_FLAGS)
+	  $(STAGE1_CONFIGURE_FLAGS) \
+	  @gcc_host_pie@
 @endif gcc-bootstrap
 
 .PHONY: configure-stage2-gcc maybe-configure-stage2-gcc
@@ -12089,7 +12090,8 @@
 	  $(HOST_CONFIGARGS) --build=${build_alias} --host=${host_alias} \
 	  --target=${target_alias} \
 	  --with-build-libsubdir=$(HOST_SUBDIR) \
-	  $(STAGE2_CONFIGURE_FLAGS)
+	  $(STAGE2_CONFIGURE_FLAGS) \
+	  @gcc_host_pie@
 @endif gcc-bootstrap
 
 .PHONY: configure-stage3-gcc maybe-configure-stage3-gcc
@@ -12122,7 +12124,8 @@
 	  $(HOST_CONFIGARGS) --build=${build_alias} --host=${host_alias} \
 	  --target=${target_alias} \
 	  --with-build-libsubdir=$(HOST_SUBDIR) \
-	  $(STAGE3_CONFIGURE_FLAGS)
+	  $(STAGE3_CONFIGURE_FLAGS) \
+	  @gcc_host_pie@
 @endif gcc-bootstrap
 
 .PHONY: configure-stage4-gcc maybe-configure-stage4-gcc
@@ -12155,7 +12158,8 @@
 	  $(HOST_CONFIGARGS) --build=${build_alias} --host=${host_alias} \
 	  --target=${target_alias} \
 	  --with-build-libsubdir=$(HOST_SUBDIR) \
-	  $(STAGE4_CONFIGURE_FLAGS)
+	  $(STAGE4_CONFIGURE_FLAGS) \
+	  @gcc_host_pie@
 @endif gcc-bootstrap
 
 .PHONY: configure-stageprofile-gcc maybe-configure-stageprofile-gcc
@@ -12188,7 +12192,8 @@
 	  $(HOST_CONFIGARGS) --build=${build_alias} --host=${host_alias} \
 	  --target=${target_alias} \
 	  --with-build-libsubdir=$(HOST_SUBDIR) \
-	  $(STAGEprofile_CONFIGURE_FLAGS)
+	  $(STAGEprofile_CONFIGURE_FLAGS) \
+	  @gcc_host_pie@
 @endif gcc-bootstrap
 
 .PHONY: configure-stagetrain-gcc maybe-configure-stagetrain-gcc
@@ -12221,7 +12226,8 @@
 	  $(HOST_CONFIGARGS) --build=${build_alias} --host=${host_alias} \
 	  --target=${target_alias} \
 	  --with-build-libsubdir=$(HOST_SUBDIR) \
-	  $(STAGEtrain_CONFIGURE_FLAGS)
+	  $(STAGEtrain_CONFIGURE_FLAGS) \
+	  @gcc_host_pie@
 @endif gcc-bootstrap
 
 .PHONY: configure-stagefeedback-gcc maybe-configure-stagefeedback-gcc
@@ -12254,7 +12260,8 @@
 	  $(HOST_CONFIGARGS) --build=${build_alias} --host=${host_alias} \
 	  --target=${target_alias} \
 	  --with-build-libsubdir=$(HOST_SUBDIR) \
-	  $(STAGEfeedback_CONFIGURE_FLAGS)
+	  $(STAGEfeedback_CONFIGURE_FLAGS) \
+	  @gcc_host_pie@
 @endif gcc-bootstrap
 
 .PHONY: configure-stageautoprofile-gcc maybe-configure-stageautoprofile-gcc
@@ -12287,7 +12294,8 @@
 	  $(HOST_CONFIGARGS) --build=${build_alias} --host=${host_alias} \
 	  --target=${target_alias} \
 	  --with-build-libsubdir=$(HOST_SUBDIR) \
-	  $(STAGEautoprofile_CONFIGURE_FLAGS)
+	  $(STAGEautoprofile_CONFIGURE_FLAGS) \
+	  @gcc_host_pie@
 @endif gcc-bootstrap
 
 .PHONY: configure-stageautofeedback-gcc maybe-configure-stageautofeedback-gcc
@@ -12320,7 +12328,8 @@
 	  $(HOST_CONFIGARGS) --build=${build_alias} --host=${host_alias} \
 	  --target=${target_alias} \
 	  --with-build-libsubdir=$(HOST_SUBDIR) \
-	  $(STAGEautofeedback_CONFIGURE_FLAGS)
+	  $(STAGEautofeedback_CONFIGURE_FLAGS) \
+	  @gcc_host_pie@
 @endif gcc-bootstrap
 
 
diff --git a/c++tools/ChangeLog b/c++tools/ChangeLog
index 5d98eef..7efb9fa 100644
--- a/c++tools/ChangeLog
+++ b/c++tools/ChangeLog
@@ -1,3 +1,8 @@
+2023-06-22  Marek Polacek  <polacek@redhat.com>
+
+	* configure.ac (--enable-host-bind-now): New check.
+	* configure: Regenerate.
+
 2023-06-15  Marek Polacek  <polacek@redhat.com>
 
 	* Makefile.in: Rename PIEFLAG to PICFLAG.  Set LD_PICFLAG.  Use it.
diff --git a/c++tools/configure b/c++tools/configure
index 8808700..006efe07 100755
--- a/c++tools/configure
+++ b/c++tools/configure
@@ -628,6 +628,7 @@
 GREP
 CXXCPP
 LD_PICFLAG
+enable_host_bind_now
 PICFLAG
 MAINTAINER
 CXX_AUX_TOOLS
@@ -702,6 +703,7 @@
 enable_checking
 enable_default_pie
 enable_host_pie
+enable_host_bind_now
 with_gcc_major_version_only
 '
       ac_precious_vars='build_alias
@@ -1336,6 +1338,7 @@
                           yes,no,all,none,release.
   --enable-default-pie    enable Position Independent Executable as default
   --enable-host-pie       build host code as PIE
+  --enable-host-bind-now  link host code as BIND_NOW
 
 Optional Packages:
   --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
@@ -3007,6 +3010,14 @@
 
 
 
+# Enable --enable-host-bind-now
+# Check whether --enable-host-bind-now was given.
+if test "${enable_host_bind_now+set}" = set; then :
+  enableval=$enable_host_bind_now; LD_PICFLAG="$LD_PICFLAG -Wl,-z,now"
+fi
+
+
+
 
 # Check if O_CLOEXEC is defined by fcntl
 
diff --git a/c++tools/configure.ac b/c++tools/configure.ac
index 44dfacc..c2a1660 100644
--- a/c++tools/configure.ac
+++ b/c++tools/configure.ac
@@ -110,6 +110,13 @@
 		[build host code as PIE])],
 [PICFLAG=-fPIE; LD_PICFLAG=-pie], [])
 AC_SUBST(PICFLAG)
+
+# Enable --enable-host-bind-now
+AC_ARG_ENABLE(host-bind-now,
+[AS_HELP_STRING([--enable-host-bind-now],
+       [link host code as BIND_NOW])],
+[LD_PICFLAG="$LD_PICFLAG -Wl,-z,now"], [])
+AC_SUBST(enable_host_bind_now)
 AC_SUBST(LD_PICFLAG)
 
 # Check if O_CLOEXEC is defined by fcntl
diff --git a/configure b/configure
index 5715912..0d3f5c6 100755
--- a/configure
+++ b/configure
@@ -689,6 +689,7 @@
 host_libs_picflag
 PICFLAG
 host_shared
+gcc_host_pie
 host_pie
 extra_linker_plugin_flags
 extra_linker_plugin_configure_flags
@@ -8649,23 +8650,31 @@
 
 
 
-# Enable --enable-host-pie.
-# Checked early to determine whether jit is an 'all' language
+# Handle --enable-host-pie
+# If host PIE executables are the default (or must be forced on) for some host,
+# we must pass that configuration to the gcc directory.
+gcc_host_pie=
 # Check whether --enable-host-pie was given.
 if test "${enable_host_pie+set}" = set; then :
   enableval=$enable_host_pie; host_pie=$enableval
  case $host in
-   x86_64-*-darwin* | aarch64-*-darwin*)
+   *-*-darwin2*)
      if test x$host_pie != xyes ; then
-       # PIC is the default, and actually cannot be switched off.
-       echo configure.ac: warning: PIC code is required for the configured target, host-shared setting ignored. 1>&2
+       # for Darwin20+ this is required.
+       { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: PIE executables are required for the configured host, host-pie setting ignored." >&5
+$as_echo "$as_me: WARNING: PIE executables are required for the configured host, host-pie setting ignored." >&2;}
        host_pie=yes
+       gcc_host_pie=--enable-host-pie
      fi ;;
   *) ;;
  esac
 else
   case $host in
-  *-*-darwin2*) host_pie=yes ;;
+  *-*-darwin2*)
+    # Default to PIE (mandatory for aarch64).
+    host_pie=yes
+    gcc_host_pie=--enable-host-pie
+    ;;
   *) host_pie=no ;;
  esac
 fi
@@ -8673,6 +8682,7 @@
 
 
 
+
 # Enable --enable-host-shared.
 # Checked early to determine whether jit is an 'all' language
 # Check whether --enable-host-shared was given.
@@ -8682,21 +8692,24 @@
    x86_64-*-darwin* | aarch64-*-darwin*)
      if test x$host_shared != xyes ; then
        # PIC is the default, and actually cannot be switched off.
-       echo configure.ac: warning: PIC code is required for the configured target, host-shared setting ignored. 1>&2
+       { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: PIC code is required for the configured host; host-shared setting ignored." >&5
+$as_echo "$as_me: WARNING: PIC code is required for the configured host; host-shared setting ignored." >&2;}
        host_shared=yes
      fi ;;
    *-*-darwin*)
-     if test x$host_pie == xyes ; then
-       echo configure.ac: warning: PIC code is required for PIE executables. 1>&2
+     if test x$host_pie = xyes -a x$host_shared != xyes ; then
+       { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: PIC code is required for PIE host executables host-shared setting ignored." >&5
+$as_echo "$as_me: WARNING: PIC code is required for PIE host executables host-shared setting ignored." >&2;}
        host_shared=yes
      fi ;;
   *) ;;
  esac
 else
   case $host in
+  # 64B x86_64 and Aarch64 Darwin default to PIC.
   x86_64-*-darwin* | aarch64-*-darwin*) host_shared=yes ;;
-  # Darwin needs PIC objects to link PIE executables.
-  *-*-darwin*) host_shared=host_pie ;;
+  # 32B and powerpc64 Darwin must use PIC to link PIE exes.
+  *-*-darwin*) host_shared=$host_pie ;;
   *) host_shared=no;;
  esac
 fi
@@ -8705,7 +8718,18 @@
 
 
 if test x$host_shared = xyes; then
-  PICFLAG=-fPIC
+  case $host in
+    *-*-darwin*)
+      # Since host shared is the default for 64b Darwin, and also enabled for
+      # host_pie, ensure that we present the PIE flag when host_pie is active.
+      if test x$host_pie = xyes; then
+        PICFLAG=-fPIE
+      fi
+      ;;
+    *)
+      PICFLAG=-fPIC
+      ;;
+  esac
 elif test x$host_pie = xyes; then
   PICFLAG=-fPIE
 else
diff --git a/configure.ac b/configure.ac
index 11b68b7..dddab2a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1891,27 +1891,35 @@
   extra_linker_plugin_flags=)
 AC_SUBST(extra_linker_plugin_flags)
 
-# Enable --enable-host-pie.
-# Checked early to determine whether jit is an 'all' language
+# Handle --enable-host-pie
+# If host PIE executables are the default (or must be forced on) for some host,
+# we must pass that configuration to the gcc directory.
+gcc_host_pie=
 AC_ARG_ENABLE(host-pie,
 [AS_HELP_STRING([--enable-host-pie],
 		[build position independent host executables])],
 [host_pie=$enableval
  case $host in
-   x86_64-*-darwin* | aarch64-*-darwin*)
+   *-*-darwin2*)
      if test x$host_pie != xyes ; then
-       # PIC is the default, and actually cannot be switched off.
-       echo configure.ac: warning: PIC code is required for the configured target, host-shared setting ignored. 1>&2
+       # for Darwin20+ this is required.
+       AC_MSG_WARN([PIE executables are required for the configured host, host-pie setting ignored.])
        host_pie=yes
+       gcc_host_pie=--enable-host-pie
      fi ;;
   *) ;;
  esac],
 [case $host in
-  *-*-darwin2*) host_pie=yes ;;
+  *-*-darwin2*)
+    # Default to PIE (mandatory for aarch64).
+    host_pie=yes
+    gcc_host_pie=--enable-host-pie
+    ;;
   *) host_pie=no ;;
  esac])
 
 AC_SUBST(host_pie)
+AC_SUBST(gcc_host_pie)
 
 # Enable --enable-host-shared.
 # Checked early to determine whether jit is an 'all' language
@@ -1923,27 +1931,39 @@
    x86_64-*-darwin* | aarch64-*-darwin*)
      if test x$host_shared != xyes ; then
        # PIC is the default, and actually cannot be switched off.
-       echo configure.ac: warning: PIC code is required for the configured target, host-shared setting ignored. 1>&2
+       AC_MSG_WARN([PIC code is required for the configured host; host-shared setting ignored.])
        host_shared=yes
      fi ;;
    *-*-darwin*)
-     if test x$host_pie == xyes ; then
-       echo configure.ac: warning: PIC code is required for PIE executables. 1>&2
+     if test x$host_pie = xyes -a x$host_shared != xyes ; then
+       AC_MSG_WARN([PIC code is required for PIE host executables host-shared setting ignored.])
        host_shared=yes
      fi ;;
   *) ;;
  esac],
 [case $host in
+  # 64B x86_64 and Aarch64 Darwin default to PIC.
   x86_64-*-darwin* | aarch64-*-darwin*) host_shared=yes ;;
-  # Darwin needs PIC objects to link PIE executables.
-  *-*-darwin*) host_shared=host_pie ;;
+  # 32B and powerpc64 Darwin must use PIC to link PIE exes.
+  *-*-darwin*) host_shared=$host_pie ;;
   *) host_shared=no;;
  esac])
 
 AC_SUBST(host_shared)
 
 if test x$host_shared = xyes; then
-  PICFLAG=-fPIC
+  case $host in
+    *-*-darwin*)
+      # Since host shared is the default for 64b Darwin, and also enabled for
+      # host_pie, ensure that we present the PIE flag when host_pie is active.
+      if test x$host_pie = xyes; then
+        PICFLAG=-fPIE
+      fi
+      ;;
+    *)
+      PICFLAG=-fPIC
+      ;;
+  esac
 elif test x$host_pie = xyes; then
   PICFLAG=-fPIE
 else
diff --git a/contrib/ChangeLog b/contrib/ChangeLog
index 9b73cdd..92af01d 100644
--- a/contrib/ChangeLog
+++ b/contrib/ChangeLog
@@ -1,3 +1,9 @@
+2023-06-22  David Malcolm  <dmalcolm@redhat.com>
+
+	* unicode/gen-box-drawing-chars.py: New file.
+	* unicode/gen-combining-chars.py: New file.
+	* unicode/gen-printable-chars.py: New file.
+
 2023-06-17  Thiago Jung Bauermann  <thiago.bauermann@linaro.org>
 
 	* testsuite-management/validate_failures.py (IsInterestingResult):
diff --git a/contrib/unicode/gen-box-drawing-chars.py b/contrib/unicode/gen-box-drawing-chars.py
new file mode 100755
index 0000000..9a55266
--- /dev/null
+++ b/contrib/unicode/gen-box-drawing-chars.py
@@ -0,0 +1,94 @@
+#!/usr/bin/env python3
+#
+# Script to generate gcc/text-art/box-drawing-chars.inc
+#
+# This file is part of GCC.
+#
+# GCC is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free
+# Software Foundation; either version 3, or (at your option) any later
+# version.
+#
+# GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.  */
+
+import unicodedata
+
+def get_box_drawing_char_name(up: bool,
+                              down: bool,
+                              left: bool,
+                              right: bool) -> str:
+    if 0:
+        print(f'{locals()=}')
+    if up and down:
+        vertical = True
+        up = False
+        down = False
+    else:
+        vertical = False
+
+    if left and right:
+        horizontal = True
+        left = False
+        right = False
+    else:
+        horizontal = False
+
+    weights = []
+    heavy = []
+    light = []
+    dirs = []
+    for dir_name in ('up', 'down', 'vertical', 'left', 'right', 'horizontal'):
+        val = locals()[dir_name]
+        if val:
+            dirs.append(dir_name.upper())
+
+    if not dirs:
+        return 'SPACE'
+
+    name = 'BOX DRAWINGS'
+    #print(f'{light=} {heavy=}')
+
+    if 0:
+        print(dirs)
+
+    def weights_frag(weight: str, dirs: list, prefix: bool):
+        """
+        Generate a fragment where all directions share the same weight, e.g.:
+        'HEAVY HORIZONTAL'
+        'DOWN LIGHT'
+        'LEFT DOWN HEAVY'
+        'HEAVY DOWN AND RIGHT'
+        """
+        assert len(dirs) >= 1
+        assert len(dirs) <= 2
+        if prefix:
+            return f' {weight} ' + (' AND '.join(dirs))
+        else:
+            return ' ' + (' '.join(dirs)) + f' {weight}'
+
+    assert(len(dirs) >= 1 and len(dirs) <= 2)
+    name += weights_frag('LIGHT', dirs, True)
+
+    return name
+
+print('/* Generated by contrib/unicode/gen-box-drawing-chars.py.  */')
+print()
+for i in range(16):
+    up = (i & 8)
+    down = (i & 4)
+    left = (i & 2)
+    right = (i & 1)
+    name = get_box_drawing_char_name(up, down, left, right)
+    if i < 15:
+        trailing_comma = ','
+    else:
+        trailing_comma = ' '
+    unichar = unicodedata.lookup(name)
+    print(f'0x{ord(unichar):04X}{trailing_comma} /* "{unichar}": U+{ord(unichar):04X}: {name} */')
diff --git a/contrib/unicode/gen-combining-chars.py b/contrib/unicode/gen-combining-chars.py
new file mode 100755
index 0000000..fb5ef50
--- /dev/null
+++ b/contrib/unicode/gen-combining-chars.py
@@ -0,0 +1,75 @@
+#!/usr/bin/env python3
+#
+# Script to generate libcpp/combining-chars.inc
+#
+# This file is part of GCC.
+#
+# GCC is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free
+# Software Foundation; either version 3, or (at your option) any later
+# version.
+#
+# GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.  */
+
+from pprint import pprint
+import unicodedata
+
+def is_combining_char(code_point) -> bool:
+    return unicodedata.combining(chr(code_point)) != 0
+
+class Range:
+    def __init__(self, start, end, value):
+        self.start = start
+        self.end = end
+        self.value = value
+
+    def __repr__(self):
+        return f'Range({self.start:x}, {self.end:x}, {self.value})'
+
+def make_ranges(value_callback):
+    ranges = []
+    for code_point in range(0x10FFFF):
+        value = is_combining_char(code_point)
+        if 0:
+            print(f'{code_point=:x} {value=}')
+        if ranges and ranges[-1].value == value:
+            # Extend current range
+            ranges[-1].end = code_point
+        else:
+            # Start a new range
+            ranges.append(Range(code_point, code_point, value))
+    return ranges
+
+ranges = make_ranges(is_combining_char)
+if 0:
+    pprint(ranges)
+
+print(f"/* Generated by contrib/unicode/gen-combining-chars.py")
+print(f"   using version {unicodedata.unidata_version}"
+      " of the Unicode standard.  */")
+print("\nstatic const cppchar_t combining_range_ends[] = {", end="")
+for i, r in enumerate(ranges):
+    if i % 8:
+        print(" ", end="")
+    else:
+        print("\n  ", end="")
+    print("0x%x," % r.end, end="")
+print("\n};\n")
+print("static const bool is_combining[] = {", end="")
+for i, r in enumerate(ranges):
+    if i % 24:
+        print(" ", end="")
+    else:
+        print("\n  ", end="")
+    if r.value:
+        print("1,", end="")
+    else:
+        print("0,", end="")
+print("\n};")
diff --git a/contrib/unicode/gen-printable-chars.py b/contrib/unicode/gen-printable-chars.py
new file mode 100755
index 0000000..7684c08
--- /dev/null
+++ b/contrib/unicode/gen-printable-chars.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python3
+#
+# Script to generate libcpp/printable-chars.inc
+#
+# This file is part of GCC.
+#
+# GCC is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free
+# Software Foundation; either version 3, or (at your option) any later
+# version.
+#
+# GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.  */
+
+from pprint import pprint
+import unicodedata
+
+def is_printable_char(code_point) -> bool:
+    category = unicodedata.category(chr(code_point))
+    # "Cc" is "control" and "Cf" is "format"
+    return category[0] != 'C'
+
+class Range:
+    def __init__(self, start, end, value):
+        self.start = start
+        self.end = end
+        self.value = value
+
+    def __repr__(self):
+        return f'Range({self.start:x}, {self.end:x}, {self.value})'
+
+def make_ranges(value_callback):
+    ranges = []
+    for code_point in range(0x10FFFF):
+        value = is_printable_char(code_point)
+        if 0:
+            print(f'{code_point=:x} {value=}')
+        if ranges and ranges[-1].value == value:
+            # Extend current range
+            ranges[-1].end = code_point
+        else:
+            # Start a new range
+            ranges.append(Range(code_point, code_point, value))
+    return ranges
+
+ranges = make_ranges(is_printable_char)
+if 0:
+    pprint(ranges)
+
+print(f"/* Generated by contrib/unicode/gen-printable-chars.py")
+print(f"   using version {unicodedata.unidata_version}"
+      " of the Unicode standard.  */")
+print("\nstatic const cppchar_t printable_range_ends[] = {", end="")
+for i, r in enumerate(ranges):
+    if i % 8:
+        print(" ", end="")
+    else:
+        print("\n  ", end="")
+    print("0x%x," % r.end, end="")
+print("\n};\n")
+print("static const bool is_printable[] = {", end="")
+for i, r in enumerate(ranges):
+    if i % 24:
+        print(" ", end="")
+    else:
+        print("\n  ", end="")
+    if r.value:
+        print("1,", end="")
+    else:
+        print("0,", end="")
+print("\n};")
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 2d9e425..a7d51b5 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,646 @@
+2023-06-25  Juzhe-Zhong  <juzhe.zhong@rivai.ai>
+
+	* config/riscv/riscv-vsetvl.cc (vector_insn_info::parse_insn): Ehance
+	AVL propagation.
+	* config/riscv/riscv-vsetvl.h: New function.
+
+2023-06-25  Li Xu  <xuli1@eswincomputing.com>
+
+	* config/riscv/riscv-vector-builtins-bases.cc: change emit_insn to
+	emit_move_insn
+
+2023-06-25  Juzhe-Zhong  <juzhe.zhong@rivai.ai>
+
+	* config/riscv/autovec.md (len_load_<mode>): Remove.
+	(len_maskload<mode><vm>): Remove.
+	(len_store_<mode>): New pattern.
+	(len_maskstore<mode><vm>): New pattern.
+	* config/riscv/predicates.md (autovec_length_operand): New predicate.
+	* config/riscv/riscv-protos.h (enum insn_type): New enum.
+	(expand_load_store): New function.
+	* config/riscv/riscv-v.cc (emit_vlmax_masked_insn): Ditto.
+	(emit_nonvlmax_masked_insn): Ditto.
+	(expand_load_store): Ditto.
+	* config/riscv/riscv-vector-builtins.cc
+	(function_expander::use_contiguous_store_insn): Add avl_type operand
+	into pred_store.
+	* config/riscv/vector.md: Ditto.
+
+2023-06-25  Ju-Zhe Zhong  <juzhe.zhong@rivai.ai>
+
+	* internal-fn.cc (expand_partial_store_optab_fn): Fix bug of BIAS
+	argument index.
+
+2023-06-25  Pan Li  <pan2.li@intel.com>
+
+	* config/riscv/vector.md: Revert.
+
+2023-06-25  Pan Li  <pan2.li@intel.com>
+
+	* config/riscv/genrvv-type-indexer.cc (valid_type): Revert changes.
+	* config/riscv/riscv-modes.def (RVV_TUPLE_MODES): Ditto.
+	(ADJUST_ALIGNMENT): Ditto.
+	(RVV_TUPLE_PARTIAL_MODES): Ditto.
+	(ADJUST_NUNITS): Ditto.
+	* config/riscv/riscv-vector-builtins-types.def (vfloat16mf4x2_t): Ditto.
+	(vfloat16mf4x3_t): Ditto.
+	(vfloat16mf4x4_t): Ditto.
+	(vfloat16mf4x5_t): Ditto.
+	(vfloat16mf4x6_t): Ditto.
+	(vfloat16mf4x7_t): Ditto.
+	(vfloat16mf4x8_t): Ditto.
+	(vfloat16mf2x2_t): Ditto.
+	(vfloat16mf2x3_t): Ditto.
+	(vfloat16mf2x4_t): Ditto.
+	(vfloat16mf2x5_t): Ditto.
+	(vfloat16mf2x6_t): Ditto.
+	(vfloat16mf2x7_t): Ditto.
+	(vfloat16mf2x8_t): Ditto.
+	(vfloat16m1x2_t): Ditto.
+	(vfloat16m1x3_t): Ditto.
+	(vfloat16m1x4_t): Ditto.
+	(vfloat16m1x5_t): Ditto.
+	(vfloat16m1x6_t): Ditto.
+	(vfloat16m1x7_t): Ditto.
+	(vfloat16m1x8_t): Ditto.
+	(vfloat16m2x2_t): Ditto.
+	(vfloat16m2x3_t): Diito.
+	(vfloat16m2x4_t): Diito.
+	(vfloat16m4x2_t): Diito.
+	* config/riscv/riscv-vector-builtins.def (vfloat16mf4x2_t): Ditto.
+	(vfloat16mf4x3_t): Ditto.
+	(vfloat16mf4x4_t): Ditto.
+	(vfloat16mf4x5_t): Ditto.
+	(vfloat16mf4x6_t): Ditto.
+	(vfloat16mf4x7_t): Ditto.
+	(vfloat16mf4x8_t): Ditto.
+	(vfloat16mf2x2_t): Ditto.
+	(vfloat16mf2x3_t): Ditto.
+	(vfloat16mf2x4_t): Ditto.
+	(vfloat16mf2x5_t): Ditto.
+	(vfloat16mf2x6_t): Ditto.
+	(vfloat16mf2x7_t): Ditto.
+	(vfloat16mf2x8_t): Ditto.
+	(vfloat16m1x2_t): Ditto.
+	(vfloat16m1x3_t): Ditto.
+	(vfloat16m1x4_t): Ditto.
+	(vfloat16m1x5_t): Ditto.
+	(vfloat16m1x6_t): Ditto.
+	(vfloat16m1x7_t): Ditto.
+	(vfloat16m1x8_t): Ditto.
+	(vfloat16m2x2_t): Ditto.
+	(vfloat16m2x3_t): Ditto.
+	(vfloat16m2x4_t): Ditto.
+	(vfloat16m4x2_t): Ditto.
+	* config/riscv/riscv-vector-switch.def (TUPLE_ENTRY): Ditto.
+	* config/riscv/riscv.md: Ditto.
+	* config/riscv/vector-iterators.md: Ditto.
+
+2023-06-25  Ju-Zhe Zhong  <juzhe.zhong@rivai.ai>
+
+	* gimple-fold.cc (arith_overflowed_p): Apply LEN_MASK_{LOAD,STORE}.
+	(gimple_fold_partial_load_store_mem_ref): Ditto.
+	(gimple_fold_partial_store): Ditto.
+	(gimple_fold_call): Ditto.
+
+2023-06-25  liuhongt  <hongtao.liu@intel.com>
+
+	PR target/110309
+	* config/i386/sse.md (maskload<mode><avx512fmaskmodelower>):
+	Refine pattern with UNSPEC_MASKLOAD.
+	(maskload<mode><avx512fmaskmodelower>): Ditto.
+	(*<avx512>_load<mode>_mask): Extend mode iterator to
+	VI12HFBF_AVX512VL.
+	(*<avx512>_load<mode>): Ditto.
+
+2023-06-25  Ju-Zhe Zhong  <juzhe.zhong@rivai.ai>
+
+	* tree-ssa-alias.cc (call_may_clobber_ref_p_1): Add LEN_MASK_STORE.
+
+2023-06-25  Ju-Zhe Zhong  <juzhe.zhong@rivai.ai>
+
+	* tree-ssa-alias.cc (ref_maybe_used_by_call_p_1): Apply
+	LEN_MASK_{LOAD,STORE}
+
+2023-06-25  yulong  <shiyulong@iscas.ac.cn>
+
+	* config/riscv/vector.md: Add float16 attr at sew、vlmul and ratio.
+
+2023-06-24  Roger Sayle  <roger@nextmovesoftware.com>
+
+	* config/i386/i386.md (*<code>qi_ext<mode>_3): New define_insn.
+
+2023-06-24  Juzhe-Zhong  <juzhe.zhong@rivai.ai>
+
+	* config/riscv/autovec.md (*fma<mode>): set clobber to Pmode in expand stage.
+	(*fma<VI:mode><P:mode>): Ditto.
+	(*fnma<mode>): Ditto.
+	(*fnma<VI:mode><P:mode>): Ditto.
+
+2023-06-24  Juzhe-Zhong  <juzhe.zhong@rivai.ai>
+
+	* config/riscv/autovec.md (fma<mode>4): New pattern.
+	(*fma<mode>): Ditto.
+	(fnma<mode>4): Ditto.
+	(*fnma<mode>): Ditto.
+	(fms<mode>4): Ditto.
+	(*fms<mode>): Ditto.
+	(fnms<mode>4): Ditto.
+	(*fnms<mode>): Ditto.
+	* config/riscv/riscv-protos.h (emit_vlmax_fp_ternary_insn):
+	New function.
+	* config/riscv/riscv-v.cc (emit_vlmax_fp_ternary_insn): Ditto.
+	* config/riscv/vector.md: Fix attribute bug.
+
+2023-06-24  Ju-Zhe Zhong  <juzhe.zhong@rivai.ai>
+
+	* tree-ssa-loop-ivopts.cc (get_mem_type_for_internal_fn):
+	Apply LEN_MASK_{LOAD,STORE}.
+
+2023-06-24  Ju-Zhe Zhong  <juzhe.zhong@rivai.ai>
+
+	* tree-ssa-loop-ivopts.cc (get_alias_ptr_type_for_ptr_address):
+	Add LEN_MASK_{LOAD,STORE}.
+
+2023-06-24  David Malcolm  <dmalcolm@redhat.com>
+
+	* diagnostic-format-sarif.cc: Add #define INCLUDE_VECTOR.
+	* diagnostic.cc: Likewise.
+	* text-art/box-drawing.cc: Likewise.
+	* text-art/canvas.cc: Likewise.
+	* text-art/ruler.cc: Likewise.
+	* text-art/selftests.cc: Likewise.
+	* text-art/selftests.h (text_art::canvas): New forward decl.
+	* text-art/style.cc: Add #define INCLUDE_VECTOR.
+	* text-art/styled-string.cc: Likewise.
+	* text-art/table.cc: Likewise.
+	* text-art/table.h: Remove #include <vector>.
+	* text-art/theme.cc: Add #define INCLUDE_VECTOR.
+	* text-art/types.h: Check that INCLUDE_VECTOR is defined.
+	Remove #include of <vector> and <string>.
+	* text-art/widget.cc: Add #define INCLUDE_VECTOR.
+	* text-art/widget.h: Remove #include <vector>.
+
+2023-06-24  Ju-Zhe Zhong  <juzhe.zhong@rivai.ai>
+
+	* internal-fn.cc (expand_partial_store_optab_fn): Adapt for LEN_MASK_STORE.
+	(internal_load_fn_p): Add LEN_MASK_LOAD.
+	(internal_store_fn_p): Add LEN_MASK_STORE.
+	(internal_fn_mask_index): Add LEN_MASK_{LOAD,STORE}.
+	(internal_fn_stored_value_index): Add LEN_MASK_STORE.
+	(internal_len_load_store_bias):  Add LEN_MASK_{LOAD,STORE}.
+	* optabs-tree.cc (can_vec_mask_load_store_p): Adapt for LEN_MASK_{LOAD,STORE}.
+	(get_len_load_store_mode): Ditto.
+	* optabs-tree.h (can_vec_mask_load_store_p): Ditto.
+	(get_len_load_store_mode): Ditto.
+	* tree-vect-stmts.cc (check_load_store_for_partial_vectors): Ditto.
+	(get_all_ones_mask): New function.
+	(vectorizable_store): Apply LEN_MASK_{LOAD,STORE} into vectorizer.
+	(vectorizable_load): Ditto.
+
+2023-06-23  Marek Polacek  <polacek@redhat.com>
+
+	* doc/cpp.texi (__cplusplus): Document value for -std=c++26 and
+	-std=gnu++26.  Document that for C++23, its value is 202302L.
+	* doc/invoke.texi: Document -std=c++26 and -std=gnu++26.
+	* dwarf2out.cc (highest_c_language): Handle GNU C++26.
+	(gen_compile_unit_die): Likewise.
+
+2023-06-23  Jan Hubicka  <jh@suse.cz>
+
+	* tree-ssa-phiprop.cc (propagate_with_phi): Compute post dominators on
+	demand.
+	(pass_phiprop::execute): Do not compute it here; return
+	update_ssa_only_virtuals if something changed.
+	(pass_data_phiprop): Remove TODO_update_ssa from todos.
+
+2023-06-23   Michael Meissner  <meissner@linux.ibm.com>
+	    Aaron Sawdey   <acsawdey@linux.ibm.com>
+
+	PR target/105325
+	* config/rs6000/genfusion.pl (gen_ld_cmpi_p10_one): Fix problems that
+	allowed prefixed lwa to be generated.
+	* config/rs6000/fusion.md: Regenerate.
+	* config/rs6000/predicates.md (ds_form_mem_operand): Delete.
+	* config/rs6000/rs6000.md (prefixed attribute): Add support for load
+	plus compare immediate fused insns.
+	(maybe_prefixed): Likewise.
+
+2023-06-23  Roger Sayle  <roger@nextmovesoftware.com>
+
+	* simplify-rtx.cc (simplify_subreg):  Optimize lowpart SUBREGs
+	of ASHIFT to const0_rtx with sufficiently large shift count.
+	Optimize highpart SUBREGs of ASHIFT as the shift operand when
+	the shift count is the correct offset.  Optimize SUBREGs of
+	multi-word logic operations if the SUBREGs of both operands
+	can be simplified.
+
+2023-06-23  Richard Biener  <rguenther@suse.de>
+
+	* varasm.cc (initializer_constant_valid_p_1): Only
+	allow conversions between scalar floating point types.
+
+2023-06-23  Richard Biener  <rguenther@suse.de>
+
+	* tree-vect-stmts.cc (vectorizable_assignment):
+	Properly handle non-integral operands when analyzing
+	conversions.
+
+2023-06-23  Prathamesh Kulkarni  <prathamesh.kulkarni@linaro.org>
+
+	PR tree-optimization/110280
+	* match.pd (vec_perm_expr(v, v, mask) -> v): Explicitly build vector
+	using build_vector_from_val with the element of input operand, and
+	mask's type if operand and mask's types don't match.
+
+2023-06-23  Richard Biener  <rguenther@suse.de>
+
+	* fold-const.cc (tree_simple_nonnegative_warnv_p): Guard
+	the truth_value_p case with !VECTOR_TYPE_P.
+
+2023-06-23  Richard Biener  <rguenther@suse.de>
+
+	* tree-vect-patterns.cc (vect_look_through_possible_promotion):
+	Exit early when the type isn't scalar integral.
+
+2023-06-23  Richard Biener  <rguenther@suse.de>
+
+	* match.pd ((outertype)((innertype0)a+(innertype1)b)
+	-> ((newtype)a+(newtype)b)): Use element_precision
+	where appropriate.
+
+2023-06-23  Richard Biener  <rguenther@suse.de>
+
+	* fold-const.cc (fold_binary_loc): Use element_precision
+	when trying (double)float1 CMP (double)float2 to
+	float1 CMP float2 simplification.
+	* match.pd: Likewise.
+
+2023-06-23  Richard Biener  <rguenther@suse.de>
+
+	* tree-vect-stmts.cc (vectorizable_load): Avoid useless
+	copies of VMAT_INVARIANT vectorized stmts, fix SLP support.
+
+2023-06-23  Richard Biener  <rguenther@suse.de>
+
+	* tree-vect-stmts.cc (vector_vector_composition_type):
+	Handle composition of a vector from a number of elements that
+	happens to match its number of lanes.
+
+2023-06-22  Marek Polacek  <polacek@redhat.com>
+
+	* configure.ac (--enable-host-bind-now): New check.  Add
+	-Wl,-z,now to LD_PICFLAG if --enable-host-bind-now.
+	* configure: Regenerate.
+	* doc/install.texi: Document --enable-host-bind-now.
+
+2023-06-22  Di Zhao OS  <dizhao@os.amperecomputing.com>
+
+	* config/aarch64/aarch64.cc: Change fma_reassoc_width for ampere1.
+
+2023-06-22  Richard Biener  <rguenther@suse.de>
+
+	PR tree-optimization/110332
+	* tree-ssa-phiprop.cc (propagate_with_phi): Always
+	check aliasing with edge inserted loads.
+
+2023-06-22  Roger Sayle  <roger@nextmovesoftware.com>
+	    Uros Bizjak  <ubizjak@gmail.com>
+
+	* config/i386/i386-expand.cc (ix86_expand_sse_ptest): Recognize
+	expansion of ptestc with equal operands as producing const1_rtx.
+	* config/i386/i386.cc (ix86_rtx_costs): Provide accurate cost
+	estimates of UNSPEC_PTEST, where the ptest performs the PAND
+	or PAND of its operands.
+	* config/i386/sse.md (define_split): Transform CCCmode UNSPEC_PTEST
+	of reg_equal_p operands into an x86_stc instruction.
+	(define_split): Split pandn/ptestz/set{n?}e into ptestc/set{n?}c.
+	(define_split): Similar to above for strict_low_part destinations.
+	(define_split): Split pandn/ptestz/j{n?}e into ptestc/j{n?}c.
+
+2023-06-22  David Malcolm  <dmalcolm@redhat.com>
+
+	PR analyzer/106626
+	* Makefile.in (ANALYZER_OBJS): Add analyzer/access-diagram.o.
+	* doc/invoke.texi (Wanalyzer-out-of-bounds): Add description of
+	text art.
+	(fanalyzer-debug-text-art): New.
+
+2023-06-22  David Malcolm  <dmalcolm@redhat.com>
+
+	* Makefile.in (OBJS-libcommon): Add text-art/box-drawing.o,
+	text-art/canvas.o, text-art/ruler.o, text-art/selftests.o,
+	text-art/style.o, text-art/styled-string.o, text-art/table.o,
+	text-art/theme.o, and text-art/widget.o.
+	* color-macros.h (COLOR_FG_BRIGHT_BLACK): New.
+	(COLOR_FG_BRIGHT_RED): New.
+	(COLOR_FG_BRIGHT_GREEN): New.
+	(COLOR_FG_BRIGHT_YELLOW): New.
+	(COLOR_FG_BRIGHT_BLUE): New.
+	(COLOR_FG_BRIGHT_MAGENTA): New.
+	(COLOR_FG_BRIGHT_CYAN): New.
+	(COLOR_FG_BRIGHT_WHITE): New.
+	(COLOR_BG_BRIGHT_BLACK): New.
+	(COLOR_BG_BRIGHT_RED): New.
+	(COLOR_BG_BRIGHT_GREEN): New.
+	(COLOR_BG_BRIGHT_YELLOW): New.
+	(COLOR_BG_BRIGHT_BLUE): New.
+	(COLOR_BG_BRIGHT_MAGENTA): New.
+	(COLOR_BG_BRIGHT_CYAN): New.
+	(COLOR_BG_BRIGHT_WHITE): New.
+	* common.opt (fdiagnostics-text-art-charset=): New option.
+	(diagnostic-text-art.h): New SourceInclude.
+	(diagnostic_text_art_charset) New Enum and EnumValues.
+	* configure: Regenerate.
+	* configure.ac (gccdepdir): Add text-art to loop.
+	* diagnostic-diagram.h: New file.
+	* diagnostic-format-json.cc (json_emit_diagram): New.
+	(diagnostic_output_format_init_json): Wire it up to
+	context->m_diagrams.m_emission_cb.
+	* diagnostic-format-sarif.cc: Include "diagnostic-diagram.h" and
+	"text-art/canvas.h".
+	(sarif_result::on_nested_diagnostic): Move code to...
+	(sarif_result::add_related_location): ...this new function.
+	(sarif_result::on_diagram): New.
+	(sarif_builder::emit_diagram): New.
+	(sarif_builder::make_message_object_for_diagram): New.
+	(sarif_emit_diagram): New.
+	(diagnostic_output_format_init_sarif): Set
+	context->m_diagrams.m_emission_cb to sarif_emit_diagram.
+	* diagnostic-text-art.h: New file.
+	* diagnostic.cc: Include "diagnostic-text-art.h",
+	"diagnostic-diagram.h", and "text-art/theme.h".
+	(diagnostic_initialize): Initialize context->m_diagrams and
+	call diagnostics_text_art_charset_init.
+	(diagnostic_finish): Clean up context->m_diagrams.m_theme.
+	(diagnostic_emit_diagram): New.
+	(diagnostics_text_art_charset_init): New.
+	* diagnostic.h (text_art::theme): New forward decl.
+	(class diagnostic_diagram): Likewise.
+	(diagnostic_context::m_diagrams): New field.
+	(diagnostic_emit_diagram): New decl.
+	* doc/invoke.texi (Diagnostic Message Formatting Options): Add
+	-fdiagnostics-text-art-charset=.
+	(-fdiagnostics-plain-output): Add
+	-fdiagnostics-text-art-charset=none.
+	* gcc.cc: Include "diagnostic-text-art.h".
+	(driver_handle_option): Handle OPT_fdiagnostics_text_art_charset_.
+	* opts-common.cc (decode_cmdline_options_to_array): Add
+	"-fdiagnostics-text-art-charset=none" to expanded_args for
+	-fdiagnostics-plain-output.
+	* opts.cc: Include "diagnostic-text-art.h".
+	(common_handle_option): Handle OPT_fdiagnostics_text_art_charset_.
+	* pretty-print.cc (pp_unicode_character): New.
+	* pretty-print.h (pp_unicode_character): New decl.
+	* selftest-run-tests.cc: Include "text-art/selftests.h".
+	(selftest::run_tests): Call text_art_tests.
+	* text-art/box-drawing-chars.inc: New file, generated by
+	contrib/unicode/gen-box-drawing-chars.py.
+	* text-art/box-drawing.cc: New file.
+	* text-art/box-drawing.h: New file.
+	* text-art/canvas.cc: New file.
+	* text-art/canvas.h: New file.
+	* text-art/ruler.cc: New file.
+	* text-art/ruler.h: New file.
+	* text-art/selftests.cc: New file.
+	* text-art/selftests.h: New file.
+	* text-art/style.cc: New file.
+	* text-art/styled-string.cc: New file.
+	* text-art/table.cc: New file.
+	* text-art/table.h: New file.
+	* text-art/theme.cc: New file.
+	* text-art/theme.h: New file.
+	* text-art/types.h: New file.
+	* text-art/widget.cc: New file.
+	* text-art/widget.h: New file.
+
+2023-06-21  Uros Bizjak  <ubizjak@gmail.com>
+
+	* function.h (emit_initial_value_sets):
+	Change return type from int to void.
+	(aggregate_value_p): Change return type from int to bool.
+	(prologue_contains): Ditto.
+	(epilogue_contains): Ditto.
+	(prologue_epilogue_contains): Ditto.
+	* function.cc (temp_slot): Make "in_use" variable bool.
+	(make_slot_available): Update for changed "in_use" variable.
+	(assign_stack_temp_for_type): Ditto.
+	(emit_initial_value_sets): Change return type from int to void
+	and update function body accordingly.
+	(instantiate_virtual_regs): Ditto.
+	(rest_of_handle_thread_prologue_and_epilogue): Ditto.
+	(safe_insn_predicate): Change return type from int to bool.
+	(aggregate_value_p): Change return type from int to bool
+	and update function body accordingly.
+	(prologue_contains): Change return type from int to bool.
+	(prologue_epilogue_contains): Ditto.
+
+2023-06-21  Alexander Monakov  <amonakov@ispras.ru>
+
+	* common.opt (fp_contract_mode) [on]: Remove fallback.
+	* config/sh/sh.md (*fmasf4): Correct flag_fp_contract_mode test.
+	* doc/invoke.texi (-ffp-contract): Update.
+	* trans-mem.cc (diagnose_tm_1): Skip internal function calls.
+
+2023-06-21  Kyrylo Tkachov  <kyrylo.tkachov@arm.com>
+
+	* config/aarch64/aarch64-sve.md (mask_gather_load<mode><v_int_container>):
+	Add alternatives to prefer to avoid same input and output Z register.
+	(mask_gather_load<mode><v_int_container>): Likewise.
+	(*mask_gather_load<mode><v_int_container>_<su>xtw_unpacked): Likewise.
+	(*mask_gather_load<mode><v_int_container>_sxtw): Likewise.
+	(*mask_gather_load<mode><v_int_container>_uxtw): Likewise.
+	(@aarch64_gather_load_<ANY_EXTEND:optab><SVE_4HSI:mode><SVE_4BHI:mode>):
+	Likewise.
+	(@aarch64_gather_load_<ANY_EXTEND:optab><SVE_2HSDI:mode><SVE_2BHSI:mode>):
+	Likewise.
+	(*aarch64_gather_load_<ANY_EXTEND:optab><SVE_2HSDI:mode>
+	<SVE_2BHSI:mode>_<ANY_EXTEND2:su>xtw_unpacked): Likewise.
+	(*aarch64_gather_load_<ANY_EXTEND:optab><SVE_2HSDI:mode>
+	<SVE_2BHSI:mode>_sxtw): Likewise.
+	(*aarch64_gather_load_<ANY_EXTEND:optab><SVE_2HSDI:mode>
+	<SVE_2BHSI:mode>_uxtw): Likewise.
+	(@aarch64_ldff1_gather<mode>): Likewise.
+	(@aarch64_ldff1_gather<mode>): Likewise.
+	(*aarch64_ldff1_gather<mode>_sxtw): Likewise.
+	(*aarch64_ldff1_gather<mode>_uxtw): Likewise.
+	(@aarch64_ldff1_gather_<ANY_EXTEND:optab><VNx4_WIDE:mode>
+	<VNx4_NARROW:mode>): Likewise.
+	(@aarch64_ldff1_gather_<ANY_EXTEND:optab><VNx2_WIDE:mode>
+	<VNx2_NARROW:mode>): Likewise.
+	(*aarch64_ldff1_gather_<ANY_EXTEND:optab><VNx2_WIDE:mode>
+	<VNx2_NARROW:mode>_sxtw): Likewise.
+	(*aarch64_ldff1_gather_<ANY_EXTEND:optab><VNx2_WIDE:mode>
+	<VNx2_NARROW:mode>_uxtw): Likewise.
+	* config/aarch64/aarch64-sve2.md (@aarch64_gather_ldnt<mode>): Likewise.
+	(@aarch64_gather_ldnt_<ANY_EXTEND:optab><SVE_FULL_SDI:mode>
+	<SVE_PARTIAL_I:mode>): Likewise.
+
+2023-06-21  Kyrylo Tkachov  <kyrylo.tkachov@arm.com>
+
+	* config/aarch64/aarch64-sve.md (mask_gather_load<mode><v_int_container>):
+	Convert to compact alternatives syntax.
+	(mask_gather_load<mode><v_int_container>): Likewise.
+	(*mask_gather_load<mode><v_int_container>_<su>xtw_unpacked): Likewise.
+	(*mask_gather_load<mode><v_int_container>_sxtw): Likewise.
+	(*mask_gather_load<mode><v_int_container>_uxtw): Likewise.
+	(@aarch64_gather_load_<ANY_EXTEND:optab><SVE_4HSI:mode><SVE_4BHI:mode>):
+	Likewise.
+	(@aarch64_gather_load_<ANY_EXTEND:optab><SVE_2HSDI:mode><SVE_2BHSI:mode>):
+	Likewise.
+	(*aarch64_gather_load_<ANY_EXTEND:optab><SVE_2HSDI:mode>
+	<SVE_2BHSI:mode>_<ANY_EXTEND2:su>xtw_unpacked): Likewise.
+	(*aarch64_gather_load_<ANY_EXTEND:optab><SVE_2HSDI:mode>
+	<SVE_2BHSI:mode>_sxtw): Likewise.
+	(*aarch64_gather_load_<ANY_EXTEND:optab><SVE_2HSDI:mode>
+	<SVE_2BHSI:mode>_uxtw): Likewise.
+	(@aarch64_ldff1_gather<mode>): Likewise.
+	(@aarch64_ldff1_gather<mode>): Likewise.
+	(*aarch64_ldff1_gather<mode>_sxtw): Likewise.
+	(*aarch64_ldff1_gather<mode>_uxtw): Likewise.
+	(@aarch64_ldff1_gather_<ANY_EXTEND:optab><VNx4_WIDE:mode>
+	<VNx4_NARROW:mode>): Likewise.
+	(@aarch64_ldff1_gather_<ANY_EXTEND:optab><VNx2_WIDE:mode>
+	<VNx2_NARROW:mode>): Likewise.
+	(*aarch64_ldff1_gather_<ANY_EXTEND:optab><VNx2_WIDE:mode>
+	<VNx2_NARROW:mode>_sxtw): Likewise.
+	(*aarch64_ldff1_gather_<ANY_EXTEND:optab><VNx2_WIDE:mode>
+	<VNx2_NARROW:mode>_uxtw): Likewise.
+	* config/aarch64/aarch64-sve2.md (@aarch64_gather_ldnt<mode>): Likewise.
+	(@aarch64_gather_ldnt_<ANY_EXTEND:optab><SVE_FULL_SDI:mode>
+	<SVE_PARTIAL_I:mode>): Likewise.
+
+2023-06-21  Kyrylo Tkachov  <kyrylo.tkachov@arm.com>
+
+	Revert:
+	2023-06-21  Kyrylo Tkachov  <kyrylo.tkachov@arm.com>
+
+	* config/aarch64/aarch64-sve.md (mask_gather_load<mode><v_int_container>):
+	Convert to compact alternatives syntax.
+	(mask_gather_load<mode><v_int_container>): Likewise.
+	(*mask_gather_load<mode><v_int_container>_<su>xtw_unpacked): Likewise.
+	(*mask_gather_load<mode><v_int_container>_sxtw): Likewise.
+	(*mask_gather_load<mode><v_int_container>_uxtw): Likewise.
+	(@aarch64_gather_load_<ANY_EXTEND:optab><SVE_4HSI:mode><SVE_4BHI:mode>):
+	Likewise.
+	(@aarch64_gather_load_<ANY_EXTEND:optab><SVE_2HSDI:mode><SVE_2BHSI:mode>):
+	Likewise.
+	(*aarch64_gather_load_<ANY_EXTEND:optab><SVE_2HSDI:mode>
+	<SVE_2BHSI:mode>_<ANY_EXTEND2:su>xtw_unpacked): Likewise.
+	(*aarch64_gather_load_<ANY_EXTEND:optab><SVE_2HSDI:mode>
+	<SVE_2BHSI:mode>_sxtw): Likewise.
+	(*aarch64_gather_load_<ANY_EXTEND:optab><SVE_2HSDI:mode>
+	<SVE_2BHSI:mode>_uxtw): Likewise.
+	(@aarch64_ldff1_gather<mode>): Likewise.
+	(@aarch64_ldff1_gather<mode>): Likewise.
+	(*aarch64_ldff1_gather<mode>_sxtw): Likewise.
+	(*aarch64_ldff1_gather<mode>_uxtw): Likewise.
+	(@aarch64_ldff1_gather_<ANY_EXTEND:optab><VNx4_WIDE:mode>
+	<VNx4_NARROW:mode>): Likewise.
+	(@aarch64_ldff1_gather_<ANY_EXTEND:optab><VNx2_WIDE:mode>
+	<VNx2_NARROW:mode>): Likewise.
+	(*aarch64_ldff1_gather_<ANY_EXTEND:optab><VNx2_WIDE:mode>
+	<VNx2_NARROW:mode>_sxtw): Likewise.
+	(*aarch64_ldff1_gather_<ANY_EXTEND:optab><VNx2_WIDE:mode>
+	<VNx2_NARROW:mode>_uxtw): Likewise.
+	* config/aarch64/aarch64-sve2.md (@aarch64_gather_ldnt<mode>): Likewise.
+	(@aarch64_gather_ldnt_<ANY_EXTEND:optab><SVE_FULL_SDI:mode>
+	<SVE_PARTIAL_I:mode>): Likewise.
+
+2023-06-21  Ju-Zhe Zhong  <juzhe.zhong@rivai.ai>
+
+	* optabs-query.cc (can_vec_mask_load_store_p): Move to optabs-tree.cc.
+	(get_len_load_store_mode): Ditto.
+	* optabs-query.h (can_vec_mask_load_store_p): Move to optabs-tree.h.
+	(get_len_load_store_mode): Ditto.
+	* optabs-tree.cc (can_vec_mask_load_store_p): New function.
+	(get_len_load_store_mode): Ditto.
+	* optabs-tree.h (can_vec_mask_load_store_p): Ditto.
+	(get_len_load_store_mode): Ditto.
+	* tree-if-conv.cc: include optabs-tree instead of optabs-query
+
+2023-06-21  Richard Biener  <rguenther@suse.de>
+
+	* tree-ssa-loop-ivopts.cc (add_iv_candidate_for_use): Use
+	split_constant_offset for the POINTER_PLUS_EXPR case.
+
+2023-06-21  Richard Biener  <rguenther@suse.de>
+
+	* tree-ssa-loop-ivopts.cc (record_group_use): Use
+	split_constant_offset.
+
+2023-06-21  Richard Biener  <rguenther@suse.de>
+
+	* tree-loop-distribution.cc (classify_builtin_st): Use
+	split_constant_offset.
+	* tree-ssa-loop-ivopts.h (strip_offset): Remove.
+	* tree-ssa-loop-ivopts.cc (strip_offset): Make static.
+
+2023-06-21  Kyrylo Tkachov  <kyrylo.tkachov@arm.com>
+
+	* config/aarch64/aarch64-sve.md (mask_gather_load<mode><v_int_container>):
+	Convert to compact alternatives syntax.
+	(mask_gather_load<mode><v_int_container>): Likewise.
+	(*mask_gather_load<mode><v_int_container>_<su>xtw_unpacked): Likewise.
+	(*mask_gather_load<mode><v_int_container>_sxtw): Likewise.
+	(*mask_gather_load<mode><v_int_container>_uxtw): Likewise.
+	(@aarch64_gather_load_<ANY_EXTEND:optab><SVE_4HSI:mode><SVE_4BHI:mode>):
+	Likewise.
+	(@aarch64_gather_load_<ANY_EXTEND:optab><SVE_2HSDI:mode><SVE_2BHSI:mode>):
+	Likewise.
+	(*aarch64_gather_load_<ANY_EXTEND:optab><SVE_2HSDI:mode>
+	<SVE_2BHSI:mode>_<ANY_EXTEND2:su>xtw_unpacked): Likewise.
+	(*aarch64_gather_load_<ANY_EXTEND:optab><SVE_2HSDI:mode>
+	<SVE_2BHSI:mode>_sxtw): Likewise.
+	(*aarch64_gather_load_<ANY_EXTEND:optab><SVE_2HSDI:mode>
+	<SVE_2BHSI:mode>_uxtw): Likewise.
+	(@aarch64_ldff1_gather<mode>): Likewise.
+	(@aarch64_ldff1_gather<mode>): Likewise.
+	(*aarch64_ldff1_gather<mode>_sxtw): Likewise.
+	(*aarch64_ldff1_gather<mode>_uxtw): Likewise.
+	(@aarch64_ldff1_gather_<ANY_EXTEND:optab><VNx4_WIDE:mode>
+	<VNx4_NARROW:mode>): Likewise.
+	(@aarch64_ldff1_gather_<ANY_EXTEND:optab><VNx2_WIDE:mode>
+	<VNx2_NARROW:mode>): Likewise.
+	(*aarch64_ldff1_gather_<ANY_EXTEND:optab><VNx2_WIDE:mode>
+	<VNx2_NARROW:mode>_sxtw): Likewise.
+	(*aarch64_ldff1_gather_<ANY_EXTEND:optab><VNx2_WIDE:mode>
+	<VNx2_NARROW:mode>_uxtw): Likewise.
+	* config/aarch64/aarch64-sve2.md (@aarch64_gather_ldnt<mode>): Likewise.
+	(@aarch64_gather_ldnt_<ANY_EXTEND:optab><SVE_FULL_SDI:mode>
+	<SVE_PARTIAL_I:mode>): Likewise.
+
+2023-06-21  Tamar Christina  <tamar.christina@arm.com>
+
+	PR other/110329
+	* doc/md.texi: Replace backslashchar.
+
+2023-06-21  Richard Biener  <rguenther@suse.de>
+
+	* config/i386/i386.cc (ix86_vector_costs::finish_cost):
+	Overload.  For masked main loops make sure the vectorization
+	factor isn't more than double the number of iterations.
+
+2023-06-21  Jan Beulich  <jbeulich@suse.com>
+
+	* config/i386/i386-expand.cc (ix86_expand_copysign): Request
+	value duplication by ix86_build_signbit_mask() when AVX512F and
+	not HFmode.
+	* config/i386/sse.md (*<avx512>_vternlog<mode>_all): Convert to
+	2-alternative form. Adjust "mode" attribute. Add "enabled"
+	attribute.
+	(*<avx512>_vpternlog<mode>_1): Also permit when TARGET_AVX512F
+	&& !TARGET_PREFER_AVX256.
+	(*<avx512>_vpternlog<mode>_2): Likewise.
+	(*<avx512>_vpternlog<mode>_3): Likewise.
+
+2023-06-21  liuhongt  <hongtao.liu@intel.com>
+
+	PR target/110018
+	* tree-vect-stmts.cc (vectorizable_conversion): Use
+	intermiediate integer type for float_expr/fix_trunc_expr when
+	direct optab is not existed.
+
 2023-06-20  Tamar Christina  <tamar.christina@arm.com>
 
 	PR bootstrap/110324
diff --git a/gcc/DATESTAMP b/gcc/DATESTAMP
index 047f836..2cf61a4 100644
--- a/gcc/DATESTAMP
+++ b/gcc/DATESTAMP
@@ -1 +1 @@
-20230621
+20230626
diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 669a2a0..c478ec8 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1281,6 +1281,7 @@
 
 # Analyzer object files
 ANALYZER_OBJS = \
+	analyzer/access-diagram.o \
 	analyzer/analysis-plan.o \
 	analyzer/analyzer.o \
 	analyzer/analyzer-language.o \
@@ -1788,7 +1789,16 @@
 	json.o \
 	sbitmap.o \
 	vec.o input.o hash-table.o ggc-none.o memory-block.o \
-	selftest.o selftest-diagnostic.o sort.o
+	selftest.o selftest-diagnostic.o sort.o \
+	text-art/box-drawing.o \
+	text-art/canvas.o \
+	text-art/ruler.o \
+	text-art/selftests.o \
+	text-art/style.o \
+	text-art/styled-string.o \
+	text-art/table.o \
+	text-art/theme.o \
+	text-art/widget.o
 
 # Objects in libcommon-target.a, used by drivers and by the core
 # compiler and containing target-dependent code.
diff --git a/gcc/analyzer/ChangeLog b/gcc/analyzer/ChangeLog
index 761674a..a477da5 100644
--- a/gcc/analyzer/ChangeLog
+++ b/gcc/analyzer/ChangeLog
@@ -1,3 +1,151 @@
+2023-06-24  David Malcolm  <dmalcolm@redhat.com>
+
+	* access-diagram.cc: Add #define INCLUDE_VECTOR.
+	* bounds-checking.cc: Likewise.
+
+2023-06-22  David Malcolm  <dmalcolm@redhat.com>
+
+	PR analyzer/106626
+	* access-diagram.cc: New file.
+	* access-diagram.h: New file.
+	* analyzer.h (class region_offset): Add default ctor.
+	(region_offset::make_byte_offset): New decl.
+	(region_offset::concrete_p): New.
+	(region_offset::get_concrete_byte_offset): New.
+	(region_offset::calc_symbolic_bit_offset): New decl.
+	(region_offset::calc_symbolic_byte_offset): New decl.
+	(region_offset::dump_to_pp): New decl.
+	(region_offset::dump): New decl.
+	(operator<, operator<=, operator>, operator>=): New decls for
+	region_offset.
+	* analyzer.opt
+	(-param=analyzer-text-art-string-ellipsis-threshold=): New.
+	(-param=analyzer-text-art-string-ellipsis-head-len=): New.
+	(-param=analyzer-text-art-string-ellipsis-tail-len=): New.
+	(-param=analyzer-text-art-ideal-canvas-width=): New.
+	(fanalyzer-debug-text-art): New.
+	* bounds-checking.cc: Include "intl.h", "diagnostic-diagram.h",
+	and "analyzer/access-diagram.h".
+	(class out_of_bounds::oob_region_creation_event_capacity): New.
+	(out_of_bounds::out_of_bounds): Add "model" and "sval_hint"
+	params.
+	(out_of_bounds::mark_interesting_stuff): Use the base region.
+	(out_of_bounds::add_region_creation_events): Use
+	oob_region_creation_event_capacity.
+	(out_of_bounds::get_dir): New pure vfunc.
+	(out_of_bounds::maybe_show_notes): New.
+	(out_of_bounds::maybe_show_diagram): New.
+	(out_of_bounds::make_access_diagram): New.
+	(out_of_bounds::m_model): New field.
+	(out_of_bounds::m_sval_hint): New field.
+	(out_of_bounds::m_region_creation_event_id): New field.
+	(concrete_out_of_bounds::concrete_out_of_bounds): Update for new
+	fields.
+	(concrete_past_the_end::concrete_past_the_end): Likewise.
+	(concrete_past_the_end::add_region_creation_events): Use
+	oob_region_creation_event_capacity.
+	(concrete_buffer_overflow::concrete_buffer_overflow): Update for
+	new fields.
+	(concrete_buffer_overflow::emit): Replace call to
+	maybe_describe_array_bounds with maybe_show_notes.
+	(concrete_buffer_overflow::get_dir): New.
+	(concrete_buffer_over_read::concrete_buffer_over_read): Update for
+	new fields.
+	(concrete_buffer_over_read::emit): Replace call to
+	maybe_describe_array_bounds with maybe_show_notes.
+	(concrete_buffer_overflow::get_dir): New.
+	(concrete_buffer_underwrite::concrete_buffer_underwrite): Update
+	for new fields.
+	(concrete_buffer_underwrite::emit): Replace call to
+	maybe_describe_array_bounds with maybe_show_notes.
+	(concrete_buffer_underwrite::get_dir): New.
+	(concrete_buffer_under_read::concrete_buffer_under_read): Update
+	for new fields.
+	(concrete_buffer_under_read::emit): Replace call to
+	maybe_describe_array_bounds with maybe_show_notes.
+	(concrete_buffer_under_read::get_dir): New.
+	(symbolic_past_the_end::symbolic_past_the_end): Update for new
+	fields.
+	(symbolic_buffer_overflow::symbolic_buffer_overflow): Likewise.
+	(symbolic_buffer_overflow::emit): Call maybe_show_notes.
+	(symbolic_buffer_overflow::get_dir): New.
+	(symbolic_buffer_over_read::symbolic_buffer_over_read): Update for
+	new fields.
+	(symbolic_buffer_over_read::emit): Call maybe_show_notes.
+	(symbolic_buffer_over_read::get_dir): New.
+	(region_model::check_symbolic_bounds): Add "sval_hint" param.  Pass
+	it and sized_offset_reg to diagnostics.
+	(region_model::check_region_bounds): Add "sval_hint" param, passing
+	it to diagnostics.
+	* diagnostic-manager.cc
+	(diagnostic_manager::emit_saved_diagnostic): Pass logger to
+	pending_diagnostic::emit.
+	* engine.cc: Add logger param to pending_diagnostic::emit
+	implementations.
+	* infinite-recursion.cc: Likewise.
+	* kf-analyzer.cc: Likewise.
+	* kf.cc: Likewise.  Add nullptr for new param of
+	check_region_for_write.
+	* pending-diagnostic.h: Likewise in decl.
+	* region-model-manager.cc
+	(region_model_manager::get_or_create_int_cst): Convert param from
+	poly_int64 to const poly_wide_int_ref &.
+	(region_model_manager::maybe_fold_binop): Support type being NULL
+	when checking for floating-point types.
+	Check for (X + Y) - X => Y.  Be less strict about types when folding
+	associative ops.  Check for (X + Y) * CST => (X * CST) + (Y * CST).
+	* region-model-manager.h
+	(region_model_manager::get_or_create_int_cst): Convert param from
+	poly_int64 to const poly_wide_int_ref &.
+	* region-model.cc: Add logger param to pending_diagnostic::emit
+	implementations.
+	(region_model::check_external_function_for_access_attr): Update
+	for new param of check_region_for_write.
+	(region_model::deref_rvalue): Use nullptr rather than NULL.
+	(region_model::get_capacity): Handle RK_STRING.
+	(region_model::check_region_access): Add "sval_hint" param; pass it to
+	check_region_bounds.
+	(region_model::check_region_for_write): Add "sval_hint" param;
+	pass it to check_region_access.
+	(region_model::check_region_for_read): Add NULL for new param to
+	check_region_access.
+	(region_model::set_value): Pass rhs_sval to
+	check_region_for_write.
+	(region_model::get_representative_path_var_1): Handle SK_CONSTANT
+	in the check for infinite recursion.
+	* region-model.h (region_model::check_region_for_write): Add
+	"sval_hint" param.
+	(region_model::check_region_access): Likewise.
+	(region_model::check_symbolic_bounds): Likewise.
+	(region_model::check_region_bounds): Likewise.
+	* region.cc (region_offset::make_byte_offset): New.
+	(region_offset::calc_symbolic_bit_offset): New.
+	(region_offset::calc_symbolic_byte_offset): New.
+	(region_offset::dump_to_pp): New.
+	(region_offset::dump): New.
+	(struct linear_op): New.
+	(operator<, operator<=, operator>, operator>=): New, for
+	region_offset.
+	(region::get_next_offset): New.
+	(region::get_relative_symbolic_offset): Use ptrdiff_type_node.
+	(field_region::get_relative_symbolic_offset): Likewise.
+	(element_region::get_relative_symbolic_offset): Likewise.
+	(bit_range_region::get_relative_symbolic_offset): Likewise.
+	* region.h (region::get_next_offset): New decl.
+	* sm-fd.cc: Add logger param to pending_diagnostic::emit
+	implementations.
+	* sm-file.cc: Likewise.
+	* sm-malloc.cc: Likewise.
+	* sm-pattern-test.cc: Likewise.
+	* sm-sensitive.cc: Likewise.
+	* sm-signal.cc: Likewise.
+	* sm-taint.cc: Likewise.
+	* store.cc (bit_range::contains_p): Allow "out" to be null.
+	* store.h (byte_range::get_start_bit_offset): New.
+	(byte_range::get_next_bit_offset): New.
+	* varargs.cc: Add logger param to pending_diagnostic::emit
+	implementations.
+
 2023-06-10  Tim Lange  <mail@tim-lange.me>
 
 	PR analyzer/109577
diff --git a/gcc/analyzer/access-diagram.cc b/gcc/analyzer/access-diagram.cc
new file mode 100644
index 0000000..467c9bd
--- /dev/null
+++ b/gcc/analyzer/access-diagram.cc
@@ -0,0 +1,2406 @@
+/* Text art visualizations within -fanalyzer.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#define INCLUDE_ALGORITHM
+#define INCLUDE_MEMORY
+#define INCLUDE_MAP
+#define INCLUDE_SET
+#define INCLUDE_VECTOR
+#include "system.h"
+#include "coretypes.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "function.h"
+#include "basic-block.h"
+#include "gimple.h"
+#include "diagnostic.h"
+#include "intl.h"
+#include "make-unique.h"
+#include "tree-diagnostic.h" /* for default_tree_printer.  */
+#include "analyzer/analyzer.h"
+#include "analyzer/region-model.h"
+#include "analyzer/access-diagram.h"
+#include "text-art/ruler.h"
+#include "fold-const.h"
+
+#if ENABLE_ANALYZER
+
+/* Consider this code:
+     int32_t arr[10];
+     arr[10] = x;
+   where we've emitted a buffer overflow diagnostic like this:
+     out-of-bounds write from byte 40 till byte 43 but 'arr' ends at byte 40
+
+   We want to emit a diagram that visualizes:
+   - the spatial relationship between the valid region to access, versus
+   the region that was actually accessed: does it overlap, was it touching,
+   close, or far away?  Was it before or after in memory?  What are the
+   relative sizes involved?
+   - the direction of the access (read vs write)
+
+   The following code supports emitting diagrams similar to the following:
+
+   #                                        +--------------------------------+
+   #                                        |write from ‘x’ (type: ‘int32_t’)|
+   #                                        +--------------------------------+
+   #                                                        |
+   #                                                        |
+   #                                                        v
+   #  +---------+-----------+-----------+   +--------------------------------+
+   #  |   [0]   |    ...    |    [9]    |   |       after valid range        |
+   #  +---------+-----------+-----------+   |                                |
+   #  |   ‘arr’ (type: ‘int32_t[10]’)   |   |                                |
+   #  +---------------------------------+   +--------------------------------+
+   #  |~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~|   |~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~|
+   #                   |                                    |
+   #         +---------+--------+                 +---------+---------+
+   #         |capacity: 40 bytes|                 |overflow of 4 bytes|
+   #         +------------------+                 +-------------------+
+
+  where the diagram is laid out via table columns where each table column
+  represents either a range of bits/bytes, or is a spacing column (to highlight
+  the boundary between valid vs invalid accesses).  The table columns can be
+  seen via -fanalyzer-debug-text-art.  For example, here there are 5 table
+  columns ("tc0" through "tc4"):
+
+   #  +---------+-----------+-----------+---+--------------------------------+
+   #  |   tc0   |    tc1    |    tc2    |tc3|              tc4               |
+   #  +---------+-----------+-----------+---+--------------------------------+
+   #  |bytes 0-3|bytes 4-35 |bytes 36-39|   |          bytes 40-43           |
+   #  +---------+-----------+-----------+   +--------------------------------+
+   #
+   #                                        +--------------------------------+
+   #                                        |write from ‘x’ (type: ‘int32_t’)|
+   #                                        +--------------------------------+
+   #                                                        |
+   #                                                        |
+   #                                                        v
+   #  +---------+-----------+-----------+   +--------------------------------+
+   #  |   [0]   |    ...    |    [9]    |   |       after valid range        |
+   #  +---------+-----------+-----------+   |                                |
+   #  |   ‘arr’ (type: ‘int32_t[10]’)   |   |                                |
+   #  +---------------------------------+   +--------------------------------+
+   #  |~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~|   |~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~|
+   #                   |                                    |
+   #         +---------+--------+                 +---------+---------+
+   #         |capacity: 40 bytes|                 |overflow of 4 bytes|
+   #         +------------------+                 +-------------------+
+
+  The diagram is built up from the following:
+
+   #                                        +--------------------------------+
+   #                                        | ITEM FOR SVALUE/ACCESSED REGION|
+   #                                        +--------------------------------+
+   #                                                        |
+   #                                                        | DIRECTION WIDGET
+   #                                                        v
+   #  +---------------------------------+   +--------------------------------+
+   #  |   VALID REGION                  |   | INVALID ACCESS                 |
+   #  +---------------------------------+   +--------------------------------+
+   #
+   #  |                       VALID-VS-INVALID RULER                         |
+
+  i.e. a vbox_widget containing 4 child widgets laid out vertically:
+  - ALIGNED CHILD WIDGET: ITEM FOR SVALUE/ACCESSED REGION
+  - DIRECTION WIDGET
+  - ALIGNED CHILD WIDGET: VALID AND INVALID ACCESSES
+  - VALID-VS-INVALID RULER.
+
+  A more complicated example, given this overflow:
+     char buf[100];
+     strcpy (buf, LOREM_IPSUM);
+
+   01| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
+   02| |[0]|[1]|[2]|[3]|[4]|[5]|   ...    |[440]|[441]|[442]|[443]|[444]|[445]|
+   03| +---+---+---+---+---+---+          +-----+-----+-----+-----+-----+-----+
+   04| |'L'|'o'|'r'|'e'|'m'|' '|          | 'o' | 'r' | 'u' | 'm' | '.' | NUL |
+   05| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
+   06| |                  string literal (type: 'char[446]')                  |
+   07| +----------------------------------------------------------------------+
+   08|   |   |   |   |   |   |  |  |    |    |     |     |     |     |     |
+   09|   |   |   |   |   |   |  |  |    |    |     |     |     |     |     |
+   10|   v   v   v   v   v   v  v  v    v    v     v     v     v     v     v
+   11| +---+---------------------+----++--------------------------------------+
+   12| |[0]|         ...         |[99]||          after valid range           |
+   13| +---+---------------------+----+|                                      |
+   14| |  'buf' (type: 'char[100]')   ||                                      |
+   15| +------------------------------++--------------------------------------+
+   16| |~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~||~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
+   17|                |                                   |
+   18|      +---------+---------+              +----------+----------+
+   19|      |capacity: 100 bytes|              |overflow of 346 bytes|
+   20|      +-------------------+              +---------------------+
+
+ which is:
+
+   01| ALIGNED CHILD WIDGET (lines 01-07): (string_region_spatial_item)-+-----+
+   02| |[0]|[1]|[2]|[3]|[4]|[5]|   ...    |[440]|[441]|[442]|[443]|[444]|[445]|
+   03| +---+---+---+---+---+---+          +-----+-----+-----+-----+-----+-----+
+   04| |'L'|'o'|'r'|'e'|'m'|' '|          | 'o' | 'r' | 'u' | 'm' | '.' | NUL |
+   05| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
+   06| |                  string literal (type: 'char[446]')                  |
+   07| +----------------------------------------------------------------------+
+   08| DIRECTION WIDGET (lines 08-10)   |    |     |     |     |     |     |
+   09|   |   |   |   |   |   |  |  |    |    |     |     |     |     |     |
+   10|   v   v   v   v   v   v  v  v    v    v     v     v     v     v     v
+   11| ALIGNED CHILD WIDGET (lines 11-15)-------------------------------------+
+   12| VALID REGION  ...         |[99]|| INVALID ACCESS                       |
+   13| +---+---------------------+----+|                                      |
+   14| |  'buf' (type: 'char[100]')   ||                                      |
+   15| +------------------------------++--------------------------------------+
+   16| VALID-VS-INVALID RULER (lines 16-20): ~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
+   17|                |                                   |
+   18|      +---------+---------+              +----------+----------+
+   19|      |capacity: 100 bytes|              |overflow of 346 bytes|
+   20|      +-------------------+              +---------------------+
+
+   We build the diagram in several phases:
+   - (1) we construct an access_diagram_impl widget.  Within the ctor, we have
+   these subphases:
+   -   (1.1) find all of the boundaries of interest
+   -   (1.2) use the boundaries to build a bit_table_map, associating bit ranges
+   with table columns (e.g. "byte 0 is column 0, bytes 1-98 are column 2" etc)
+   -   (1.3) create child widgets that share this table-based geometry
+   - (2) ask the widget for its size request
+   -   (2.1) column widths and row heights for the table are computed by
+   access_diagram_impl::calc_req_size
+   -   (2.2) child widgets request sizes based on these widths/heights
+   - (3) create a canvas of the appropriate size
+   - (4) paint the widget hierarchy to the canvas.  */
+
+
+using namespace text_art;
+
+namespace ana {
+
+static styled_string
+fmt_styled_string (style_manager &sm,
+		   const char *fmt, ...)
+    ATTRIBUTE_GCC_DIAG(2, 3);
+
+static styled_string
+fmt_styled_string (style_manager &sm,
+		   const char *fmt, ...)
+{
+  va_list ap;
+  va_start (ap, fmt);
+  styled_string result
+    = styled_string::from_fmt_va (sm, default_tree_printer, fmt, &ap);
+  va_end (ap);
+  return result;
+}
+
+class access_diagram_impl;
+class bit_to_table_map;
+
+static void
+pp_bit_size_t (pretty_printer *pp, bit_size_t num_bits)
+{
+  if (num_bits % BITS_PER_UNIT == 0)
+    {
+      byte_size_t num_bytes = num_bits / BITS_PER_UNIT;
+      if (num_bytes == 1)
+	pp_printf (pp, _("%wi byte"), num_bytes.to_uhwi ());
+      else
+	pp_printf (pp, _("%wi bytes"), num_bytes.to_uhwi ());
+    }
+  else
+    {
+      if (num_bits == 1)
+	pp_printf (pp, _("%wi bit"), num_bits.to_uhwi ());
+      else
+	pp_printf (pp, _("%wi bits"), num_bits.to_uhwi ());
+    }
+}
+
+static styled_string
+get_access_size_str (style_manager &sm,
+		     const access_operation &op,
+		     access_range accessed_range,
+		     tree type)
+{
+  bit_size_expr num_bits;
+  if (accessed_range.get_size (op.m_model, &num_bits))
+    {
+      if (type)
+	{
+	  styled_string s;
+
+	  pretty_printer pp;
+	  num_bits.print (&pp);
+
+	  if (op.m_dir == DIR_READ)
+	    return fmt_styled_string (sm,
+				      _("read of %qT (%s)"),
+				      type,
+				      pp_formatted_text (&pp));
+	  else
+	    return fmt_styled_string (sm,
+				      _("write of %qT (%s)"),
+				      type,
+				      pp_formatted_text (&pp));
+	}
+      if (op.m_dir == DIR_READ)
+	return num_bits.get_formatted_str (sm,
+					   _("read of %wi bit"),
+					   _("read of %wi bits"),
+					   _("read of %wi byte"),
+					   _("read of %wi bytes"),
+					   _("read of %qE bits"),
+					   _("read of %qE bytes"));
+      else
+	return num_bits.get_formatted_str (sm,
+					   _("write of %wi bit"),
+					   _("write of %wi bits"),
+					   _("write of %wi byte"),
+					   _("write of %wi bytes"),
+					   _("write of %qE bits"),
+					   _("write of %qE bytes"));
+    }
+
+  if (type)
+    {
+      if (op.m_dir == DIR_READ)
+	return fmt_styled_string (sm, _("read of %qT"), type);
+      else
+	return fmt_styled_string (sm, _("write of %qT"), type);
+    }
+
+  if (op.m_dir == DIR_READ)
+    return styled_string (sm, _("read"));
+  else
+    return styled_string (sm, _("write"));
+}
+
+/* Subroutine of clean_up_for_diagram.  */
+
+static tree
+strip_any_cast (tree expr)
+{
+  if (TREE_CODE (expr) == NOP_EXPR
+      || TREE_CODE (expr) == NON_LVALUE_EXPR)
+    expr = TREE_OPERAND (expr, 0);
+  return expr;
+}
+
+/* Subroutine of clean_up_for_diagram.  */
+
+static tree
+remove_ssa_names (tree expr)
+{
+  if (TREE_CODE (expr) == SSA_NAME
+      && SSA_NAME_VAR (expr))
+    return SSA_NAME_VAR (expr);
+  tree t = copy_node (expr);
+  for (int i = 0; i < TREE_OPERAND_LENGTH (expr); i++)
+    TREE_OPERAND (t, i) = remove_ssa_names (TREE_OPERAND (expr, i));
+  return t;
+}
+
+/* We want to be able to print tree expressions from the analyzer,
+   which is in the middle end.
+
+   We could use the front-end pretty_printer's formatting routine,
+   but:
+   (a) some have additional state in a pretty_printer subclass, so we'd
+   need to clone global_dc->printer
+   (b) the "aka" type information added by the C and C++ frontends are
+   too verbose when building a diagram, and there isn't a good way to ask
+   for a less verbose version of them.
+
+   Hence we use default_tree_printer.
+   However, we want to avoid printing SSA names, and instead print the
+   underlying var name.
+   Ideally there would be a better tree printer for use by middle end
+   warnings, but as workaround, this function clones a tree, replacing
+   SSA names with the var names.  */
+
+tree
+clean_up_for_diagram (tree expr)
+{
+  tree without_ssa_names = remove_ssa_names (expr);
+  return strip_any_cast (without_ssa_names);
+}
+
+/* struct bit_size_expr.  */
+
+text_art::styled_string
+bit_size_expr::get_formatted_str (text_art::style_manager &sm,
+				  const char *concrete_single_bit_fmt,
+				  const char *concrete_plural_bits_fmt,
+				  const char *concrete_single_byte_fmt,
+				  const char *concrete_plural_bytes_fmt,
+				  const char *symbolic_bits_fmt,
+				  const char *symbolic_bytes_fmt) const
+{
+  if (TREE_CODE (m_num_bits) == INTEGER_CST)
+    {
+      bit_size_t concrete_num_bits = wi::to_offset (m_num_bits);
+      if (concrete_num_bits % BITS_PER_UNIT == 0)
+	{
+	  byte_size_t concrete_num_bytes = concrete_num_bits / BITS_PER_UNIT;
+	  if (concrete_num_bytes == 1)
+	    return fmt_styled_string (sm, concrete_single_byte_fmt,
+				      concrete_num_bytes.to_uhwi ());
+	  else
+	    return fmt_styled_string (sm, concrete_plural_bytes_fmt,
+				      concrete_num_bytes.to_uhwi ());
+	}
+      else
+	{
+	  if (concrete_num_bits == 1)
+	    return fmt_styled_string (sm, concrete_single_bit_fmt,
+				      concrete_num_bits.to_uhwi ());
+	  else
+	    return fmt_styled_string (sm, concrete_plural_bits_fmt,
+				      concrete_num_bits.to_uhwi ());
+	}
+    }
+  else
+    {
+      if (tree bytes_expr = maybe_get_as_bytes ())
+	return fmt_styled_string (sm,
+				  symbolic_bytes_fmt,
+				  clean_up_for_diagram (bytes_expr));
+      return fmt_styled_string (sm,
+				symbolic_bits_fmt,
+				clean_up_for_diagram (m_num_bits));
+    }
+}
+
+void
+bit_size_expr::print (pretty_printer *pp) const
+{
+  if (TREE_CODE (m_num_bits) == INTEGER_CST)
+    {
+      bit_size_t concrete_num_bits = wi::to_offset (m_num_bits);
+      pp_bit_size_t (pp, concrete_num_bits);
+    }
+  else
+    {
+      if (tree bytes_expr = maybe_get_as_bytes ())
+	pp_printf (pp, _("%qE bytes"), bytes_expr);
+      else
+	pp_printf (pp, _("%qE bits"), m_num_bits);
+    }
+}
+
+tree
+bit_size_expr::maybe_get_as_bytes () const
+{
+  switch (TREE_CODE (m_num_bits))
+    {
+    default:
+      break;
+    case INTEGER_CST:
+      {
+	const bit_size_t num_bits = wi::to_offset (m_num_bits);
+	if (num_bits % BITS_PER_UNIT != 0)
+	  return NULL_TREE;
+	const bit_size_t num_bytes = num_bits / BITS_PER_UNIT;
+	return wide_int_to_tree (size_type_node, num_bytes);
+      }
+      break;
+    case PLUS_EXPR:
+    case MINUS_EXPR:
+      {
+	bit_size_expr op0
+	  = bit_size_expr (TREE_OPERAND (m_num_bits, 0));
+	tree op0_as_bytes = op0.maybe_get_as_bytes ();
+	if (!op0_as_bytes)
+	  return NULL_TREE;
+	bit_size_expr op1
+	  = bit_size_expr (TREE_OPERAND (m_num_bits, 1));
+	tree op1_as_bytes = op1.maybe_get_as_bytes ();
+	if (!op1_as_bytes)
+	  return NULL_TREE;
+	return fold_build2 (TREE_CODE (m_num_bits), size_type_node,
+			    op0_as_bytes, op1_as_bytes);
+      }
+      break;
+    case MULT_EXPR:
+      {
+	bit_size_expr op1
+	  = bit_size_expr (TREE_OPERAND (m_num_bits, 1));
+	if (tree op1_as_bytes = op1.maybe_get_as_bytes ())
+	  return fold_build2 (MULT_EXPR, size_type_node,
+			      TREE_OPERAND (m_num_bits, 0),
+			      op1_as_bytes);
+      }
+      break;
+    }
+  return NULL_TREE;
+}
+
+/* struct access_range.  */
+
+access_range::access_range (const region *base_region, const bit_range &bits)
+: m_start (region_offset::make_concrete (base_region,
+					 bits.get_start_bit_offset ())),
+  m_next (region_offset::make_concrete (base_region,
+					bits.get_next_bit_offset ()))
+{
+}
+
+access_range::access_range (const region *base_region, const byte_range &bytes)
+: m_start (region_offset::make_concrete (base_region,
+					 bytes.get_start_bit_offset ())),
+  m_next (region_offset::make_concrete (base_region,
+					bytes.get_next_bit_offset ()))
+{
+}
+
+access_range::access_range (const region &reg, region_model_manager *mgr)
+: m_start (reg.get_offset (mgr)),
+  m_next (reg.get_next_offset (mgr))
+{
+}
+
+bool
+access_range::get_size (const region_model &model, bit_size_expr *out) const
+{
+  tree start_expr = m_start.calc_symbolic_bit_offset (model);
+  if (!start_expr)
+    return false;
+  tree next_expr = m_next.calc_symbolic_bit_offset (model);
+  if (!next_expr)
+    return false;
+  *out = bit_size_expr (fold_build2 (MINUS_EXPR, size_type_node,
+					 next_expr, start_expr));
+  return true;
+}
+
+bool
+access_range::contains_p (const access_range &other) const
+{
+  return (m_start <= other.m_start
+	  && other.m_next <= m_next);
+}
+
+bool
+access_range::empty_p () const
+{
+  bit_range concrete_bits (0, 0);
+  if (!as_concrete_bit_range (&concrete_bits))
+    return false;
+  return concrete_bits.empty_p ();
+}
+
+void
+access_range::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  if (m_start.concrete_p () && m_next.concrete_p ())
+    {
+      bit_range bits (m_start.get_bit_offset (),
+		      m_next.get_bit_offset () - m_start.get_bit_offset ());
+      bits.dump_to_pp (pp);
+      return;
+    }
+  pp_character (pp, '[');
+  m_start.dump_to_pp (pp, simple);
+  pp_string (pp, " to ");
+  m_next.dump_to_pp (pp, simple);
+  pp_character (pp, ')');
+}
+
+DEBUG_FUNCTION void
+access_range::dump (bool simple) const
+{
+  pretty_printer pp;
+  pp_format_decoder (&pp) = default_tree_printer;
+  pp_show_color (&pp) = pp_show_color (global_dc->printer);
+  pp.buffer->stream = stderr;
+  dump_to_pp (&pp, simple);
+  pp_newline (&pp);
+  pp_flush (&pp);
+}
+
+void
+access_range::log (const char *title, logger &logger) const
+{
+  logger.start_log_line ();
+  logger.log_partial ("%s: ", title);
+  dump_to_pp (logger.get_printer (), true);
+  logger.end_log_line ();
+}
+
+/* struct access_operation.  */
+
+access_range
+access_operation::get_valid_bits () const
+{
+  const svalue *capacity_in_bytes_sval = m_model.get_capacity (m_base_region);
+  return access_range
+    (region_offset::make_concrete (m_base_region, 0),
+     region_offset::make_byte_offset (m_base_region, capacity_in_bytes_sval));
+}
+
+access_range
+access_operation::get_actual_bits () const
+{
+  return access_range (m_reg, get_manager ());
+}
+
+/* If there are any bits accessed invalidly before the valid range,
+   return true and write their range to *OUT.
+   Return false if there aren't, or if there's a problem
+   (e.g. symbolic ranges.  */
+
+bool
+access_operation::maybe_get_invalid_before_bits (access_range *out) const
+{
+  access_range valid_bits (get_valid_bits ());
+  access_range actual_bits (get_actual_bits ());
+
+  if (actual_bits.m_start >= valid_bits.m_start)
+    {
+      /* No part of accessed range is before the valid range.  */
+      return false;
+    }
+  else if (actual_bits.m_next > valid_bits.m_start)
+    {
+      /* Get part of accessed range that's before the valid range.  */
+      *out = access_range (actual_bits.m_start, valid_bits.m_start);
+      return true;
+    }
+  else
+    {
+      /* Accessed range is fully before valid range.  */
+      *out = actual_bits;
+      return true;
+    }
+}
+
+/* If there are any bits accessed invalidly after the valid range,
+   return true and write their range to *OUT.
+   Return false if there aren't, or if there's a problem.  */
+
+bool
+access_operation::maybe_get_invalid_after_bits (access_range *out) const
+{
+  access_range valid_bits (get_valid_bits ());
+  access_range actual_bits (get_actual_bits ());
+
+  if (actual_bits.m_next <= valid_bits.m_next)
+    {
+      /* No part of accessed range is after the valid range.  */
+      return false;
+    }
+  else if (actual_bits.m_start < valid_bits.m_next)
+    {
+      /* Get part of accessed range that's after the valid range.  */
+      *out = access_range (valid_bits.m_next, actual_bits.m_next);
+      return true;
+    }
+  else
+    {
+      /* Accessed range is fully after valid range.  */
+      *out = actual_bits;
+      return true;
+    }
+}
+
+/* A class for capturing all of the region offsets of interest (both concrete
+   and symbolic), to help align everything in the diagram.
+   Boundaries can be soft or hard; hard boundaries are emphasized visually
+   (e.g. the boundary between valid vs invalid accesses).
+
+   Offsets in the boundaries are all expressed relative to the base
+   region of the access_operation.  */
+
+class boundaries
+{
+public:
+  enum class kind { HARD, SOFT};
+
+  boundaries (const region &base_reg)
+  : m_base_reg (base_reg)
+  {
+  }
+
+  void add (region_offset offset, enum kind k)
+  {
+    m_all_offsets.insert (offset);
+    if (k == kind::HARD)
+      m_hard_offsets.insert (offset);
+  }
+
+  void add (const access_range &range, enum kind kind)
+  {
+    add (range.m_start, kind);
+    add (range.m_next, kind);
+  }
+
+  void add (const region &reg, region_model_manager *mgr, enum kind kind)
+  {
+    add (access_range (reg.get_offset (mgr),
+		       reg.get_next_offset (mgr)),
+	 kind);
+  }
+
+  void add (const byte_range bytes, enum kind kind)
+  {
+    add (access_range (&m_base_reg, bytes), kind);
+  }
+
+  void add_all_bytes_in_range (const byte_range &bytes)
+  {
+    for (byte_offset_t byte_idx = bytes.get_start_byte_offset ();
+	 byte_idx <= bytes.get_next_byte_offset ();
+	 byte_idx = byte_idx + 1)
+      add (region_offset::make_concrete (&m_base_reg, byte_idx * 8),
+	   kind::SOFT);
+  }
+
+  void add_all_bytes_in_range (const access_range &range)
+  {
+    byte_range bytes (0, 0);
+    bool valid = range.as_concrete_byte_range (&bytes);
+    gcc_assert (valid);
+    add_all_bytes_in_range (bytes);
+  }
+
+  void log (logger &logger) const
+  {
+    logger.log ("boundaries:");
+    logger.inc_indent ();
+    for (auto offset : m_all_offsets)
+      {
+	enum kind k = get_kind (offset);
+	logger.start_log_line ();
+	logger.log_partial ("%s: ", (k == kind::HARD) ? "HARD" : "soft");
+	offset.dump_to_pp (logger.get_printer (), true);
+	logger.end_log_line ();
+      }
+    logger.dec_indent ();
+  }
+
+  enum kind get_kind (region_offset offset) const
+  {
+    gcc_assert (m_all_offsets.find (offset) != m_all_offsets.end ());
+    if (m_hard_offsets.find (offset) != m_hard_offsets.end ())
+      return kind::HARD;
+    else
+      return kind::SOFT;
+  }
+
+  std::set<region_offset>::const_iterator begin () const
+  {
+    return m_all_offsets.begin ();
+  }
+  std::set<region_offset>::const_iterator end () const
+  {
+    return m_all_offsets.end ();
+  }
+  std::set<region_offset>::size_type size () const
+  {
+    return m_all_offsets.size ();
+  }
+
+private:
+  const region &m_base_reg;
+  std::set<region_offset> m_all_offsets;
+  std::set<region_offset> m_hard_offsets;
+};
+
+/* A widget that wraps a table but offloads column-width calculation
+   to a shared object, so that we can vertically line up multiple tables
+   and have them all align their columns.
+
+   For example, in:
+
+   01| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
+   02| |[0]|[1]|[2]|[3]|[4]|[5]|   ...    |[440]|[441]|[442]|[443]|[444]|[445]|
+   03| +---+---+---+---+---+---+          +-----+-----+-----+-----+-----+-----+
+   04| |'L'|'o'|'r'|'e'|'m'|' '|          | 'o' | 'r' | 'u' | 'm' | '.' | NUL |
+   05| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
+   06| |                  string literal (type: 'char[446]')                  |
+   07| +----------------------------------------------------------------------+
+   08|   |   |   |   |   |   |  |  |    |    |     |     |     |     |     |
+   09|   |   |   |   |   |   |  |  |    |    |     |     |     |     |     |
+   10|   v   v   v   v   v   v  v  v    v    v     v     v     v     v     v
+   11|+---+---------------------+----++--------------------------------------+
+   12||[0]|         ...         |[99]||          after valid range           |
+   13|+---+---------------------+----+|                                      |
+   14||  'buf' (type: 'char[100]')   ||                                      |
+   15|+------------------------------++--------------------------------------+
+   16||~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~||~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
+   17|               |                                   |
+   18|     +---------+---------+              +----------+----------+
+   19|     |capacity: 100 bytes|              |overflow of 346 bytes|
+   20|     +-------------------+              +---------------------+
+
+   rows 01-07 and rows 11-15 are x_aligned_table_widget instances.  */
+
+class x_aligned_table_widget : public leaf_widget
+{
+public:
+  x_aligned_table_widget (table t,
+			  const theme &theme,
+			  table_dimension_sizes &col_widths)
+  : m_table (std::move (t)),
+    m_theme (theme),
+    m_col_widths (col_widths),
+    m_row_heights (t.get_size ().h),
+    m_cell_sizes (m_col_widths, m_row_heights),
+    m_tg (m_table, m_cell_sizes)
+  {
+  }
+
+  const char *get_desc () const override
+  {
+    return "x_aligned_table_widget";
+  }
+
+  canvas::size_t calc_req_size () final override
+  {
+    /* We don't compute the size requirements;
+       the parent should have done this.  */
+    return m_tg.get_canvas_size ();
+  }
+
+  void paint_to_canvas (canvas &canvas) final override
+  {
+    m_table.paint_to_canvas (canvas,
+			     get_top_left (),
+			     m_tg,
+			     m_theme);
+  }
+
+  const table &get_table () const { return m_table; }
+  table_cell_sizes &get_cell_sizes () { return m_cell_sizes; }
+  void recalc_coords ()
+  {
+    m_tg.recalc_coords ();
+  }
+
+private:
+  table m_table;
+  const theme &m_theme;
+  table_dimension_sizes &m_col_widths; // Reference to shared column widths
+  table_dimension_sizes m_row_heights; // Unique row heights
+  table_cell_sizes m_cell_sizes;
+  table_geometry m_tg;
+};
+
+/* A widget for printing arrows between the accessed region
+   and the svalue, showing the direction of the access.
+
+   For example, in:
+
+   01| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
+   02| |[0]|[1]|[2]|[3]|[4]|[5]|   ...    |[440]|[441]|[442]|[443]|[444]|[445]|
+   03| +---+---+---+---+---+---+          +-----+-----+-----+-----+-----+-----+
+   04| |'L'|'o'|'r'|'e'|'m'|' '|          | 'o' | 'r' | 'u' | 'm' | '.' | NUL |
+   05| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
+   06| |                  string literal (type: 'char[446]')                  |
+   07| +----------------------------------------------------------------------+
+   08|   |   |   |   |   |   |  |  |    |    |     |     |     |     |     |
+   09|   |   |   |   |   |   |  |  |    |    |     |     |     |     |     |
+   10|   v   v   v   v   v   v  v  v    v    v     v     v     v     v     v
+   11|+---+---------------------+----++--------------------------------------+
+   12||[0]|         ...         |[99]||          after valid range           |
+   13|+---+---------------------+----+|                                      |
+   14||  'buf' (type: 'char[100]')   ||                                      |
+   15|+------------------------------++--------------------------------------+
+   16||~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~||~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
+   17|               |                                   |
+   18|     +---------+---------+              +----------+----------+
+   19|     |capacity: 100 bytes|              |overflow of 346 bytes|
+   20|     +-------------------+              +---------------------+
+
+   rows 8-10 are the direction widget.  */
+
+class direction_widget : public leaf_widget
+{
+public:
+  direction_widget (const access_diagram_impl &dia_impl,
+		    const bit_to_table_map &btm)
+  : leaf_widget (),
+    m_dia_impl (dia_impl),
+    m_btm (btm)
+  {
+  }
+  const char *get_desc () const override
+  {
+    return "direction_widget";
+  }
+  canvas::size_t calc_req_size () final override
+  {
+    /* Get our width from our siblings.  */
+    return canvas::size_t (0, 3);
+  }
+  void paint_to_canvas (canvas &canvas) final override;
+
+private:
+  const access_diagram_impl &m_dia_impl;
+  const bit_to_table_map &m_btm;
+};
+
+/* A widget for adding an x_ruler to a diagram based on table columns,
+   offloading column-width calculation to shared objects, so that the ruler
+   lines up with other tables in the diagram.
+
+   For example, in:
+
+   01| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
+   02| |[0]|[1]|[2]|[3]|[4]|[5]|   ...    |[440]|[441]|[442]|[443]|[444]|[445]|
+   03| +---+---+---+---+---+---+          +-----+-----+-----+-----+-----+-----+
+   04| |'L'|'o'|'r'|'e'|'m'|' '|          | 'o' | 'r' | 'u' | 'm' | '.' | NUL |
+   05| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
+   06| |                  string literal (type: 'char[446]')                  |
+   07| +----------------------------------------------------------------------+
+   08|   |   |   |   |   |   |  |  |    |    |     |     |     |     |     |
+   09|   |   |   |   |   |   |  |  |    |    |     |     |     |     |     |
+   10|   v   v   v   v   v   v  v  v    v    v     v     v     v     v     v
+   11|+---+---------------------+----++--------------------------------------+
+   12||[0]|         ...         |[99]||          after valid range           |
+   13|+---+---------------------+----+|                                      |
+   14||  'buf' (type: 'char[100]')   ||                                      |
+   15|+------------------------------++--------------------------------------+
+   16||~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~||~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
+   17|               |                                   |
+   18|     +---------+---------+              +----------+----------+
+   19|     |capacity: 100 bytes|              |overflow of 346 bytes|
+   20|     +-------------------+              +---------------------+
+
+   rows 16-20 are the x_aligned_x_ruler_widget.  */
+
+class x_aligned_x_ruler_widget : public leaf_widget
+{
+public:
+  x_aligned_x_ruler_widget (const access_diagram_impl &dia_impl,
+			    const theme &theme,
+			    table_dimension_sizes &col_widths)
+  : m_dia_impl (dia_impl),
+    m_theme (theme),
+    m_col_widths (col_widths)
+  {
+  }
+
+  const char *get_desc () const override
+  {
+    return "x_aligned_ruler_widget";
+  }
+
+  void add_range (const table::range_t &x_range,
+		  styled_string text,
+		  style::id_t style_id)
+  {
+    m_labels.push_back (label (x_range, std::move (text), style_id));
+  }
+
+  canvas::size_t calc_req_size () final override
+  {
+    x_ruler r (make_x_ruler ());
+    return r.get_size ();
+  }
+
+  void paint_to_canvas (canvas &canvas) final override
+  {
+    x_ruler r (make_x_ruler ());
+    r.paint_to_canvas (canvas,
+		       get_top_left (),
+		       m_theme);
+  }
+
+private:
+  struct label
+  {
+    label (const table::range_t &table_x_range,
+	   styled_string text,
+	   style::id_t style_id)
+    : m_table_x_range (table_x_range),
+      m_text (std::move (text)),
+      m_style_id (style_id)
+    {
+    }
+    table::range_t m_table_x_range;
+    styled_string m_text;
+    style::id_t m_style_id;
+  };
+
+  x_ruler make_x_ruler () const;
+
+  const access_diagram_impl &m_dia_impl;
+  const theme &m_theme;
+  table_dimension_sizes &m_col_widths;
+  std::vector<label> m_labels;
+};
+
+/* A two-way mapping between access_ranges and table columns, for use by
+   spatial_item subclasses for creating tables.
+   For example when visualizing a bogus access of 'int arr[10];'
+   at 'arr[10]', we might have:
+   - table column 0 is "bytes 0-3" (for arr[0])
+   - table column 1 is "bytes 4-35" (for arr[1] through arr[8])
+   - table column 2 is "bytes 36-39 (for arr[9])
+   - table column 3 is blank to emphasize a hard boundary between
+     valid/invalid accesses.
+   - table column 4 is "bytes 40-44" (for arr[10])
+
+   We store this as a pair of maps from region_offset to table x; in
+   the abvove example:
+
+     region offset          table_x  prev_table_x
+     bit 0 (aka byte 0)     0        (none)
+     bit 32 (aka byte 4)    1        0
+     bit 288 (aka byte 36)  2        1
+     bit 320 (aka byte 40)  4        2
+     bit 352 (aka byte 44)  (none)   (none)
+
+     so that e.g given the half-open byte range [0, 40)
+     we can determine the closed range of table x [0, 2].  */
+
+class bit_to_table_map
+{
+public:
+  /* Populate m_table_x_for_bit and m_bit_for_table_x.  */
+  void populate (const boundaries &boundaries, logger *logger)
+  {
+    LOG_SCOPE (logger);
+
+    int table_x = 0;
+    std::vector <region_offset> vec_boundaries (boundaries.begin (),
+						boundaries.end ());
+
+    /* Sort into an order that makes sense.  */
+    std::sort (vec_boundaries.begin (),
+	       vec_boundaries.end ());
+
+    if (logger)
+      {
+	logger->log ("vec_boundaries");
+	logger->inc_indent ();
+	for (unsigned idx = 0; idx < vec_boundaries.size (); idx++)
+	  {
+	    logger->start_log_line ();
+	    logger->log_partial ("idx: %i: ", idx);
+	    vec_boundaries[idx].dump_to_pp (logger->get_printer (), true);
+	    logger->end_log_line ();
+	  }
+	logger->dec_indent ();
+      }
+
+    for (size_t idx = 0; idx < vec_boundaries.size (); idx++)
+      {
+	const region_offset &offset = vec_boundaries[idx];
+	if (idx > 0 && (idx + 1) < vec_boundaries.size ())
+	  {
+	    if (boundaries.get_kind (offset) == boundaries::kind::HARD)
+	      table_x += 1;
+	  }
+	m_table_x_for_offset[offset] = table_x;
+	if ((idx + 1) < vec_boundaries.size ())
+	  {
+	    const region_offset &next_offset = vec_boundaries[idx + 1];
+	    m_table_x_for_prev_offset[next_offset] = table_x;
+	    m_range_for_table_x[table_x] = access_range (offset, next_offset);
+	  }
+	table_x += 1;
+      }
+    m_num_columns = table_x - 1;
+
+    if (logger)
+      log (*logger);
+  }
+
+  unsigned get_num_columns () const
+  {
+    return m_num_columns;
+  }
+
+  table::range_t get_table_x_for_range (const access_range &range) const
+  {
+    return table::range_t (get_table_x_for_offset (range.m_start),
+			   get_table_x_for_prev_offset (range.m_next) + 1);
+  }
+
+  table::rect_t get_table_rect (const access_range &range,
+				const int table_y, const int table_h) const
+  {
+    const table::range_t x_range (get_table_x_for_range (range));
+    return table::rect_t (table::coord_t (x_range.start, table_y),
+			  table::size_t (x_range.get_size (), table_h));
+  }
+
+  table::rect_t get_table_rect (const region *base_reg,
+				const bit_range &bits,
+				const int table_y, const int table_h) const
+  {
+    const access_range range (base_reg, bits);
+    return get_table_rect (range, table_y, table_h);
+  }
+
+  table::rect_t get_table_rect (const region *base_reg,
+				const byte_range &bytes,
+				const int table_y, const int table_h) const
+  {
+    return get_table_rect (base_reg, bytes.as_bit_range (), table_y, table_h);
+  }
+
+  bool maybe_get_access_range_for_table_x (int table_x,
+					   access_range *out) const
+  {
+    auto slot = m_range_for_table_x.find (table_x);
+    if (slot == m_range_for_table_x.end ())
+      return false;
+    *out = slot->second;
+    return true;
+  }
+
+  void log (logger &logger) const
+  {
+    logger.log ("table columns");
+    logger.inc_indent ();
+    for (unsigned table_x = 0; table_x < get_num_columns (); table_x++)
+      {
+	logger.start_log_line ();
+	logger.log_partial ("table_x: %i", table_x);
+	access_range range_for_column (NULL, bit_range (0, 0));
+	if (maybe_get_access_range_for_table_x (table_x, &range_for_column))
+	  {
+	    logger.log_partial (": range: ");
+	    range_for_column.dump_to_pp (logger.get_printer (), true);
+	  }
+	logger.end_log_line ();
+      }
+    logger.dec_indent ();
+  }
+
+private:
+  int get_table_x_for_offset (region_offset offset) const
+  {
+    auto slot = m_table_x_for_offset.find (offset);
+
+    /* If this fails, then we probably failed to fully populate m_boundaries
+       in find_boundaries.  */
+    gcc_assert (slot != m_table_x_for_offset.end ());
+
+    return slot->second;
+  }
+
+  int get_table_x_for_prev_offset (region_offset offset) const
+  {
+    auto slot = m_table_x_for_prev_offset.find (offset);
+
+    /* If this fails, then we probably failed to fully populate m_boundaries
+       in find_boundaries.  */
+    gcc_assert (slot != m_table_x_for_prev_offset.end ());
+
+    return slot->second;
+  }
+
+  std::map<region_offset, int> m_table_x_for_offset;
+  std::map<region_offset, int> m_table_x_for_prev_offset;
+  std::map<int, access_range> m_range_for_table_x;
+  unsigned m_num_columns;
+};
+
+/* Base class for something in the diagram that participates
+   in two steps of diagram creation:
+   (a) populating a boundaries instance with the boundaries of interest
+   (b) creating a table instance for itself.
+
+   Offsets in the boundaries are all expressed relative to the base
+   region of the access_operation.  */
+
+class spatial_item
+{
+public:
+  virtual void add_boundaries (boundaries &out, logger *) const = 0;
+
+  virtual table make_table (const bit_to_table_map &btm,
+			    style_manager &sm) const = 0;
+};
+
+/* Subclass of spatial_item for visualizing the region of memory
+   that's valid to access relative to the base region of region accessed in
+   the operation.  */
+
+class valid_region_spatial_item : public spatial_item
+{
+public:
+  valid_region_spatial_item (const access_operation &op,
+			     diagnostic_event_id_t region_creation_event_id)
+  : m_op (op),
+    m_region_creation_event_id (region_creation_event_id)
+  {}
+
+  void add_boundaries (boundaries &out, logger *logger) const final override
+  {
+    LOG_SCOPE (logger);
+    access_range valid_bits = m_op.get_valid_bits ();
+    if (logger)
+      {
+	logger->start_log_line ();
+	logger->log_partial ("valid bits: ");
+	valid_bits.dump_to_pp (logger->get_printer (), true);
+	logger->end_log_line ();
+      }
+    out.add (valid_bits, boundaries::kind::HARD);
+
+    /* Support for showing first and final element in array types.  */
+    if (tree base_type = m_op.m_base_region->get_type ())
+      if (TREE_CODE (base_type) == ARRAY_TYPE)
+	{
+	  if (logger)
+	    logger->log ("showing first and final element in array type");
+	  region_model_manager *mgr = m_op.m_model.get_manager ();
+	  tree domain = TYPE_DOMAIN (base_type);
+	  if (TYPE_MIN_VALUE (domain) && TYPE_MAX_VALUE (domain))
+	    {
+	      const svalue *min_idx_sval
+		= mgr->get_or_create_constant_svalue (TYPE_MIN_VALUE (domain));
+	      const svalue *max_idx_sval
+		= mgr->get_or_create_constant_svalue (TYPE_MAX_VALUE (domain));
+	      const region *min_element =
+		mgr->get_element_region (m_op.m_base_region,
+					 TREE_TYPE (base_type),
+					 min_idx_sval);
+	      out.add (*min_element, mgr, boundaries::kind::SOFT);
+	      const region *max_element =
+		mgr->get_element_region (m_op.m_base_region,
+					 TREE_TYPE (base_type),
+					 max_idx_sval);
+	      out.add (*max_element, mgr, boundaries::kind::SOFT);
+	    }
+	}
+  }
+
+  /* Subroutine of make_table when base region has ARRAY_TYPE.  */
+  void add_array_elements_to_table (table &t,
+				    const bit_to_table_map &btm,
+				    style_manager &sm) const
+  {
+    tree base_type = m_op.m_base_region->get_type ();
+    gcc_assert (TREE_CODE (base_type) == ARRAY_TYPE);
+
+    tree domain = TYPE_DOMAIN (base_type);
+    if (!(TYPE_MIN_VALUE (domain) && TYPE_MAX_VALUE (domain)))
+      return;
+
+    region_model_manager * const mgr = m_op.get_manager ();
+    const int table_y = 0;
+    const int table_h = 1;
+    const table::range_t table_y_range (table_y, table_y + table_h);
+
+    t.add_row ();
+    const svalue *min_idx_sval
+      = mgr->get_or_create_constant_svalue (TYPE_MIN_VALUE (domain));
+    const region *min_element = mgr->get_element_region (m_op.m_base_region,
+							 TREE_TYPE (base_type),
+							 min_idx_sval);
+    const access_range min_element_range (*min_element, mgr);
+    const table::range_t min_element_x_range
+      = btm.get_table_x_for_range (min_element_range);
+
+    t.set_cell_span (table::rect_t (min_element_x_range,
+				    table_y_range),
+		     fmt_styled_string (sm, "[%E]",
+					TYPE_MIN_VALUE (domain)));
+
+    const svalue *max_idx_sval
+      = mgr->get_or_create_constant_svalue (TYPE_MAX_VALUE (domain));
+    const region *max_element = mgr->get_element_region (m_op.m_base_region,
+							 TREE_TYPE (base_type),
+							 max_idx_sval);
+    if (min_element == max_element)
+      return; // 1-element array
+
+    const access_range max_element_range (*max_element, mgr);
+    const table::range_t max_element_x_range
+      = btm.get_table_x_for_range (max_element_range);
+    t.set_cell_span (table::rect_t (max_element_x_range,
+				    table_y_range),
+		     fmt_styled_string (sm, "[%E]",
+					TYPE_MAX_VALUE (domain)));
+
+    const table::range_t other_elements_x_range (min_element_x_range.next,
+						 max_element_x_range.start);
+    if (other_elements_x_range.get_size () > 0)
+      t.set_cell_span (table::rect_t (other_elements_x_range, table_y_range),
+		       styled_string (sm, "..."));
+  }
+
+  table make_table (const bit_to_table_map &btm,
+		    style_manager &sm) const final override
+  {
+    table t (table::size_t (btm.get_num_columns (), 1));
+
+    if (tree base_type = m_op.m_base_region->get_type ())
+      if (TREE_CODE (base_type) == ARRAY_TYPE)
+	add_array_elements_to_table (t, btm, sm);
+
+    access_range valid_bits = m_op.get_valid_bits ();
+    const int table_y = t.get_size ().h - 1;
+    const int table_h = 1;
+    table::rect_t rect = btm.get_table_rect (valid_bits, table_y, table_h);
+    styled_string s;
+    switch (m_op.m_base_region->get_kind ())
+      {
+      default:
+	s = styled_string (sm, _("region"));
+	break;
+      case RK_DECL:
+	{
+	  const decl_region *decl_reg
+	    = as_a <const decl_region *> (m_op.m_base_region);
+	  tree decl = decl_reg->get_decl ();
+	  s = fmt_styled_string (sm, "%qE (type: %qT)",
+				 decl,
+				 TREE_TYPE (decl));
+	}
+	break;
+      case RK_HEAP_ALLOCATED:
+	{
+	  if (m_region_creation_event_id.known_p ())
+	    s = fmt_styled_string (sm, _("buffer allocated on heap at %@"),
+				   &m_region_creation_event_id);
+	  else
+	    s = styled_string (sm, _("heap-allocated buffer"));
+	}
+	break;
+      case RK_ALLOCA:
+	{
+	  if (m_region_creation_event_id.known_p ())
+	    s = fmt_styled_string (sm, _("buffer allocated on stack at %@"),
+				   &m_region_creation_event_id);
+	  else
+	    s = styled_string (sm, _("stack-allocated buffer"));
+	}
+	break;
+      case RK_STRING:
+	{
+	  const string_region *string_reg
+	    = as_a <const string_region *> (m_op.m_base_region);
+	  tree string_cst = string_reg->get_string_cst ();
+	  s = fmt_styled_string (sm, _("string literal (type: %qT)"),
+				 TREE_TYPE (string_cst));
+	}
+	break;
+      }
+    t.set_cell_span (rect, std::move (s));
+
+    return t;
+  }
+
+private:
+  const access_operation &m_op;
+  diagnostic_event_id_t m_region_creation_event_id;
+};
+
+/* Subclass of spatial_item for visualizing the region of memory
+   that's actually accessed by the read or write, for reads and
+   for write cases where we don't know the svalue written.  */
+
+class accessed_region_spatial_item : public spatial_item
+{
+public:
+  accessed_region_spatial_item (const access_operation &op) : m_op (op) {}
+
+  void add_boundaries (boundaries &out, logger *logger) const final override
+  {
+    LOG_SCOPE (logger);
+    access_range actual_bits = m_op.get_actual_bits ();
+    if (logger)
+      {
+	logger->start_log_line ();
+	logger->log_partial ("actual bits: ");
+	actual_bits.dump_to_pp (logger->get_printer (), true);
+	logger->end_log_line ();
+      }
+    out.add (actual_bits, boundaries::kind::HARD);
+  }
+
+  table make_table (const bit_to_table_map &btm,
+		    style_manager &sm) const final override
+  {
+    table t (table::size_t (btm.get_num_columns (), 1));
+
+    access_range actual_bits = m_op.get_actual_bits ();
+    const int table_y = 0;
+    const int table_h = 1;
+    table::rect_t rect = btm.get_table_rect (actual_bits, table_y, table_h);
+    t.set_cell_span (rect, styled_string (get_label_string (sm)));
+
+    return t;
+  }
+
+private:
+  styled_string get_label_string (style_manager &sm) const
+  {
+    const access_range accessed_bits (m_op.get_actual_bits ());
+    return get_access_size_str (sm,
+				m_op,
+				accessed_bits,
+				m_op.m_reg.get_type ());
+  }
+
+  const access_operation &m_op;
+};
+
+/* Subclass of spatial_item for when we know the svalue being written
+   to the accessed region.
+   Can be subclassed to give visualizations of specific kinds of svalue.  */
+
+class svalue_spatial_item : public spatial_item
+{
+public:
+  static std::unique_ptr<svalue_spatial_item> make (const access_operation &op,
+						    const svalue &sval,
+						    access_range actual_bits,
+						    const theme &theme);
+
+  svalue_spatial_item (const access_operation &op,
+		       const svalue &sval,
+		       access_range actual_bits)
+  : m_op (op), m_sval (sval), m_actual_bits (actual_bits)
+  {}
+
+  void add_boundaries (boundaries &out, logger *logger) const override
+  {
+    LOG_SCOPE (logger);
+    out.add (m_actual_bits, boundaries::kind::HARD);
+  }
+
+  table make_table (const bit_to_table_map &btm,
+		    style_manager &sm) const override
+  {
+    table t (table::size_t (btm.get_num_columns (), 0));
+
+    const int table_y = t.add_row ();
+    const int table_h = 1;
+    table::rect_t rect = btm.get_table_rect (m_actual_bits, table_y, table_h);
+    t.set_cell_span (rect, styled_string (get_label_string (sm)));
+    return t;
+  }
+
+protected:
+  styled_string get_label_string (style_manager &sm) const
+  {
+    tree rep_tree = m_op.m_model.get_representative_tree (&m_sval);
+    if (rep_tree)
+      {
+	if (TREE_CODE (rep_tree) == SSA_NAME)
+	  rep_tree = SSA_NAME_VAR (rep_tree);
+	switch (TREE_CODE (rep_tree))
+	  {
+	  default:
+	    break;
+	  case INTEGER_CST:
+	    return fmt_styled_string (sm, _("write of %<(%T) %E%>"),
+				      TREE_TYPE (rep_tree),
+				      rep_tree);
+
+	  case PARM_DECL:
+	  case VAR_DECL:
+	    return fmt_styled_string (sm, _("write from %qE (type: %qT)"),
+				      rep_tree,
+				      TREE_TYPE (rep_tree));
+	    break;
+	  }
+	}
+
+    const access_range accessed_bits (m_op.get_actual_bits ());
+    return get_access_size_str (sm,
+				m_op,
+				accessed_bits,
+				m_sval.get_type ());
+  }
+
+  const access_operation &m_op;
+  const svalue &m_sval;
+  access_range m_actual_bits;
+};
+
+/* Subclass of svalue_spatial_item for initial_svalue of a string_region
+   i.e. for string literals.
+
+   There are three cases:
+   (a) for long strings, show just the head and tail of the string,
+   with an ellipsis:
+     +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
+     |[0]|[1]|[2]|[3]|[4]|[5]|          |[440]|[441]|[442]|[443]|[444]|[445]|
+     +---+---+---+---+---+---+   ...    +-----+-----+-----+-----+-----+-----+
+     |‘L’|‘o’|‘r’|‘e’|‘m’|‘ ’|          | ‘o’ | ‘r’ | ‘u’ | ‘m’ | ‘.’ | NUL |
+     +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
+     |                  string literal (type: ‘char[446]’)                  |
+     +----------------------------------------------------------------------+
+   (b) For sufficiently short strings, show the full string:
+     +----------+---------+---------+---------+---------+ +-----------------+
+     |   [0]    |   [1]   |   [2]   |   [3]   |   [4]   | |       [5]       |
+     +----------+---------+---------+---------+---------+ +-----------------+
+     |   ‘h’    |   ‘e’   |   ‘l’   |   ‘l’   |   ‘o’   | |       NUL       |
+     +----------+---------+---------+---------+---------+-+-----------------+
+     |                   string literal (type: ‘char[6]’)                   |
+     +----------------------------------------------------------------------+
+   (c) for non-ASCII strings that are short enough to show the full string,
+   show how unicode code points of the bytes decoded as UTF-8:
+     +-----+-----+-----+----+----++----+----+----+----+----+----+----+------+
+     | [0] | [1] | [2] |[3] |[4] ||[5] |[6] |[7] |[8] |[9] |[10]|[11]| [12] |
+     +-----+-----+-----+----+----++----+----+----+----+----+----+----+------+
+     |0xe6 |0x96 |0x87 |0xe5|0xad||0x97|0xe5|0x8c|0x96|0xe3|0x81|0x91| 0x00 |
+     +-----+-----+-----+----+----++----+----+----+----+----+----+----+------+
+     |     U+6587      |    U+5b57     |    U+5316    |    U+3051    |U+0000|
+     +-----------------+---------------+--------------+--------------+------+
+     |                  string literal (type: ‘char[13]’)                   |
+     +----------------------------------------------------------------------+
+   and show the characters themselves if unicode is supported and they are not
+   control characters:
+     ┌─────┬─────┬─────┬────┬────┐┌────┬────┬────┬────┬────┬────┬────┬──────┐
+     │ [0] │ [1] │ [2] │[3] │[4] ││[5] │[6] │[7] │[8] │[9] │[10]│[11]│ [12] │
+     ├─────┼─────┼─────┼────┼────┤├────┼────┼────┼────┼────┼────┼────┼──────┤
+     │0xe6 │0x96 │0x87 │0xe5│0xad││0x97│0xe5│0x8c│0x96│0xe3│0x81│0x91│ 0x00 │
+     ├─────┴─────┴─────┼────┴────┴┴────┼────┴────┴────┼────┴────┴────┼──────┤
+     │     U+6587      │    U+5b57     │    U+5316    │    U+3051    │U+0000│
+     ├─────────────────┼───────────────┼──────────────┼──────────────┼──────┤
+     │       文        │      字       │      化      │      け      │ NUL  │
+     ├─────────────────┴───────────────┴──────────────┴──────────────┴──────┤
+     │                  string literal (type: ‘char[13]’)                   │
+     └──────────────────────────────────────────────────────────────────────┘
+*/
+
+class string_region_spatial_item : public svalue_spatial_item
+{
+public:
+  string_region_spatial_item (const access_operation &op,
+			      const svalue &sval,
+			      access_range actual_bits,
+			      const string_region &string_reg,
+			      const theme &theme)
+  : svalue_spatial_item (op, sval, actual_bits),
+    m_string_reg (string_reg),
+    m_theme (theme),
+    m_ellipsis_threshold (param_analyzer_text_art_string_ellipsis_threshold),
+    m_ellipsis_head_len (param_analyzer_text_art_string_ellipsis_head_len),
+    m_ellipsis_tail_len (param_analyzer_text_art_string_ellipsis_tail_len),
+    m_show_full_string (calc_show_full_string ()),
+    m_show_utf8 (m_show_full_string && !pure_ascii_p ())
+  {
+  }
+
+  void add_boundaries (boundaries &out, logger *logger) const override
+  {
+    LOG_SCOPE (logger);
+    out.add (m_actual_bits, boundaries::kind::HARD);
+
+    tree string_cst = get_string_cst ();
+    /* TREE_STRING_LENGTH is sizeof, not strlen.  */
+    if (m_show_full_string)
+      out.add_all_bytes_in_range (m_actual_bits);
+    else
+      {
+	byte_range head_of_string (0, m_ellipsis_head_len);
+	out.add_all_bytes_in_range (head_of_string);
+	byte_range tail_of_string
+	  (TREE_STRING_LENGTH (string_cst) - m_ellipsis_tail_len,
+	   m_ellipsis_tail_len);
+	out.add_all_bytes_in_range (tail_of_string);
+	/* Adding the above pair of ranges will also effectively add
+	   the boundaries of the range of ellipsized chars, as they're
+	   exactly in between head_of_string and tail_of_string.  */
+      }
+  }
+
+  table make_table (const bit_to_table_map &btm,
+		    style_manager &sm) const override
+  {
+    table t (table::size_t (btm.get_num_columns (), 0));
+
+    const int byte_idx_table_y = t.add_row ();
+    const int byte_val_table_y = t.add_row ();
+
+    byte_range bytes (0, 0);
+    bool valid = m_actual_bits.as_concrete_byte_range (&bytes);
+    gcc_assert (valid);
+    tree string_cst = get_string_cst ();
+    if (m_show_full_string)
+      {
+       for (byte_offset_t byte_idx = bytes.get_start_byte_offset ();
+	    byte_idx < bytes.get_next_byte_offset ();
+	    byte_idx = byte_idx + 1)
+	 add_column_for_byte (t, btm, sm, byte_idx,
+			      byte_idx_table_y, byte_val_table_y);
+
+       if (m_show_utf8)
+	 {
+	   const bool show_unichars = m_theme.unicode_p ();
+	   const int utf8_code_point_table_y = t.add_row ();
+	   int utf8_character_table_y;
+	   if (show_unichars)
+	     utf8_character_table_y = t.add_row ();
+
+	   /* We don't actually want the display widths here, but
+	      it's an easy way to decode UTF-8.  */
+	   cpp_char_column_policy policy (8, cpp_wcwidth);
+	   cpp_display_width_computation dw (TREE_STRING_POINTER (string_cst),
+					     TREE_STRING_LENGTH (string_cst),
+					     policy);
+	   while (!dw.done ())
+	     {
+	       cpp_decoded_char decoded_char;
+	       dw.process_next_codepoint (&decoded_char);
+
+	       if (!decoded_char.m_valid_ch)
+		 continue;
+	       size_t start_byte_idx
+		 = decoded_char.m_start_byte - TREE_STRING_POINTER (string_cst);
+	       byte_size_t size_in_bytes
+		 = decoded_char.m_next_byte - decoded_char.m_start_byte;
+	       byte_range bytes (start_byte_idx, size_in_bytes);
+
+	       const table::rect_t code_point_table_rect
+		 = btm.get_table_rect (&m_string_reg, bytes,
+				       utf8_code_point_table_y, 1);
+	       char buf[100];
+	       sprintf (buf, "U+%04x", decoded_char.m_ch);
+	       t.set_cell_span (code_point_table_rect,
+				styled_string (sm, buf));
+
+	       if (show_unichars)
+		 {
+		   const table::rect_t character_table_rect
+		     = btm.get_table_rect (&m_string_reg, bytes,
+					   utf8_character_table_y, 1);
+		   if (cpp_is_printable_char (decoded_char.m_ch))
+		     t.set_cell_span (character_table_rect,
+				      styled_string (decoded_char.m_ch));
+		   else if (decoded_char.m_ch == 0)
+		     t.set_cell_span (character_table_rect,
+				      styled_string (sm, "NUL"));
+		   else
+		     t.set_cell_span (character_table_rect,
+				      styled_string (sm, ""));
+		 }
+	     }
+	 }
+      }
+    else
+      {
+	/* Head of string.  */
+	for (int byte_idx = 0; byte_idx < m_ellipsis_head_len; byte_idx++)
+	  add_column_for_byte (t, btm, sm, byte_idx,
+			       byte_idx_table_y, byte_val_table_y);
+
+	/* Ellipsis (two rows high).  */
+	const byte_range ellipsis_bytes
+	  (m_ellipsis_head_len,
+	   TREE_STRING_LENGTH (string_cst)
+	   - (m_ellipsis_head_len + m_ellipsis_tail_len));
+	const table::rect_t table_rect
+	  = btm.get_table_rect (&m_string_reg, ellipsis_bytes,
+				byte_idx_table_y, 2);
+	t.set_cell_span(table_rect, styled_string (sm, "..."));
+
+	/* Tail of string.  */
+	for (int byte_idx
+	       = (TREE_STRING_LENGTH (string_cst) - m_ellipsis_tail_len);
+	     byte_idx < TREE_STRING_LENGTH (string_cst);
+	     byte_idx++)
+	  add_column_for_byte (t, btm, sm, byte_idx,
+			       byte_idx_table_y, byte_val_table_y);
+      }
+
+    const int summary_table_y = t.add_row ();
+    t.set_cell_span (btm.get_table_rect (&m_string_reg, bytes,
+					 summary_table_y, 1),
+		     fmt_styled_string (sm,
+					_("string literal (type: %qT)"),
+					TREE_TYPE (string_cst)));
+
+    return t;
+  }
+
+  tree get_string_cst () const { return m_string_reg.get_string_cst (); }
+
+private:
+  bool calc_show_full_string () const
+  {
+    tree string_cst = get_string_cst ();
+    if (TREE_STRING_LENGTH (string_cst) < m_ellipsis_threshold)
+      return true;
+    if (TREE_STRING_LENGTH (string_cst) <
+	(m_ellipsis_head_len + m_ellipsis_tail_len))
+      return true;
+    return false;
+  }
+
+  bool pure_ascii_p () const
+  {
+    tree string_cst = get_string_cst ();
+    for (unsigned byte_idx = 0;
+	 byte_idx < (unsigned) TREE_STRING_LENGTH (string_cst);
+	 byte_idx++)
+      {
+	unsigned char ch = TREE_STRING_POINTER (string_cst)[byte_idx];
+	if (ch >= 0x80)
+	  return false;
+      }
+    return true;
+  }
+
+  void add_column_for_byte (table &t, const bit_to_table_map &btm,
+			    style_manager &sm,
+			    const byte_offset_t byte_idx,
+			    const int byte_idx_table_y,
+			    const int byte_val_table_y) const
+  {
+    tree string_cst = get_string_cst ();
+    gcc_assert (byte_idx >= 0);
+    gcc_assert (byte_idx < TREE_STRING_LENGTH (string_cst));
+
+    const byte_range bytes (byte_idx, 1);
+    if (1) // show_byte_indices
+      {
+	const table::rect_t idx_table_rect
+	  = btm.get_table_rect (&m_string_reg, bytes, byte_idx_table_y, 1);
+	t.set_cell_span (idx_table_rect,
+			 fmt_styled_string (sm, "[%li]",
+					    byte_idx.ulow ()));
+      }
+
+    char byte_val = TREE_STRING_POINTER (string_cst)[byte_idx.ulow ()];
+    const table::rect_t val_table_rect
+      = btm.get_table_rect (&m_string_reg, bytes, byte_val_table_y, 1);
+    table_cell_content content (make_cell_content_for_byte (sm, byte_val));
+    t.set_cell_span (val_table_rect, std::move (content));
+  }
+
+  table_cell_content make_cell_content_for_byte (style_manager &sm,
+						 unsigned char byte_val) const
+  {
+    if (!m_show_utf8)
+       {
+	if (byte_val == '\0')
+	  return styled_string (sm, "NUL");
+	else if (byte_val < 0x80)
+	  if (ISPRINT (byte_val))
+	    return fmt_styled_string (sm, "%qc", byte_val);
+       }
+    char buf[100];
+    sprintf (buf, "0x%02x", byte_val);
+    return styled_string (sm, buf);
+  }
+
+  const string_region &m_string_reg;
+  const theme &m_theme;
+  const int m_ellipsis_threshold;
+  const int m_ellipsis_head_len;
+  const int m_ellipsis_tail_len;
+  const bool m_show_full_string;
+  const bool m_show_utf8;
+};
+
+std::unique_ptr<svalue_spatial_item>
+svalue_spatial_item::make (const access_operation &op,
+			   const svalue &sval,
+			   access_range actual_bits,
+			   const theme &theme)
+{
+  if (const initial_svalue *initial_sval = sval.dyn_cast_initial_svalue ())
+    if (const string_region *string_reg
+	= initial_sval->get_region ()->dyn_cast_string_region ())
+      return make_unique <string_region_spatial_item> (op, sval, actual_bits,
+						       *string_reg, theme);
+  return make_unique <svalue_spatial_item> (op, sval, actual_bits);
+}
+
+/* Widget subclass implementing access diagrams.  */
+
+class access_diagram_impl : public vbox_widget
+{
+public:
+  access_diagram_impl (const access_operation &op,
+		       diagnostic_event_id_t region_creation_event_id,
+		       style_manager &sm,
+		       const theme &theme,
+		       logger *logger)
+  : m_op (op),
+    m_region_creation_event_id (region_creation_event_id),
+    m_sm (sm),
+    m_theme (theme),
+    m_logger (logger),
+    m_invalid (false),
+    m_valid_region_spatial_item (op, region_creation_event_id),
+    m_accessed_region_spatial_item (op),
+    m_btm (),
+    m_calc_req_size_called (false)
+  {
+    LOG_SCOPE (logger);
+
+    if (logger)
+      {
+	access_range invalid_before_bits;
+	if (op.maybe_get_invalid_before_bits (&invalid_before_bits))
+	  invalid_before_bits.log ("invalid before range", *logger);
+	access_range invalid_after_bits;
+	if (op.maybe_get_invalid_after_bits (&invalid_after_bits))
+	  invalid_after_bits.log ("invalid after range", *logger);
+
+	if (op.m_sval_hint)
+	  {
+	    logger->start_log_line ();
+	    logger->log_partial ("sval_hint: ");
+	    op.m_sval_hint->dump_to_pp (logger->get_printer (), true);
+	    logger->end_log_line ();
+	  }
+      }
+
+    /* Register painting styles.  */
+    {
+      style valid_style;
+      valid_style.m_fg_color = style::named_color::GREEN;
+      valid_style.m_bold = true;
+      m_valid_style_id = m_sm.get_or_create_id (valid_style);
+
+      style invalid_style;
+      invalid_style.m_fg_color = style::named_color::RED;
+      invalid_style.m_bold = true;
+      m_invalid_style_id = m_sm.get_or_create_id (invalid_style);
+    }
+
+    if (op.m_sval_hint)
+      {
+	access_range actual_bits = m_op.get_actual_bits ();
+	m_svalue_spatial_item = svalue_spatial_item::make (m_op,
+							   *op.m_sval_hint,
+							   actual_bits,
+							   m_theme);
+      }
+
+    /* Two passes:
+       First, figure out all of the boundaries of interest.
+       Then use that to build child widgets showing the regions of interest,
+       with a common tabular layout.  */
+
+    m_boundaries = find_boundaries ();
+    if (logger)
+      m_boundaries->log (*logger);
+
+    /* Populate m_table_x_for_bit and m_bit_for_table_x.
+       Each table column represents the range [offset, next_offset).
+       We don't create a column in the table for the final offset, but we
+       do populate it, so that looking at the table_x of one beyond the
+       final table column gives us the upper bound offset.  */
+    m_btm.populate (*m_boundaries, logger);
+
+    /* Gracefully reject cases where the boundary sorting has gone wrong
+       (due to awkward combinations of symbolic values).  */
+    {
+      table::range_t actual_bits_x_range
+	= m_btm.get_table_x_for_range (m_op.get_actual_bits ());
+      if (actual_bits_x_range.get_size () <= 0)
+	{
+	  if (logger)
+	    logger->log ("giving up: bad table columns for actual_bits");
+	  m_invalid = true;
+	  return;
+	}
+      table::range_t valid_bits_x_range
+	= m_btm.get_table_x_for_range (m_op.get_valid_bits ());
+      if (valid_bits_x_range.get_size () <= 0)
+	{
+	  if (logger)
+	    logger->log ("giving up: bad table columns for valid_bits");
+	  m_invalid = true;
+	  return;
+	}
+    }
+
+    m_col_widths
+      = make_unique <table_dimension_sizes> (m_btm.get_num_columns ());
+
+    /* Now create child widgets.  */
+
+    if (flag_analyzer_debug_text_art)
+      {
+	table t_headings (make_headings_table ());
+	add_aligned_child_table (std::move (t_headings));
+      }
+
+    if (m_svalue_spatial_item)
+      {
+	table t_sval (m_svalue_spatial_item->make_table (m_btm, m_sm));
+	add_aligned_child_table (std::move (t_sval));
+      }
+    else
+      {
+	table t_accessed
+	  (m_accessed_region_spatial_item.make_table (m_btm, m_sm));
+	add_aligned_child_table (std::move (t_accessed));
+      }
+
+    add_direction_widget ();
+
+    table t_valid (m_valid_region_spatial_item.make_table (m_btm, m_sm));
+    add_invalid_accesses_to_region_table (t_valid);
+    add_aligned_child_table (std::move (t_valid));
+
+    add_valid_vs_invalid_ruler ();
+  }
+
+  const char *get_desc () const override
+  {
+    return "access_diagram_impl";
+  }
+
+  canvas::size_t calc_req_size () final override
+  {
+    if (m_invalid)
+      return canvas::size_t (0, 0);
+
+    /* Now compute the size requirements for the tables.  */
+    for (auto iter : m_aligned_table_widgets)
+      iter->get_cell_sizes ().pass_1 (iter->get_table ());
+    for (auto iter : m_aligned_table_widgets)
+      iter->get_cell_sizes ().pass_2 (iter->get_table ());
+
+    adjust_to_scale();
+
+    /* ...and relayout the tables.  */
+    for (auto iter : m_aligned_table_widgets)
+      iter->recalc_coords ();
+
+    /* Populate the canvas_x per table_x.  */
+    m_col_start_x.clear ();
+    int iter_canvas_x = 0;
+    for (auto w : m_col_widths->m_requirements)
+      {
+	m_col_start_x.push_back (iter_canvas_x);
+	iter_canvas_x += w + 1;
+      }
+    m_col_start_x.push_back (iter_canvas_x);
+
+    m_calc_req_size_called = true;
+
+    return vbox_widget::calc_req_size ();
+  }
+
+  int get_canvas_x_for_table_x (int table_x) const
+  {
+    gcc_assert (m_calc_req_size_called);
+    return m_col_start_x[table_x];
+  }
+
+  canvas::range_t get_canvas_x_range (const table::range_t &table_x_range) const
+  {
+    gcc_assert (m_calc_req_size_called);
+    return canvas::range_t (get_canvas_x_for_table_x (table_x_range.start),
+			    get_canvas_x_for_table_x (table_x_range.next));
+  }
+
+  const access_operation &get_op () const { return m_op; }
+
+  style::id_t get_style_id_for_validity (bool is_valid) const
+  {
+    return is_valid ? m_valid_style_id : m_invalid_style_id;
+  }
+
+  const theme &get_theme () const { return m_theme; }
+
+private:
+  /* Figure out all of the boundaries of interest when visualizing ths op.  */
+  std::unique_ptr<boundaries>
+  find_boundaries () const
+  {
+    std::unique_ptr<boundaries> result
+      = make_unique<boundaries> (*m_op.m_base_region);
+
+    m_valid_region_spatial_item.add_boundaries (*result, m_logger);
+    m_accessed_region_spatial_item.add_boundaries (*result, m_logger);
+    if (m_svalue_spatial_item)
+      m_svalue_spatial_item->add_boundaries (*result, m_logger);
+
+    return result;
+  }
+
+  void add_aligned_child_table (table t)
+  {
+    x_aligned_table_widget *w
+      = new x_aligned_table_widget (std::move (t), m_theme, *m_col_widths);
+    m_aligned_table_widgets.push_back (w);
+    add_child (std::unique_ptr<widget> (w));
+  }
+
+  /* Create a table showing headings for use by -fanalyzer-debug-text-art, for
+     example:
+     +---------+-----------+-----------+---+--------------------------------+
+     |   tc0   |    tc1    |    tc2    |tc3|              tc4               |
+     +---------+-----------+-----------+---+--------------------------------+
+     |bytes 0-3|bytes 4-35 |bytes 36-39|   |          bytes 40-43           |
+     +---------+-----------+-----------+   +--------------------------------+
+     which has:
+     - a row showing the table column numbers, labelled "tc0", "tc1", etc
+     - a row showing the memory range of each table column that has one.  */
+
+  table make_headings_table () const
+  {
+    table t (table::size_t (m_btm.get_num_columns (), 2));
+
+    for (int table_x = 0; table_x < t.get_size ().w; table_x++)
+      {
+	const int table_y = 0;
+	t.set_cell (table::coord_t (table_x, table_y),
+		    fmt_styled_string (m_sm, "tc%i", table_x));
+      }
+    for (int table_x = 0; table_x < t.get_size ().w; table_x++)
+      {
+	const int table_y = 1;
+	access_range range_for_column (NULL, bit_range (0, 0));
+	if (m_btm.maybe_get_access_range_for_table_x (table_x,
+						      &range_for_column))
+	  {
+	    pretty_printer pp;
+	    pp_format_decoder (&pp) = default_tree_printer;
+	    range_for_column.dump_to_pp (&pp, true);
+	    t.set_cell (table::coord_t (table_x, table_y),
+			styled_string (m_sm, pp_formatted_text (&pp)));
+	  }
+      }
+
+    return t;
+  }
+
+  void add_direction_widget ()
+  {
+    add_child (::make_unique<direction_widget> (*this, m_btm));
+  }
+
+  void add_invalid_accesses_to_region_table (table &t_region)
+  {
+    gcc_assert (t_region.get_size ().w == (int)m_btm.get_num_columns ());
+
+    const int table_y = 0;
+    const int table_h = t_region.get_size ().h;
+
+    access_range invalid_before_bits;
+    if (m_op.maybe_get_invalid_before_bits (&invalid_before_bits))
+      {
+	t_region.set_cell_span (m_btm.get_table_rect (invalid_before_bits,
+						      table_y, table_h),
+				styled_string (m_sm,
+					       _("before valid range")));
+      }
+    access_range invalid_after_bits;
+    if (m_op.maybe_get_invalid_after_bits (&invalid_after_bits))
+      {
+	t_region.set_cell_span (m_btm.get_table_rect (invalid_after_bits,
+						      table_y, table_h),
+				styled_string (m_sm,
+					       _("after valid range")));
+      }
+  }
+
+  void maybe_add_gap (x_aligned_x_ruler_widget *w,
+		      const access_range &lower,
+		      const access_range &upper) const
+  {
+    LOG_SCOPE (m_logger);
+    if (m_logger)
+      {
+	lower.log ("lower", *m_logger);
+	upper.log ("upper", *m_logger);
+      }
+    tree lower_next = lower.m_next.calc_symbolic_bit_offset (m_op.m_model);
+    if (!lower_next)
+      {
+	if (m_logger)
+	  m_logger->log ("failed to get lower_next");
+	return;
+      }
+    tree upper_start = upper.m_start.calc_symbolic_bit_offset (m_op.m_model);
+    if (!upper_start)
+      {
+	if (m_logger)
+	  m_logger->log ("failed to get upper_start");
+	return;
+      }
+    tree num_bits_gap = fold_build2 (MINUS_EXPR,
+				     size_type_node,
+				     upper_start, lower_next);
+    if (m_logger)
+      m_logger->log ("num_bits_gap: %qE", num_bits_gap);
+    tree zero = build_int_cst (size_type_node, 0);
+    tristate ts_gt_zero = m_op.m_model.eval_condition (num_bits_gap,
+						       GT_EXPR,
+						       zero,
+						       NULL);
+    if (ts_gt_zero.is_false ())
+      {
+	if (m_logger)
+	  m_logger->log ("rejecting as not > 0");
+	return;
+      }
+
+    bit_size_expr num_bits (num_bits_gap);
+    styled_string label = num_bits.get_formatted_str (m_sm,
+						      _("%wi bit"),
+						      _("%wi bits"),
+						      _("%wi byte"),
+						      _("%wi bytes"),
+						      _("%qE bits"),
+						      _("%qE bytes"));
+    w->add_range (m_btm.get_table_x_for_range (access_range (lower.m_next,
+							     upper.m_start)),
+		  std::move (label),
+		  style::id_plain);
+  }
+
+  styled_string
+  make_warning_string (styled_string &&text)
+  {
+    styled_string result;
+    if (!m_theme.emojis_p ())
+      return std::move (text);
+
+    result.append (styled_string (0x26A0, /* U+26A0 WARNING SIGN.  */
+				  true));
+    /* U+26A0 WARNING SIGN has East_Asian_Width == Neutral, but in its
+       emoji variant is printed (by vte at least) with a 2nd half
+       overlapping the next char.  Hence we add two spaces here: a space
+       to be covered by this overlap, plus another space of padding.  */
+    result.append (styled_string (m_sm, "  "));
+    result.append (std::move (text));
+    return result;
+  }
+
+  /* Add a ruler child widet showing valid, invalid, and gaps.  */
+  void add_valid_vs_invalid_ruler ()
+  {
+    LOG_SCOPE (m_logger);
+
+    x_aligned_x_ruler_widget *w
+      = new x_aligned_x_ruler_widget (*this, m_theme, *m_col_widths);
+
+    access_range invalid_before_bits;
+    if (m_op.maybe_get_invalid_before_bits (&invalid_before_bits))
+      {
+	if (m_logger)
+	  invalid_before_bits.log ("invalid_before_bits", *m_logger);
+	bit_size_expr num_before_bits;
+	if (invalid_before_bits.get_size (m_op.m_model, &num_before_bits))
+	  {
+	    styled_string label;
+	    if (m_op.m_dir == DIR_READ)
+	      label = num_before_bits.get_formatted_str
+		(m_sm,
+		 _("under-read of %wi bit"),
+		 _("under-read of %wi bits"),
+		 _("under-read of %wi byte"),
+		 _("under-read of %wi bytes"),
+		 _("under-read of %qE bits"),
+		 _("under-read of %qE bytes"));
+	    else
+	      label = num_before_bits.get_formatted_str
+		(m_sm,
+		 _("underwrite of %wi bit"),
+		 _("underwrite of %wi bits"),
+		 _("underwrite of %wi byte"),
+		 _("underwrite of %wi bytes"),
+		 _("underwrite of %qE bits"),
+		 _("underwrite of %qE bytes"));
+	    w->add_range (m_btm.get_table_x_for_range (invalid_before_bits),
+			  make_warning_string (std::move (label)),
+			  m_invalid_style_id);
+	  }
+      }
+    else
+      {
+	if (m_logger)
+	  m_logger->log ("no invalid_before_bits");
+      }
+
+    /* It would be nice to be able to use std::optional<access_range> here,
+       but std::optional is C++17.  */
+    bool got_valid_bits = false;
+    access_range valid_bits (m_op.get_valid_bits ());
+    bit_size_expr num_valid_bits;
+    if (valid_bits.get_size (m_op.m_model, &num_valid_bits))
+      {
+	if (m_logger)
+	  valid_bits.log ("valid_bits", *m_logger);
+
+	got_valid_bits = true;
+	maybe_add_gap (w, invalid_before_bits, valid_bits);
+
+	styled_string label;
+	if (m_op.m_dir == DIR_READ)
+	  label = num_valid_bits.get_formatted_str (m_sm,
+						    _("size: %wi bit"),
+						    _("size: %wi bits"),
+						    _("size: %wi byte"),
+						    _("size: %wi bytes"),
+						    _("size: %qE bits"),
+						    _("size: %qE bytes"));
+	else
+	  label = num_valid_bits.get_formatted_str (m_sm,
+						    _("capacity: %wi bit"),
+						    _("capacity: %wi bits"),
+						    _("capacity: %wi byte"),
+						    _("capacity: %wi bytes"),
+						    _("capacity: %qE bits"),
+						    _("capacity: %qE bytes"));
+	w->add_range (m_btm.get_table_x_for_range (m_op.get_valid_bits ()),
+		      std::move (label),
+		      m_valid_style_id);
+      }
+
+    access_range invalid_after_bits;
+    if (m_op.maybe_get_invalid_after_bits (&invalid_after_bits))
+      {
+	if (got_valid_bits)
+	  maybe_add_gap (w, valid_bits, invalid_after_bits);
+
+	if (m_logger)
+	  invalid_before_bits.log ("invalid_after_bits", *m_logger);
+
+	bit_size_expr num_after_bits;
+	if (invalid_after_bits.get_size (m_op.m_model, &num_after_bits))
+	  {
+	    styled_string label;
+	    if (m_op.m_dir == DIR_READ)
+	      label = num_after_bits.get_formatted_str
+		(m_sm,
+		 _("over-read of %wi bit"),
+		 _("over-read of %wi bits"),
+		 _("over-read of %wi byte"),
+		 _("over-read of %wi bytes"),
+		 _("over-read of %qE bits"),
+		 _("over-read of %qE bytes"));
+	    else
+	      label = num_after_bits.get_formatted_str
+		(m_sm,
+		 _("overflow of %wi bit"),
+		 _("overflow of %wi bits"),
+		 _("overflow of %wi byte"),
+		 _("overflow of %wi bytes"),
+		 _("over-read of %qE bits"),
+		 _("overflow of %qE bytes"));
+	    w->add_range (m_btm.get_table_x_for_range (invalid_after_bits),
+			  make_warning_string (std::move (label)),
+			  m_invalid_style_id);
+	  }
+      }
+    else
+      {
+	if (m_logger)
+	  m_logger->log ("no invalid_after_bits");
+      }
+
+    add_child (std::unique_ptr<widget> (w));
+  }
+
+  /* Subroutine of calc_req_size.
+     Try to allocate surplus canvas width to table columns to make the
+     per table-column canvas widths closer to being to scale.
+     See e.g.:
+       https://en.wikipedia.org/wiki/Fair_item_allocation
+       https://en.wikipedia.org/wiki/Mathematics_of_apportionment
+  */
+  void adjust_to_scale ()
+  {
+    LOG_SCOPE (m_logger);
+    const unsigned num_columns = m_btm.get_num_columns ();
+    std::vector<bit_offset_t> bit_sizes (num_columns);
+    for (unsigned table_x = 0; table_x < num_columns; table_x++)
+      {
+	access_range range_for_column (NULL, bit_range (0, 0));
+	if (m_btm.maybe_get_access_range_for_table_x (table_x,
+						      &range_for_column))
+	  {
+	    bit_size_t size_in_bits;
+	    if (!range_for_column.get_size_in_bits (&size_in_bits))
+	      size_in_bits = BITS_PER_UNIT; // arbitrary non-zero value
+	    gcc_assert (size_in_bits > 0);
+	    bit_sizes[table_x] = size_in_bits;
+	  }
+	else
+	  bit_sizes[table_x] = 0;
+      }
+
+    while (adjust_to_scale_once (bit_sizes))
+      {
+      }
+  }
+  bool adjust_to_scale_once (const std::vector<bit_offset_t> &bit_sizes)
+  {
+    LOG_SCOPE (m_logger);
+
+    const unsigned num_columns = m_btm.get_num_columns ();
+
+    /* Find the total canvas width currently required.
+       Require one extra canvas column for the right-hand border
+       of the table.  */
+    int total_width = 1;
+    for (unsigned table_x = 0; table_x < num_columns; table_x++)
+      {
+	int canvas_w = m_col_widths->m_requirements[table_x];
+	gcc_assert (canvas_w >= 0);
+	total_width += canvas_w + 1;
+      }
+
+    const int max_width = param_analyzer_text_art_ideal_canvas_width;
+    if (total_width >= max_width)
+      {
+	if (m_logger)
+	  m_logger->log ("bailing out: total_width=%i ,>= max_width (%i)\n",
+			 total_width, max_width);
+	return false;
+      }
+
+    const int fixed_point = 1024;
+    std::vector<bit_offset_t> canvas_w_per_bit (num_columns);
+    for (unsigned table_x = 0; table_x < num_columns; table_x++)
+      {
+	bit_offset_t bit_size = bit_sizes[table_x];
+	if (bit_size > 0)
+	  canvas_w_per_bit[table_x]
+	    = (m_col_widths->m_requirements[table_x] * fixed_point) / bit_size;
+	else
+	  canvas_w_per_bit[table_x] = INT_MAX;
+      }
+
+    /* Find the min canvas per bit, and give an extra canvas column to
+       the table column that has least.  */
+    size_t min_idx = std::distance (canvas_w_per_bit.begin (),
+				    std::min_element (canvas_w_per_bit.begin (),
+						      canvas_w_per_bit.end ()));
+    m_col_widths->m_requirements[min_idx] += 1;
+    if (m_logger)
+      m_logger->log ("adding 1 canvas_w to column %i\n", (int)min_idx);
+
+    return true; // keep going
+  }
+
+  const access_operation &m_op;
+  diagnostic_event_id_t m_region_creation_event_id;
+  style_manager &m_sm;
+  const theme &m_theme;
+  logger *m_logger;
+  /* In lieu of being able to throw exceptions, a flag to mark this object
+     as "invalid".  */
+  bool m_invalid;
+
+  style::id_t m_valid_style_id;
+  style::id_t m_invalid_style_id;
+
+  valid_region_spatial_item m_valid_region_spatial_item;
+  accessed_region_spatial_item m_accessed_region_spatial_item;
+  std::unique_ptr<svalue_spatial_item> m_svalue_spatial_item;
+
+  std::unique_ptr<boundaries> m_boundaries;
+
+  bit_to_table_map m_btm;
+
+  bool m_calc_req_size_called;
+
+  /* Column widths shared by all x_aligned_table_widget,
+     created once we know how many columns we need.  */
+  std::unique_ptr<table_dimension_sizes> m_col_widths;
+
+  /* All of the child x_aligned_table_widget that share
+     column widths.  */
+  std::vector<x_aligned_table_widget *> m_aligned_table_widgets;
+
+/* Mapping from table_x to canvas_x.  */
+  std::vector<int> m_col_start_x;
+};
+
+x_ruler
+x_aligned_x_ruler_widget::make_x_ruler () const
+{
+  x_ruler r (x_ruler::label_dir::BELOW);
+  for (auto& iter : m_labels)
+    {
+      canvas::range_t canvas_x_range
+	= m_dia_impl.get_canvas_x_range (iter.m_table_x_range);
+      /* Include the end-point.  */
+      canvas_x_range.next++;
+      r.add_label (canvas_x_range, iter.m_text.copy (), iter.m_style_id,
+		   x_ruler::label_kind::TEXT_WITH_BORDER);
+    }
+  return r;
+}
+
+/* class direction_widget : public leaf_widget.  */
+
+/* Paint arrows indicating the direction of the access (read vs write),
+   but only in the X-extent corresponding to the region that's actually
+   accessed.  */
+
+void
+direction_widget::paint_to_canvas (canvas &canvas)
+{
+  const access_range accessed_bits (m_dia_impl.get_op ().get_actual_bits ());
+
+  const access_range valid_bits (m_dia_impl.get_op ().get_valid_bits ());
+
+  for (unsigned table_x = 0; table_x < m_btm.get_num_columns (); table_x++)
+    {
+      access_range column_access_range;
+      if (m_btm.maybe_get_access_range_for_table_x (table_x,
+						    &column_access_range))
+	{
+	  /* Only paint arrows in the accessed region.  */
+	  if (!accessed_bits.contains_p (column_access_range))
+	    continue;
+
+	  /* Are we within the valid region?  */
+	  const bool is_valid (valid_bits.contains_p (column_access_range));
+	  const style::id_t style_id
+	    = m_dia_impl.get_style_id_for_validity (is_valid);
+	  const canvas::range_t x_canvas_range
+	    = m_dia_impl.get_canvas_x_range (table::range_t (table_x,
+							     table_x + 1));
+	  const int canvas_x = x_canvas_range.get_midpoint ();
+	  m_dia_impl.get_theme ().paint_y_arrow
+	    (canvas,
+	     canvas_x,
+	     canvas::range_t (get_y_range ()),
+	     (m_dia_impl.get_op ().m_dir == DIR_READ
+	      ? theme::y_arrow_dir::UP
+	      : theme::y_arrow_dir::DOWN),
+	     style_id);
+	}
+    }
+}
+
+/* class access_diagram : public text_art::wrapper_widget.  */
+
+/* To hide the implementation details, this is merely a wrapper around
+   an access_diagram_impl.  */
+
+access_diagram::access_diagram (const access_operation &op,
+				diagnostic_event_id_t region_creation_event_id,
+				style_manager &sm,
+				const theme &theme,
+				logger *logger)
+: wrapper_widget (make_unique <access_diagram_impl> (op,
+						     region_creation_event_id,
+						     sm,
+						     theme,
+						     logger))
+{
+}
+
+} // namespace ana
+
+#endif /* #if ENABLE_ANALYZER */
diff --git a/gcc/analyzer/access-diagram.h b/gcc/analyzer/access-diagram.h
new file mode 100644
index 0000000..c124e80
--- /dev/null
+++ b/gcc/analyzer/access-diagram.h
@@ -0,0 +1,165 @@
+/* Text art visualizations within -fanalyzer.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_ANALYZER_ACCESS_DIAGRAM_H
+#define GCC_ANALYZER_ACCESS_DIAGRAM_H
+
+#include "text-art/canvas.h"
+#include "text-art/theme.h"
+#include "text-art/widget.h"
+#include "analyzer/analyzer.h"
+#include "analyzer/store.h"
+
+namespace ana {
+
+class bit_size_expr
+{
+public:
+  bit_size_expr () : m_num_bits (NULL) {}
+  bit_size_expr (tree num_bits) : m_num_bits (num_bits) {}
+
+  text_art::styled_string
+  get_formatted_str (text_art::style_manager &sm,
+		     const char *concrete_single_bit_fmt,
+		     const char *concrete_plural_bits_fmt,
+		     const char *concrete_single_byte_fmt,
+		     const char *concrete_plural_bytes_fmt,
+		     const char *symbolic_bits_fmt,
+		     const char *symbolic_bytes_fmt) const;
+  void print (pretty_printer *pp) const;
+
+  tree maybe_get_as_bytes () const;
+
+private:
+  tree m_num_bits;
+};
+
+/* A range of bits within a base region, where each endpoint
+   could be concrete or symbolic (not necessarily the same).  */
+
+struct access_range
+{
+  access_range ()
+  : m_start (), m_next ()
+  {
+  }
+  access_range (region_offset start, region_offset next)
+  : m_start (start), m_next (next)
+  {}
+  access_range (const region *base_region, const bit_range &bits);
+  access_range (const region *base_region, const byte_range &bytes);
+  access_range (const region &reg, region_model_manager *);
+
+  bool concrete_p () const
+  {
+    return m_start.concrete_p () && m_next.concrete_p ();
+  }
+
+  bool empty_p () const;
+
+  bool get_size (const region_model &model, bit_size_expr *out) const;
+
+  bool get_size_in_bits (bit_size_t *out) const
+  {
+    if (concrete_p ())
+      {
+	*out = m_next.get_bit_offset () - m_start.get_bit_offset ();
+	return true;
+      }
+    return false;
+  }
+
+  bool as_concrete_bit_range (bit_range *out) const
+  {
+    if (!concrete_p ())
+      return false;
+    bit_size_t size = m_next.get_bit_offset () - m_start.get_bit_offset ();
+    *out = bit_range (m_start.get_bit_offset (), size);
+    return true;
+  }
+  bool as_concrete_byte_range (byte_range *out) const
+  {
+    bit_range bits (0, 0);
+    if (!as_concrete_bit_range (&bits))
+      return false;
+    return bits.as_byte_range (out);
+  }
+
+  bool contains_p (const access_range &other) const;
+
+  void dump_to_pp (pretty_printer *pp, bool) const;
+  void dump (bool) const;
+  void log (const char *title, logger &) const;
+
+  region_offset m_start;
+  region_offset m_next;
+};
+
+struct access_operation
+{
+  access_operation (const region_model &model,
+		    enum access_direction dir,
+		    const region &reg,
+		    const svalue *sval_hint)
+  : m_model (model),
+    m_dir (dir),
+    m_reg (reg),
+    m_sval_hint (sval_hint),
+    m_base_region (reg.get_base_region ())
+  {}
+
+  region_model_manager *get_manager () const
+  {
+    return m_model.get_manager ();
+  }
+
+  /* Get the valid bits to access within the base region.  */
+  access_range get_valid_bits () const;
+
+  /* Get the actual bits accessed within the base region.  */
+  access_range get_actual_bits () const;
+
+  bool maybe_get_invalid_before_bits (access_range *out) const;
+  bool maybe_get_invalid_after_bits (access_range *out) const;
+
+  const region_model &m_model;
+  enum access_direction m_dir;
+  const region &m_reg;
+  const svalue *m_sval_hint;
+  const region *m_base_region;
+};
+
+class access_diagram : public text_art::wrapper_widget
+{
+public:
+  access_diagram (const access_operation &op,
+		  diagnostic_event_id_t region_creation_event_id,
+		  text_art::style_manager &sm,
+		  const text_art::theme &theme,
+		  logger *logger);
+  const char *get_desc () const override
+  {
+    return "access_diagram";
+  }
+};
+
+} // namespace ana
+
+#endif /* GCC_ANALYZER_ACCESS_DIAGRAM_H */
diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h
index a161952..579517c 100644
--- a/gcc/analyzer/analyzer.h
+++ b/gcc/analyzer/analyzer.h
@@ -183,6 +183,11 @@
 class region_offset
 {
 public:
+  region_offset ()
+  : m_base_region (NULL), m_offset (0), m_sym_offset (NULL)
+  {
+  }
+
   static region_offset make_concrete (const region *base_region,
 				      bit_offset_t offset)
   {
@@ -193,9 +198,12 @@
   {
     return region_offset (base_region, 0, sym_offset);
   }
+  static region_offset make_byte_offset (const region *base_region,
+					 const svalue *num_bytes_sval);
 
   const region *get_base_region () const { return m_base_region; }
 
+  bool concrete_p () const { return m_sym_offset == NULL; }
   bool symbolic_p () const { return m_sym_offset != NULL; }
 
   bit_offset_t get_bit_offset () const
@@ -204,12 +212,26 @@
     return m_offset;
   }
 
+  bool get_concrete_byte_offset (byte_offset_t *out) const
+  {
+    gcc_assert (!symbolic_p ());
+    if (m_offset % BITS_PER_UNIT == 0)
+      {
+	*out = m_offset / BITS_PER_UNIT;
+	return true;
+      }
+    return false;
+  }
+
   const svalue *get_symbolic_byte_offset () const
   {
     gcc_assert (symbolic_p ());
     return m_sym_offset;
   }
 
+  tree calc_symbolic_bit_offset (const region_model &model) const;
+  const svalue *calc_symbolic_byte_offset (region_model_manager *mgr) const;
+
   bool operator== (const region_offset &other) const
   {
     return (m_base_region == other.m_base_region
@@ -217,6 +239,9 @@
 	    && m_sym_offset == other.m_sym_offset);
   }
 
+  void dump_to_pp (pretty_printer *pp, bool) const;
+  void dump (bool) const;
+
 private:
   region_offset (const region *base_region, bit_offset_t offset,
 		 const svalue *sym_offset)
@@ -228,6 +253,11 @@
   const svalue *m_sym_offset;
 };
 
+extern bool operator< (const region_offset &, const region_offset &);
+extern bool operator<= (const region_offset &, const region_offset &);
+extern bool operator> (const region_offset &, const region_offset &);
+extern bool operator>= (const region_offset &, const region_offset &);
+
 extern location_t get_stmt_location (const gimple *stmt, function *fun);
 
 extern bool compat_types_p (tree src_type, tree dst_type);
diff --git a/gcc/analyzer/analyzer.opt b/gcc/analyzer/analyzer.opt
index 9d1a937..2760aaa 100644
--- a/gcc/analyzer/analyzer.opt
+++ b/gcc/analyzer/analyzer.opt
@@ -54,6 +54,22 @@
 Common Joined UInteger Var(param_analyzer_max_enodes_for_full_dump) Init(200) Param
 The maximum depth of exploded nodes that should appear in a dot dump before switching to a less verbose format.
 
+-param=analyzer-text-art-string-ellipsis-threshold=
+Common Joined UInteger Var(param_analyzer_text_art_string_ellipsis_threshold) Init(15) Param
+The number of bytes at which to ellipsize string literals in analyzer text art diagrams.
+
+-param=analyzer-text-art-string-ellipsis-head-len=
+Common Joined UInteger Var(param_analyzer_text_art_string_ellipsis_head_len) Init(6) Param
+The number of literal bytes to show at the head of a string literal in text art when ellipsizing it.
+
+-param=analyzer-text-art-string-ellipsis-tail-len=
+Common Joined UInteger Var(param_analyzer_text_art_string_ellipsis_tail_len) Init(6) Param
+The number of literal bytes to show at the tail of a string literal in text art when ellipsizing it.
+
+-param=analyzer-text-art-ideal-canvas-width=
+Common Joined UInteger Var(param_analyzer_text_art_ideal_canvas_width) Init(72) Param
+The ideal width in characters of text art diagrams generated by the analyzer.
+
 Wanalyzer-allocation-size
 Common Var(warn_analyzer_allocation_size) Init(1) Warning
 Warn about code paths in which a pointer to a buffer is assigned to an incompatible type.
@@ -242,6 +258,10 @@
 Common Joined RejectNegative Var(flag_analyzer_checker)
 Restrict the analyzer to run just the named checker.
 
+fanalyzer-debug-text-art
+Common Var(flag_analyzer_debug_text_art) Init(0)
+Add extra annotations to diagrams.
+
 fanalyzer-fine-grained
 Common Var(flag_analyzer_fine_grained) Init(0)
 Avoid combining multiple statements into one exploded edge.
diff --git a/gcc/analyzer/bounds-checking.cc b/gcc/analyzer/bounds-checking.cc
index a5692cf..5e8de9a 100644
--- a/gcc/analyzer/bounds-checking.cc
+++ b/gcc/analyzer/bounds-checking.cc
@@ -19,21 +19,25 @@
 
 #include "config.h"
 #define INCLUDE_MEMORY
+#define INCLUDE_VECTOR
 #include "system.h"
 #include "coretypes.h"
 #include "make-unique.h"
 #include "tree.h"
 #include "function.h"
 #include "basic-block.h"
+#include "intl.h"
 #include "gimple.h"
 #include "gimple-iterator.h"
 #include "diagnostic-core.h"
 #include "diagnostic-metadata.h"
+#include "diagnostic-diagram.h"
 #include "analyzer/analyzer.h"
 #include "analyzer/analyzer-logging.h"
 #include "analyzer/region-model.h"
 #include "analyzer/checker-event.h"
 #include "analyzer/checker-path.h"
+#include "analyzer/access-diagram.h"
 
 #if ENABLE_ANALYZER
 
@@ -44,8 +48,35 @@
 class out_of_bounds : public pending_diagnostic
 {
 public:
-  out_of_bounds (const region *reg, tree diag_arg)
-  : m_reg (reg), m_diag_arg (diag_arg)
+  class oob_region_creation_event_capacity : public region_creation_event_capacity
+  {
+  public:
+    oob_region_creation_event_capacity (tree capacity,
+					const event_loc_info &loc_info,
+					out_of_bounds &oob)
+      : region_creation_event_capacity (capacity,
+					loc_info),
+	m_oob (oob)
+    {
+    }
+    void prepare_for_emission (checker_path *path,
+			       pending_diagnostic *pd,
+			       diagnostic_event_id_t emission_id) override
+    {
+      region_creation_event_capacity::prepare_for_emission (path,
+							    pd,
+							    emission_id);
+      m_oob.m_region_creation_event_id = emission_id;
+    }
+    private:
+    out_of_bounds &m_oob;
+  };
+
+  out_of_bounds (const region_model &model,
+		 const region *reg,
+		 tree diag_arg,
+		 const svalue *sval_hint)
+  : m_model (model), m_reg (reg), m_diag_arg (diag_arg), m_sval_hint (sval_hint)
   {}
 
   bool subclass_equal_p (const pending_diagnostic &base_other) const override
@@ -63,7 +94,7 @@
 
   void mark_interesting_stuff (interesting_t *interest) final override
   {
-    interest->add_region_creation (m_reg);
+    interest->add_region_creation (m_reg->get_base_region ());
   }
 
   void add_region_creation_events (const region *,
@@ -75,15 +106,25 @@
        so we don't need an event for that.  */
     if (capacity)
       emission_path.add_event
-	(make_unique<region_creation_event_capacity> (capacity, loc_info));
+	(make_unique<oob_region_creation_event_capacity> (capacity, loc_info,
+							  *this));
   }
 
+  virtual enum access_direction get_dir () const = 0;
+
 protected:
   enum memory_space get_memory_space () const
   {
     return m_reg->get_memory_space ();
   }
 
+  void
+  maybe_show_notes (location_t loc, logger *logger) const
+  {
+    maybe_describe_array_bounds (loc);
+    maybe_show_diagram (logger);
+  }
+
   /* Potentially add a note about valid ways to index this array, such
      as (given "int arr[10];"):
        note: valid subscripts for 'arr' are '[0]' to '[9]'
@@ -112,8 +153,49 @@
 	    m_diag_arg, min_idx, max_idx);
   }
 
+  void
+  maybe_show_diagram (logger *logger) const
+  {
+    access_operation op (m_model, get_dir (), *m_reg, m_sval_hint);
+
+    /* Don't attempt to make a diagram if there's no valid way of
+       accessing the base region (e.g. a 0-element array).  */
+    if (op.get_valid_bits ().empty_p ())
+      return;
+
+    if (const text_art::theme *theme = global_dc->m_diagrams.m_theme)
+      {
+	text_art::style_manager sm;
+	text_art::canvas canvas (make_access_diagram (op, sm, *theme, logger));
+	if (canvas.get_size ().w == 0 && canvas.get_size ().h == 0)
+	  {
+	    /* In lieu of exceptions, return a zero-sized diagram if there's
+	       a problem.  Give up if that's happened.  */
+	    return;
+	  }
+	diagnostic_diagram diagram
+	  (canvas,
+	   /* Alt text.  */
+	   _("Diagram visualizing the predicted out-of-bounds access"));
+	diagnostic_emit_diagram (global_dc, diagram);
+      }
+  }
+
+  text_art::canvas
+  make_access_diagram (const access_operation &op,
+		       text_art::style_manager &sm,
+		       const text_art::theme &theme,
+		       logger *logger) const
+  {
+    access_diagram d (op, m_region_creation_event_id, sm, theme, logger);
+    return d.to_canvas (sm);
+  }
+
+  region_model m_model;
   const region *m_reg;
   tree m_diag_arg;
+  const svalue *m_sval_hint;
+  diagnostic_event_id_t m_region_creation_event_id;
 };
 
 /* Abstract base class for all out-of-bounds warnings where the
@@ -122,9 +204,11 @@
 class concrete_out_of_bounds : public out_of_bounds
 {
 public:
-  concrete_out_of_bounds (const region *reg, tree diag_arg,
-			  byte_range out_of_bounds_range)
-  : out_of_bounds (reg, diag_arg),
+  concrete_out_of_bounds (const region_model &model,
+			  const region *reg, tree diag_arg,
+			  byte_range out_of_bounds_range,
+			  const svalue *sval_hint)
+  : out_of_bounds (model, reg, diag_arg, sval_hint),
     m_out_of_bounds_range (out_of_bounds_range)
   {}
 
@@ -146,9 +230,12 @@
 class concrete_past_the_end : public concrete_out_of_bounds
 {
 public:
-  concrete_past_the_end (const region *reg, tree diag_arg, byte_range range,
-			 tree byte_bound)
-  : concrete_out_of_bounds (reg, diag_arg, range), m_byte_bound (byte_bound)
+  concrete_past_the_end (const region_model &model,
+			 const region *reg, tree diag_arg, byte_range range,
+			 tree byte_bound,
+			 const svalue *sval_hint)
+  : concrete_out_of_bounds (model, reg, diag_arg, range, sval_hint),
+    m_byte_bound (byte_bound)
   {}
 
   bool
@@ -168,7 +255,9 @@
   {
     if (m_byte_bound && TREE_CODE (m_byte_bound) == INTEGER_CST)
       emission_path.add_event
-	(make_unique<region_creation_event_capacity> (m_byte_bound, loc_info));
+	(make_unique<oob_region_creation_event_capacity> (m_byte_bound,
+							  loc_info,
+							  *this));
   }
 
 protected:
@@ -180,9 +269,11 @@
 class concrete_buffer_overflow : public concrete_past_the_end
 {
 public:
-  concrete_buffer_overflow (const region *reg, tree diag_arg,
-		   byte_range range, tree byte_bound)
-  : concrete_past_the_end (reg, diag_arg, range, byte_bound)
+  concrete_buffer_overflow (const region_model &model,
+			    const region *reg, tree diag_arg,
+			    byte_range range, tree byte_bound,
+			    const svalue *sval_hint)
+  : concrete_past_the_end (model, reg, diag_arg, range, byte_bound, sval_hint)
   {}
 
   const char *get_kind () const final override
@@ -190,7 +281,8 @@
     return "concrete_buffer_overflow";
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc,
+	     logger *logger) final override
   {
     diagnostic_metadata m;
     bool warned;
@@ -238,7 +330,7 @@
 		  "write to beyond the end of %qE",
 		  m_diag_arg);
 
-	maybe_describe_array_bounds (rich_loc->get_loc ());
+	maybe_show_notes (rich_loc->get_loc (), logger);
       }
 
     return warned;
@@ -276,6 +368,8 @@
 				   start_buf, end_buf, m_byte_bound);
       }
   }
+
+  enum access_direction get_dir () const final override { return DIR_WRITE; }
 };
 
 /* Concrete subclass to complain about buffer over-reads.  */
@@ -283,9 +377,10 @@
 class concrete_buffer_over_read : public concrete_past_the_end
 {
 public:
-  concrete_buffer_over_read (const region *reg, tree diag_arg,
+  concrete_buffer_over_read (const region_model &model,
+			     const region *reg, tree diag_arg,
 			     byte_range range, tree byte_bound)
-  : concrete_past_the_end (reg, diag_arg, range, byte_bound)
+  : concrete_past_the_end (model, reg, diag_arg, range, byte_bound, NULL)
   {}
 
   const char *get_kind () const final override
@@ -293,7 +388,7 @@
     return "concrete_buffer_over_read";
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *logger) final override
   {
     diagnostic_metadata m;
     bool warned;
@@ -339,7 +434,7 @@
 		  "read from after the end of %qE",
 		  m_diag_arg);
 
-	maybe_describe_array_bounds (rich_loc->get_loc ());
+	maybe_show_notes (rich_loc->get_loc (), logger);
       }
 
     return warned;
@@ -377,6 +472,8 @@
 				   start_buf, end_buf, m_byte_bound);
       }
   }
+
+  enum access_direction get_dir () const final override { return DIR_READ; }
 };
 
 /* Concrete subclass to complain about buffer underwrites.  */
@@ -384,9 +481,11 @@
 class concrete_buffer_underwrite : public concrete_out_of_bounds
 {
 public:
-  concrete_buffer_underwrite (const region *reg, tree diag_arg,
-			      byte_range range)
-  : concrete_out_of_bounds (reg, diag_arg, range)
+  concrete_buffer_underwrite (const region_model &model,
+			      const region *reg, tree diag_arg,
+			      byte_range range,
+			      const svalue *sval_hint)
+  : concrete_out_of_bounds (model, reg, diag_arg, range, sval_hint)
   {}
 
   const char *get_kind () const final override
@@ -394,7 +493,7 @@
     return "concrete_buffer_underwrite";
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *logger) final override
   {
     diagnostic_metadata m;
     bool warned;
@@ -415,7 +514,7 @@
 	break;
       }
     if (warned)
-      maybe_describe_array_bounds (rich_loc->get_loc ());
+      maybe_show_notes (rich_loc->get_loc (), logger);
     return warned;
   }
 
@@ -449,6 +548,8 @@
 				   start_buf, end_buf);;
       }
   }
+
+  enum access_direction get_dir () const final override { return DIR_WRITE; }
 };
 
 /* Concrete subclass to complain about buffer under-reads.  */
@@ -456,9 +557,10 @@
 class concrete_buffer_under_read : public concrete_out_of_bounds
 {
 public:
-  concrete_buffer_under_read (const region *reg, tree diag_arg,
+  concrete_buffer_under_read (const region_model &model,
+			      const region *reg, tree diag_arg,
 			      byte_range range)
-  : concrete_out_of_bounds (reg, diag_arg, range)
+  : concrete_out_of_bounds (model, reg, diag_arg, range, NULL)
   {}
 
   const char *get_kind () const final override
@@ -466,7 +568,7 @@
     return "concrete_buffer_under_read";
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *logger) final override
   {
     diagnostic_metadata m;
     bool warned;
@@ -487,7 +589,7 @@
 	break;
       }
     if (warned)
-      maybe_describe_array_bounds (rich_loc->get_loc ());
+      maybe_show_notes (rich_loc->get_loc (), logger);
     return warned;
   }
 
@@ -521,6 +623,8 @@
 				   start_buf, end_buf);;
       }
   }
+
+  enum access_direction get_dir () const final override { return DIR_READ; }
 };
 
 /* Abstract class to complain about out-of-bounds read/writes where
@@ -529,9 +633,11 @@
 class symbolic_past_the_end : public out_of_bounds
 {
 public:
-  symbolic_past_the_end (const region *reg, tree diag_arg, tree offset,
-			 tree num_bytes, tree capacity)
-  : out_of_bounds (reg, diag_arg),
+  symbolic_past_the_end (const region_model &model,
+			 const region *reg, tree diag_arg, tree offset,
+			 tree num_bytes, tree capacity,
+			 const svalue *sval_hint)
+  : out_of_bounds (model, reg, diag_arg, sval_hint),
     m_offset (offset),
     m_num_bytes (num_bytes),
     m_capacity (capacity)
@@ -559,9 +665,12 @@
 class symbolic_buffer_overflow : public symbolic_past_the_end
 {
 public:
-  symbolic_buffer_overflow (const region *reg, tree diag_arg, tree offset,
-			    tree num_bytes, tree capacity)
-  : symbolic_past_the_end (reg, diag_arg, offset, num_bytes, capacity)
+  symbolic_buffer_overflow (const region_model &model,
+			    const region *reg, tree diag_arg, tree offset,
+			    tree num_bytes, tree capacity,
+			    const svalue *sval_hint)
+  : symbolic_past_the_end (model, reg, diag_arg, offset, num_bytes, capacity,
+			   sval_hint)
   {
   }
 
@@ -570,24 +679,31 @@
     return "symbolic_buffer_overflow";
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *logger) final override
   {
     diagnostic_metadata m;
+    bool warned;
     switch (get_memory_space ())
       {
       default:
 	m.add_cwe (787);
-	return warning_meta (rich_loc, m, get_controlling_option (),
-			     "buffer overflow");
+	warned = warning_meta (rich_loc, m, get_controlling_option (),
+			       "buffer overflow");
+	break;
       case MEMSPACE_STACK:
 	m.add_cwe (121);
-	return warning_meta (rich_loc, m, get_controlling_option (),
-			     "stack-based buffer overflow");
+	warned = warning_meta (rich_loc, m, get_controlling_option (),
+			       "stack-based buffer overflow");
+	break;
       case MEMSPACE_HEAP:
 	m.add_cwe (122);
-	return warning_meta (rich_loc, m, get_controlling_option (),
-			     "heap-based buffer overflow");
+	warned =  warning_meta (rich_loc, m, get_controlling_option (),
+				"heap-based buffer overflow");
+	break;
       }
+    if (warned)
+      maybe_show_notes (rich_loc->get_loc (), logger);
+    return warned;
   }
 
   label_text
@@ -658,6 +774,8 @@
 				 m_diag_arg);
     return ev.formatted_print ("out-of-bounds write");
   }
+
+  enum access_direction get_dir () const final override { return DIR_WRITE; }
 };
 
 /* Concrete subclass to complain about over-reads with symbolic values.  */
@@ -665,9 +783,11 @@
 class symbolic_buffer_over_read : public symbolic_past_the_end
 {
 public:
-  symbolic_buffer_over_read (const region *reg, tree diag_arg, tree offset,
+  symbolic_buffer_over_read (const region_model &model,
+			     const region *reg, tree diag_arg, tree offset,
 			     tree num_bytes, tree capacity)
-  : symbolic_past_the_end (reg, diag_arg, offset, num_bytes, capacity)
+  : symbolic_past_the_end (model, reg, diag_arg, offset, num_bytes, capacity,
+			   NULL)
   {
   }
 
@@ -676,25 +796,32 @@
     return "symbolic_buffer_over_read";
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *logger) final override
   {
     diagnostic_metadata m;
     m.add_cwe (126);
+    bool warned;
     switch (get_memory_space ())
       {
       default:
 	m.add_cwe (787);
-	return warning_meta (rich_loc, m, get_controlling_option (),
-			     "buffer over-read");
+	warned = warning_meta (rich_loc, m, get_controlling_option (),
+			       "buffer over-read");
+	break;
       case MEMSPACE_STACK:
 	m.add_cwe (121);
-	return warning_meta (rich_loc, m, get_controlling_option (),
-			     "stack-based buffer over-read");
+	warned = warning_meta (rich_loc, m, get_controlling_option (),
+			       "stack-based buffer over-read");
+	break;
       case MEMSPACE_HEAP:
 	m.add_cwe (122);
-	return warning_meta (rich_loc, m, get_controlling_option (),
-			     "heap-based buffer over-read");
+	warned = warning_meta (rich_loc, m, get_controlling_option (),
+			       "heap-based buffer over-read");
+	break;
       }
+    if (warned)
+      maybe_show_notes (rich_loc->get_loc (), logger);
+    return warned;
   }
 
   label_text
@@ -765,6 +892,8 @@
 				 m_diag_arg);
     return ev.formatted_print ("out-of-bounds read");
   }
+
+  enum access_direction get_dir () const final override { return DIR_READ; }
 };
 
 /* Check whether an access is past the end of the BASE_REG.
@@ -776,6 +905,7 @@
 				     const svalue *num_bytes_sval,
 				     const svalue *capacity,
 				     enum access_direction dir,
+				     const svalue *sval_hint,
 				     region_model_context *ctxt) const
 {
   gcc_assert (ctxt);
@@ -790,13 +920,21 @@
       tree offset_tree = get_representative_tree (sym_byte_offset);
       tree num_bytes_tree = get_representative_tree (num_bytes_sval);
       tree capacity_tree = get_representative_tree (capacity);
+      const region *offset_reg = m_mgr->get_offset_region (base_reg,
+							   NULL_TREE,
+							   sym_byte_offset);
+      const region *sized_offset_reg = m_mgr->get_sized_region (offset_reg,
+								NULL_TREE,
+								num_bytes_sval);
       switch (dir)
 	{
 	default:
 	  gcc_unreachable ();
 	  break;
 	case DIR_READ:
-	  ctxt->warn (make_unique<symbolic_buffer_over_read> (base_reg,
+	  gcc_assert (sval_hint == nullptr);
+	  ctxt->warn (make_unique<symbolic_buffer_over_read> (*this,
+							      sized_offset_reg,
 							      diag_arg,
 							      offset_tree,
 							      num_bytes_tree,
@@ -804,11 +942,13 @@
 	  return false;
 	  break;
 	case DIR_WRITE:
-	  ctxt->warn (make_unique<symbolic_buffer_overflow> (base_reg,
+	  ctxt->warn (make_unique<symbolic_buffer_overflow> (*this,
+							     sized_offset_reg,
 							     diag_arg,
 							     offset_tree,
 							     num_bytes_tree,
-							     capacity_tree));
+							     capacity_tree,
+							     sval_hint));
 	  return false;
 	  break;
 	}
@@ -832,6 +972,7 @@
 bool
 region_model::check_region_bounds (const region *reg,
 				   enum access_direction dir,
+				   const svalue *sval_hint,
 				   region_model_context *ctxt) const
 {
   gcc_assert (ctxt);
@@ -882,8 +1023,8 @@
 	}
       else
 	byte_offset_sval = reg_offset.get_symbolic_byte_offset ();
-		  return check_symbolic_bounds (base_reg, byte_offset_sval, num_bytes_sval,
-			     capacity, dir, ctxt);
+      return check_symbolic_bounds (base_reg, byte_offset_sval, num_bytes_sval,
+			     capacity, dir, sval_hint, ctxt);
     }
 
   /* Otherwise continue to check with concrete values.  */
@@ -902,13 +1043,17 @@
 	  gcc_unreachable ();
 	  break;
 	case DIR_READ:
-	  ctxt->warn (make_unique<concrete_buffer_under_read> (reg, diag_arg,
+	  gcc_assert (sval_hint == nullptr);
+	  ctxt->warn (make_unique<concrete_buffer_under_read> (*this, reg,
+							       diag_arg,
 							       out));
 	  oob_safe = false;
 	  break;
 	case DIR_WRITE:
-	  ctxt->warn (make_unique<concrete_buffer_underwrite> (reg, diag_arg,
-							       out));
+	  ctxt->warn (make_unique<concrete_buffer_underwrite> (*this,
+							       reg, diag_arg,
+							       out,
+							       sval_hint));
 	  oob_safe = false;
 	  break;
 	}
@@ -934,13 +1079,17 @@
 	  gcc_unreachable ();
 	  break;
 	case DIR_READ:
-	  ctxt->warn (make_unique<concrete_buffer_over_read> (reg, diag_arg,
+	  gcc_assert (sval_hint == nullptr);
+	  ctxt->warn (make_unique<concrete_buffer_over_read> (*this,
+							      reg, diag_arg,
 							      out, byte_bound));
 	  oob_safe = false;
 	  break;
 	case DIR_WRITE:
-	  ctxt->warn (make_unique<concrete_buffer_overflow> (reg, diag_arg,
-							     out, byte_bound));
+	  ctxt->warn (make_unique<concrete_buffer_overflow> (*this,
+							     reg, diag_arg,
+							     out, byte_bound,
+							     sval_hint));
 	  oob_safe = false;
 	  break;
 	}
diff --git a/gcc/analyzer/diagnostic-manager.cc b/gcc/analyzer/diagnostic-manager.cc
index 0a447f7..cfca305 100644
--- a/gcc/analyzer/diagnostic-manager.cc
+++ b/gcc/analyzer/diagnostic-manager.cc
@@ -1421,7 +1421,7 @@
 
   auto_diagnostic_group d;
   auto_cfun sentinel (sd.m_snode->m_fun);
-  if (sd.m_d->emit (&rich_loc))
+  if (sd.m_d->emit (&rich_loc, get_logger ()))
     {
       sd.emit_any_notes ();
 
diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index a5965c2..61685f4 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -1771,7 +1771,7 @@
     return OPT_Wanalyzer_stale_setjmp_buffer;
   }
 
-  bool emit (rich_location *richloc) final override
+  bool emit (rich_location *richloc, logger *) final override
   {
     return warning_at
       (richloc, get_controlling_option (),
@@ -3925,7 +3925,7 @@
     return OPT_Wanalyzer_jump_through_null;
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *) final override
   {
     return warning_at (rich_loc, get_controlling_option (),
 		       "jump through null pointer");
diff --git a/gcc/analyzer/infinite-recursion.cc b/gcc/analyzer/infinite-recursion.cc
index c262e39..3ba316e 100644
--- a/gcc/analyzer/infinite-recursion.cc
+++ b/gcc/analyzer/infinite-recursion.cc
@@ -95,7 +95,7 @@
     return OPT_Wanalyzer_infinite_recursion;
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *) final override
   {
     /* "CWE-674: Uncontrolled Recursion".  */
     diagnostic_metadata m;
diff --git a/gcc/analyzer/kf-analyzer.cc b/gcc/analyzer/kf-analyzer.cc
index 32aa87b..5aed007 100644
--- a/gcc/analyzer/kf-analyzer.cc
+++ b/gcc/analyzer/kf-analyzer.cc
@@ -255,7 +255,7 @@
     return 0;
   }
 
-  bool emit (rich_location *richloc) final override
+  bool emit (rich_location *richloc, logger *) final override
   {
     inform (richloc, "path");
     return true;
diff --git a/gcc/analyzer/kf.cc b/gcc/analyzer/kf.cc
index 1044111..3e319a0 100644
--- a/gcc/analyzer/kf.cc
+++ b/gcc/analyzer/kf.cc
@@ -553,7 +553,9 @@
   const region *sized_dest_reg = mgr->get_sized_region (dest_reg,
 							NULL_TREE,
 							num_bytes_sval);
-  model->check_region_for_write (sized_dest_reg, cd.get_ctxt ());
+  model->check_region_for_write (sized_dest_reg,
+				 nullptr,
+				 cd.get_ctxt ());
   model->fill_region (sized_dest_reg, fill_value_u8);
 }
 
@@ -587,7 +589,7 @@
     return OPT_Wanalyzer_putenv_of_auto_var;
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *) final override
   {
     auto_diagnostic_group d;
     diagnostic_metadata m;
diff --git a/gcc/analyzer/pending-diagnostic.h b/gcc/analyzer/pending-diagnostic.h
index 6423c8b..7582b37 100644
--- a/gcc/analyzer/pending-diagnostic.h
+++ b/gcc/analyzer/pending-diagnostic.h
@@ -180,7 +180,7 @@
   /* Vfunc for emitting the diagnostic.  The rich_location will have been
      populated with a diagnostic_path.
      Return true if a diagnostic is actually emitted.  */
-  virtual bool emit (rich_location *) = 0;
+  virtual bool emit (rich_location *, logger *) = 0;
 
   /* Hand-coded RTTI: get an ID for the subclass.  */
   virtual const char *get_kind () const = 0;
diff --git a/gcc/analyzer/region-model-manager.cc b/gcc/analyzer/region-model-manager.cc
index 3b95e43..1453acf 100644
--- a/gcc/analyzer/region-model-manager.cc
+++ b/gcc/analyzer/region-model-manager.cc
@@ -230,10 +230,11 @@
    for VAL of type TYPE, creating it if necessary.  */
 
 const svalue *
-region_model_manager::get_or_create_int_cst (tree type, poly_int64 val)
+region_model_manager::get_or_create_int_cst (tree type,
+					     const poly_wide_int_ref &cst)
 {
   gcc_assert (type);
-  tree tree_cst = build_int_cst (type, val);
+  tree tree_cst = wide_int_to_tree (type, cst);
   return get_or_create_constant_svalue (tree_cst);
 }
 
@@ -612,7 +613,7 @@
 	  return get_or_create_constant_svalue (result);
     }
 
-  if (FLOAT_TYPE_P (type)
+  if ((type && FLOAT_TYPE_P (type))
       || (arg0->get_type () && FLOAT_TYPE_P (arg0->get_type ()))
       || (arg1->get_type () && FLOAT_TYPE_P (arg1->get_type ())))
     return NULL;
@@ -634,6 +635,11 @@
       /* (0 - VAL) -> -VAL.  */
       if (cst0 && zerop (cst0))
 	return get_or_create_unaryop (type, NEGATE_EXPR, arg1);
+      /* (X + Y) - X -> Y.  */
+      if (const binop_svalue *binop = arg0->dyn_cast_binop_svalue ())
+	if (binop->get_op () == PLUS_EXPR)
+	  if (binop->get_arg0 () == arg1)
+	    return get_or_create_cast (type, binop->get_arg1 ());
       break;
     case MULT_EXPR:
       /* (VAL * 0).  */
@@ -726,10 +732,7 @@
   if (cst1 && associative_tree_code (op))
     if (const binop_svalue *binop = arg0->dyn_cast_binop_svalue ())
       if (binop->get_op () == op
-	  && binop->get_arg1 ()->maybe_get_constant ()
-	  && type == binop->get_type ()
-	  && type == binop->get_arg0 ()->get_type ()
-	  && type == binop->get_arg1 ()->get_type ())
+	  && binop->get_arg1 ()->maybe_get_constant ())
 	return get_or_create_binop
 	  (type, op, binop->get_arg0 (),
 	   get_or_create_binop (type, op,
@@ -748,6 +751,21 @@
 	     get_or_create_binop (size_type_node, op,
 				  binop->get_arg1 (), arg1));
 
+  /* Distribute multiplication by a constant through addition/subtraction:
+     (X + Y) * CST => (X * CST) + (Y * CST).  */
+  if (cst1 && op == MULT_EXPR)
+    if (const binop_svalue *binop = arg0->dyn_cast_binop_svalue ())
+      if (binop->get_op () == PLUS_EXPR
+	  || binop->get_op () == MINUS_EXPR)
+	{
+	  return get_or_create_binop
+	    (type, binop->get_op (),
+	     get_or_create_binop (type, op,
+				  binop->get_arg0 (), arg1),
+	     get_or_create_binop (type, op,
+				  binop->get_arg1 (), arg1));
+	}
+
   /* etc.  */
 
   return NULL;
diff --git a/gcc/analyzer/region-model-manager.h b/gcc/analyzer/region-model-manager.h
index 273fe7b..3340c3e 100644
--- a/gcc/analyzer/region-model-manager.h
+++ b/gcc/analyzer/region-model-manager.h
@@ -42,7 +42,7 @@
 
   /* svalue consolidation.  */
   const svalue *get_or_create_constant_svalue (tree cst_expr);
-  const svalue *get_or_create_int_cst (tree type, poly_int64);
+  const svalue *get_or_create_int_cst (tree type, const poly_wide_int_ref &cst);
   const svalue *get_or_create_null_ptr (tree pointer_type);
   const svalue *get_or_create_unknown_svalue (tree type);
   const svalue *get_or_create_setjmp_svalue (const setjmp_record &r,
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index 598196a..6bc60f8 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -507,7 +507,7 @@
 
   bool terminate_path_p () const final override { return true; }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *) final override
   {
     switch (m_pkind)
       {
@@ -638,7 +638,7 @@
     return OPT_Wanalyzer_shift_count_negative;
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *) final override
   {
     return warning_at (rich_loc, get_controlling_option (),
 		       "shift by negative count (%qE)", m_count_cst);
@@ -685,7 +685,7 @@
     return OPT_Wanalyzer_shift_count_overflow;
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *) final override
   {
     return warning_at (rich_loc, get_controlling_option (),
 		       "shift by count (%qE) >= precision of type (%qi)",
@@ -1736,7 +1736,7 @@
 	  tree ptr_tree = gimple_call_arg (call, access->ptrarg);
 	  const svalue *ptr_sval = get_rvalue (ptr_tree, &my_ctxt);
 	  const region *reg = deref_rvalue (ptr_sval, ptr_tree, &my_ctxt);
-	  check_region_for_write (reg, &my_ctxt);
+	  check_region_for_write (reg, nullptr, &my_ctxt);
 	  /* We don't use the size arg for now.  */
 	}
     }
@@ -2522,8 +2522,8 @@
 		const poisoned_svalue *poisoned_sval
 		  = as_a <const poisoned_svalue *> (ptr_sval);
 		enum poison_kind pkind = poisoned_sval->get_poison_kind ();
-		ctxt->warn (make_unique<poisoned_value_diagnostic>
-			      (ptr, pkind, NULL, NULL));
+		ctxt->warn (::make_unique<poisoned_value_diagnostic>
+			      (ptr, pkind, nullptr, nullptr));
 	      }
 	  }
       }
@@ -2576,7 +2576,7 @@
     return OPT_Wanalyzer_write_to_const;
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *) final override
   {
     auto_diagnostic_group d;
     bool warned;
@@ -2644,7 +2644,7 @@
     return OPT_Wanalyzer_write_to_string_literal;
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *) final override
   {
     return warning_at (rich_loc, get_controlling_option (),
 		       "write to string literal");
@@ -2742,6 +2742,15 @@
       /* Look through sized regions to get at the capacity
 	 of the underlying regions.  */
       return get_capacity (reg->get_parent_region ());
+    case RK_STRING:
+      {
+	/* "Capacity" here means "size".  */
+	const string_region *string_reg = as_a <const string_region *> (reg);
+	tree string_cst = string_reg->get_string_cst ();
+	return m_mgr->get_or_create_int_cst (size_type_node,
+					     TREE_STRING_LENGTH (string_cst));
+      }
+      break;
     }
 
   if (const svalue *recorded = get_dynamic_extents (reg))
@@ -2781,11 +2790,14 @@
 
 /* If CTXT is non-NULL, use it to warn about any problems accessing REG,
    using DIR to determine if this access is a read or write.
-   Return TRUE if an UNKNOWN_SVALUE needs be created.  */
+   Return TRUE if an UNKNOWN_SVALUE needs be created.
+   If SVAL_HINT is non-NULL, use it as a hint in diagnostics
+   about the value that would be written to REG.  */
 
 bool
 region_model::check_region_access (const region *reg,
 				   enum access_direction dir,
+				   const svalue *sval_hint,
 				   region_model_context *ctxt) const
 {
   /* Fail gracefully if CTXT is NULL.  */
@@ -2794,7 +2806,7 @@
 
   bool need_unknown_sval = false;
   check_region_for_taint (reg, dir, ctxt);
-  if (!check_region_bounds (reg, dir, ctxt))
+  if (!check_region_bounds (reg, dir, sval_hint, ctxt))
     need_unknown_sval = true;
 
   switch (dir)
@@ -2815,9 +2827,10 @@
 
 void
 region_model::check_region_for_write (const region *dest_reg,
+				      const svalue *sval_hint,
 				      region_model_context *ctxt) const
 {
-  check_region_access (dest_reg, DIR_WRITE, ctxt);
+  check_region_access (dest_reg, DIR_WRITE, sval_hint, ctxt);
 }
 
 /* If CTXT is non-NULL, use it to warn about any problems reading from REG.
@@ -2827,7 +2840,7 @@
 region_model::check_region_for_read (const region *src_reg,
 				     region_model_context *ctxt) const
 {
-  return check_region_access (src_reg, DIR_READ, ctxt);
+  return check_region_access (src_reg, DIR_READ, NULL, ctxt);
 }
 
 /* Concrete subclass for casts of pointers that lead to trailing bytes.  */
@@ -2863,7 +2876,7 @@
     return OPT_Wanalyzer_allocation_size;
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *) final override
   {
     diagnostic_metadata m;
     m.add_cwe (131);
@@ -3203,7 +3216,7 @@
 
   check_region_size (lhs_reg, rhs_sval, ctxt);
 
-  check_region_for_write (lhs_reg, ctxt);
+  check_region_for_write (lhs_reg, rhs_sval, ctxt);
 
   m_store.set_value (m_mgr->get_store_manager(), lhs_reg, rhs_sval,
 		     ctxt ? ctxt->get_uncertainty () : NULL);
@@ -3836,7 +3849,12 @@
 
   /* Prevent infinite recursion.  */
   if (visited->contains (sval))
-    return path_var (NULL_TREE, 0);
+    {
+      if (sval->get_kind () == SK_CONSTANT)
+	return path_var (sval->maybe_get_constant (), 0);
+      else
+	return path_var (NULL_TREE, 0);
+    }
   visited->add (sval);
 
   /* Handle casts by recursion into get_representative_path_var.  */
@@ -4941,7 +4959,7 @@
     return same_tree_p (m_arg, ((const float_as_size_arg &) other).m_arg);
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *) final override
   {
     diagnostic_metadata m;
     bool warned = warning_meta (rich_loc, m, get_controlling_option (),
@@ -5303,7 +5321,7 @@
     return OPT_Wanalyzer_exposure_through_uninit_copy;
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *) final override
   {
     diagnostic_metadata m;
     /* CWE-200: Exposure of Sensitive Information to an Unauthorized Actor.  */
diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h
index 12f84b2..d6d9615 100644
--- a/gcc/analyzer/region-model.h
+++ b/gcc/analyzer/region-model.h
@@ -490,6 +490,7 @@
 				  region_model_context *ctxt) const;
 
   void check_region_for_write (const region *dest_reg,
+			       const svalue *sval_hint,
 			       region_model_context *ctxt) const;
 
 private:
@@ -555,6 +556,7 @@
 				  region_model_context *ctxt) const;
   bool check_region_access (const region *reg,
 			    enum access_direction dir,
+			    const svalue *sval_hint,
 			    region_model_context *ctxt) const;
   bool check_region_for_read (const region *src_reg,
 			      region_model_context *ctxt) const;
@@ -567,8 +569,10 @@
 			      const svalue *num_bytes_sval,
 			      const svalue *capacity,
 			      enum access_direction dir,
+			      const svalue *sval_hint,
 			      region_model_context *ctxt) const;
   bool check_region_bounds (const region *reg, enum access_direction dir,
+			    const svalue *sval_hint,
 			    region_model_context *ctxt) const;
 
   void check_call_args (const call_details &cd) const;
diff --git a/gcc/analyzer/region.cc b/gcc/analyzer/region.cc
index 098b436..62ae0b2 100644
--- a/gcc/analyzer/region.cc
+++ b/gcc/analyzer/region.cc
@@ -63,6 +63,332 @@
 
 namespace ana {
 
+region_offset
+region_offset::make_byte_offset (const region *base_region,
+				 const svalue *num_bytes_sval)
+{
+  if (tree num_bytes_cst = num_bytes_sval->maybe_get_constant ())
+    {
+      gcc_assert (TREE_CODE (num_bytes_cst) == INTEGER_CST);
+      bit_offset_t num_bits = wi::to_offset (num_bytes_cst) * BITS_PER_UNIT;
+      return make_concrete (base_region, num_bits);
+    }
+  else
+    {
+      return make_symbolic (base_region, num_bytes_sval);
+    }
+}
+
+tree
+region_offset::calc_symbolic_bit_offset (const region_model &model) const
+{
+  if (symbolic_p ())
+    {
+      tree num_bytes_expr = model.get_representative_tree (m_sym_offset);
+      if (!num_bytes_expr)
+	return NULL_TREE;
+      tree bytes_to_bits_scale = build_int_cst (size_type_node, BITS_PER_UNIT);
+      return fold_build2 (MULT_EXPR, size_type_node,
+			  num_bytes_expr, bytes_to_bits_scale);
+    }
+  else
+    {
+      tree cst = wide_int_to_tree (size_type_node, m_offset);
+      return cst;
+    }
+}
+
+const svalue *
+region_offset::calc_symbolic_byte_offset (region_model_manager *mgr) const
+{
+  if (symbolic_p ())
+    return m_sym_offset;
+  else
+    {
+      byte_offset_t concrete_byte_offset;
+      if (get_concrete_byte_offset (&concrete_byte_offset))
+	return mgr->get_or_create_int_cst (size_type_node,
+					   concrete_byte_offset);
+      else
+	/* Can't handle bitfields; return UNKNOWN.  */
+	return mgr->get_or_create_unknown_svalue (size_type_node);
+    }
+}
+
+void
+region_offset::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  if (symbolic_p ())
+    {
+      /* We don't bother showing the base region.  */
+      pp_string (pp, "byte ");
+      m_sym_offset->dump_to_pp (pp, simple);
+    }
+  else
+    {
+      if (m_offset % BITS_PER_UNIT == 0)
+	{
+	  pp_string (pp, "byte ");
+	  pp_wide_int (pp, m_offset / BITS_PER_UNIT, SIGNED);
+	}
+      else
+	{
+	  pp_string (pp, "bit ");
+	  pp_wide_int (pp, m_offset, SIGNED);
+	}
+    }
+}
+
+DEBUG_FUNCTION void
+region_offset::dump (bool simple) const
+{
+  pretty_printer pp;
+  pp_format_decoder (&pp) = default_tree_printer;
+  pp_show_color (&pp) = pp_show_color (global_dc->printer);
+  pp.buffer->stream = stderr;
+  dump_to_pp (&pp, simple);
+  pp_newline (&pp);
+  pp_flush (&pp);
+}
+
+/* An svalue that matches the pattern (BASE * FACTOR) + OFFSET
+   where FACTOR or OFFSET could be the identity (represented as NULL).  */
+
+struct linear_op
+{
+  linear_op (const svalue *base,
+	     const svalue *factor,
+	     const svalue *offset)
+  : m_base (base), m_factor (factor), m_offset (offset)
+  {
+  }
+
+  bool maybe_get_cst_factor (bit_offset_t *out) const
+  {
+    if (m_factor == nullptr)
+      {
+	*out = 1;
+	return true;
+      }
+    if (tree cst_factor = m_factor->maybe_get_constant ())
+      {
+	*out = wi::to_offset (cst_factor);
+	return true;
+      }
+    return false;
+  }
+
+  bool maybe_get_cst_offset (bit_offset_t *out) const
+  {
+    if (m_offset == nullptr)
+      {
+	*out = 0;
+	return true;
+      }
+    if (tree cst_offset = m_offset->maybe_get_constant ())
+      {
+	*out = wi::to_offset (cst_offset);
+	return true;
+      }
+    return false;
+  }
+
+  static tristate
+  less (const linear_op &a, const linear_op &b)
+  {
+    /* Same base.  */
+    if (a.m_base == b.m_base)
+      {
+	bit_offset_t a_wi_factor;
+	bit_offset_t b_wi_factor;
+	if (a.maybe_get_cst_factor (&a_wi_factor)
+	    && b.maybe_get_cst_factor (&b_wi_factor))
+	  {
+	    if (a_wi_factor != b_wi_factor)
+	      return tristate (a_wi_factor < b_wi_factor);
+	    else
+	      {
+		bit_offset_t a_wi_offset;
+		bit_offset_t b_wi_offset;
+		if (a.maybe_get_cst_offset (&a_wi_offset)
+		    && b.maybe_get_cst_offset (&b_wi_offset))
+		  return tristate (a_wi_offset < b_wi_offset);
+	      }
+	  }
+      }
+    return tristate::unknown ();
+  }
+
+  static tristate
+  le (const linear_op &a, const linear_op &b)
+  {
+    /* Same base.  */
+    if (a.m_base == b.m_base)
+      {
+	bit_offset_t a_wi_factor;
+	bit_offset_t b_wi_factor;
+	if (a.maybe_get_cst_factor (&a_wi_factor)
+	    && b.maybe_get_cst_factor (&b_wi_factor))
+	  {
+	    if (a_wi_factor != b_wi_factor)
+	      return tristate (a_wi_factor <= b_wi_factor);
+	    else
+	      {
+		bit_offset_t a_wi_offset;
+		bit_offset_t b_wi_offset;
+		if (a.maybe_get_cst_offset (&a_wi_offset)
+		    && b.maybe_get_cst_offset (&b_wi_offset))
+		  return tristate (a_wi_offset <= b_wi_offset);
+	      }
+	  }
+      }
+    return tristate::unknown ();
+  }
+
+  static bool
+  from_svalue (const svalue &sval, linear_op *out)
+  {
+    switch (sval.get_kind ())
+      {
+      default:
+	break;
+      case SK_BINOP:
+	{
+	  const binop_svalue &binop_sval ((const binop_svalue &)sval);
+	  if (binop_sval.get_op () == MULT_EXPR)
+	    {
+	      *out = linear_op (binop_sval.get_arg0 (),
+				binop_sval.get_arg1 (),
+				NULL);
+	      return true;
+	    }
+	  else if (binop_sval.get_op () == PLUS_EXPR)
+	    {
+	      if (binop_sval.get_arg0 ()->get_kind () == SK_BINOP)
+		{
+		  const binop_svalue &inner_binop_sval
+		    ((const binop_svalue &)*binop_sval.get_arg0 ());
+		  if (inner_binop_sval.get_op () == MULT_EXPR)
+		    {
+		      *out = linear_op (inner_binop_sval.get_arg0 (),
+					inner_binop_sval.get_arg1 (),
+					binop_sval.get_arg1 ());
+		      return true;
+		    }
+		}
+
+	      *out = linear_op (binop_sval.get_arg0 (),
+				NULL,
+				binop_sval.get_arg1 ());
+	      return true;
+	    }
+	}
+	break;
+      }
+    return false;
+  }
+
+  const svalue *m_base;
+  const svalue *m_factor;
+  const svalue *m_offset;
+};
+
+bool
+operator< (const region_offset &a, const region_offset &b)
+{
+  if (a.symbolic_p ())
+    {
+      if (b.symbolic_p ())
+	{
+	  /* Symbolic vs symbolic.  */
+	  const svalue &a_sval = *a.get_symbolic_byte_offset ();
+	  const svalue &b_sval = *b.get_symbolic_byte_offset ();
+
+	  linear_op op_a (NULL, NULL, NULL);
+	  linear_op op_b (NULL, NULL, NULL);
+	  if (linear_op::from_svalue (a_sval, &op_a)
+	      && linear_op::from_svalue (b_sval, &op_b))
+	    {
+	      tristate ts = linear_op::less (op_a, op_b);
+	      if (ts.is_true ())
+		return true;
+	      else if (ts.is_false ())
+		return false;
+	    }
+	  /* Use svalue's deterministic order, for now.  */
+	  return (svalue::cmp_ptr (a.get_symbolic_byte_offset (),
+				   b.get_symbolic_byte_offset ())
+		  < 0);
+	}
+      else
+	/* Symbolic vs concrete: put all symbolic after all concrete.  */
+	return false;
+    }
+  else
+    {
+      if (b.symbolic_p ())
+	/* Concrete vs symbolic: put all concrete before all symbolic.  */
+	return true;
+      else
+	/* Concrete vs concrete.  */
+	return a.get_bit_offset () < b.get_bit_offset ();
+    }
+}
+
+bool
+operator<= (const region_offset &a, const region_offset &b)
+{
+  if (a.symbolic_p ())
+    {
+      if (b.symbolic_p ())
+	{
+	  /* Symbolic vs symbolic.  */
+	  const svalue &a_sval = *a.get_symbolic_byte_offset ();
+	  const svalue &b_sval = *b.get_symbolic_byte_offset ();
+
+	  linear_op op_a (NULL, NULL, NULL);
+	  linear_op op_b (NULL, NULL, NULL);
+	  if (linear_op::from_svalue (a_sval, &op_a)
+	      && linear_op::from_svalue (b_sval, &op_b))
+	    {
+	      tristate ts = linear_op::le (op_a, op_b);
+	      if (ts.is_true ())
+		return true;
+	      else if (ts.is_false ())
+		return false;
+	    }
+	  /* Use svalue's deterministic order, for now.  */
+	  return (svalue::cmp_ptr (a.get_symbolic_byte_offset (),
+				   b.get_symbolic_byte_offset ())
+		  <= 0);
+	}
+      else
+	/* Symbolic vs concrete: put all symbolic after all concrete.  */
+	return false;
+    }
+  else
+    {
+      if (b.symbolic_p ())
+	/* Concrete vs symbolic: put all concrete before all symbolic.  */
+	return true;
+      else
+	/* Concrete vs concrete.  */
+	return a.get_bit_offset () <= b.get_bit_offset ();
+    }
+}
+
+bool
+operator> (const region_offset &a, const region_offset &b)
+{
+  return b < a;
+}
+
+bool
+operator>= (const region_offset &a, const region_offset &b)
+{
+  return b <= a;
+}
+
 /* class region and its various subclasses.  */
 
 /* class region.  */
@@ -339,6 +665,35 @@
   return *m_cached_offset;
 }
 
+/* Get the region_offset for immediately beyond this region.  */
+
+region_offset
+region::get_next_offset (region_model_manager *mgr) const
+{
+  region_offset start = get_offset (mgr);
+
+  bit_size_t bit_size;
+  if (get_bit_size (&bit_size))
+    {
+      if (start.concrete_p ())
+	{
+	  bit_offset_t next_bit_offset = start.get_bit_offset () + bit_size;
+	  return region_offset::make_concrete (start.get_base_region (),
+					       next_bit_offset);
+	}
+    }
+
+  const svalue *start_byte_offset_sval = start.calc_symbolic_byte_offset (mgr);
+  const svalue *byte_size_sval = get_byte_size_sval (mgr);
+  const svalue *sum_sval
+    = mgr->get_or_create_binop (size_type_node,
+				PLUS_EXPR,
+				start_byte_offset_sval,
+				byte_size_sval);
+  return region_offset::make_symbolic (start.get_base_region (),
+				       sum_sval);
+}
+
 /* Base class implementation of region::get_byte_size vfunc.
    If the size of this region (in bytes) is known statically, write it to *OUT
    and return true.
@@ -617,7 +972,7 @@
 const svalue *
 region::get_relative_symbolic_offset (region_model_manager *mgr) const
 {
-  return mgr->get_or_create_unknown_svalue (integer_type_node);
+  return mgr->get_or_create_unknown_svalue (ptrdiff_type_node);
 }
 
 /* Attempt to get the position and size of this region expressed as a
@@ -1448,10 +1803,10 @@
   if (get_relative_concrete_offset (&out))
     {
       tree cst_tree
-	= wide_int_to_tree (integer_type_node, out / BITS_PER_UNIT);
+	= wide_int_to_tree (ptrdiff_type_node, out / BITS_PER_UNIT);
       return mgr->get_or_create_constant_svalue (cst_tree);
     }
-  return mgr->get_or_create_unknown_svalue (integer_type_node);
+  return mgr->get_or_create_unknown_svalue (ptrdiff_type_node);
 }
 
 /* class element_region : public region.  */
@@ -1533,14 +1888,14 @@
   HOST_WIDE_INT hwi_byte_size = int_size_in_bytes (elem_type);
   if (hwi_byte_size > 0)
 	  {
-      tree byte_size_tree = wide_int_to_tree (integer_type_node,
+      tree byte_size_tree = wide_int_to_tree (ptrdiff_type_node,
 					      hwi_byte_size);
       const svalue *byte_size_sval
 	= mgr->get_or_create_constant_svalue (byte_size_tree);
-      return mgr->get_or_create_binop (integer_type_node, MULT_EXPR,
+      return mgr->get_or_create_binop (ptrdiff_type_node, MULT_EXPR,
 				       m_index, byte_size_sval);
     }
-  return mgr->get_or_create_unknown_svalue (integer_type_node);
+  return mgr->get_or_create_unknown_svalue (ptrdiff_type_node);
 }
 
 /* class offset_region : public region.  */
@@ -1864,7 +2219,7 @@
   const
 {
   byte_offset_t start_byte = m_bits.get_start_bit_offset () / BITS_PER_UNIT;
-  tree start_bit_tree = wide_int_to_tree (integer_type_node, start_byte);
+  tree start_bit_tree = wide_int_to_tree (ptrdiff_type_node, start_byte);
   return mgr->get_or_create_constant_svalue (start_bit_tree);
 }
 
diff --git a/gcc/analyzer/region.h b/gcc/analyzer/region.h
index 270e5042..0c79490 100644
--- a/gcc/analyzer/region.h
+++ b/gcc/analyzer/region.h
@@ -183,6 +183,7 @@
   bool involves_p (const svalue *sval) const;
 
   region_offset get_offset (region_model_manager *mgr) const;
+  region_offset get_next_offset (region_model_manager *mgr) const;
 
   /* Attempt to get the size of this region as a concrete number of bytes.
      If successful, return true and write the size to *OUT.
diff --git a/gcc/analyzer/sm-fd.cc b/gcc/analyzer/sm-fd.cc
index d107390..03ad359 100644
--- a/gcc/analyzer/sm-fd.cc
+++ b/gcc/analyzer/sm-fd.cc
@@ -465,7 +465,7 @@
   }
 
   bool
-  emit (rich_location *rich_loc) final override
+  emit (rich_location *rich_loc, logger *) final override
   {
     /*CWE-775: Missing Release of File Descriptor or Handle after Effective
       Lifetime
@@ -550,7 +550,7 @@
   }
 
   bool
-  emit (rich_location *rich_loc) final override
+  emit (rich_location *rich_loc, logger *) final override
   {
     bool warned;
     switch (m_fd_dir)
@@ -612,7 +612,7 @@
     return OPT_Wanalyzer_fd_double_close;
   }
   bool
-  emit (rich_location *rich_loc) final override
+  emit (rich_location *rich_loc, logger *) final override
   {
     diagnostic_metadata m;
     // CWE-1341: Multiple Releases of Same Resource or Handle
@@ -677,7 +677,7 @@
   }
 
   bool
-  emit (rich_location *rich_loc) final override
+  emit (rich_location *rich_loc, logger *) final override
   {
     bool warned;
     warned = warning_at (rich_loc, get_controlling_option (),
@@ -748,7 +748,7 @@
   }
 
   bool
-  emit (rich_location *rich_loc) final override
+  emit (rich_location *rich_loc, logger *) final override
   {
     bool warned;
     warned = warning_at (rich_loc, get_controlling_option (),
@@ -859,7 +859,7 @@
   }
 
   bool
-  emit (rich_location *rich_loc) final override
+  emit (rich_location *rich_loc, logger *) final override
   {
     /* CWE-666: Operation on Resource in Wrong Phase of Lifetime.  */
     diagnostic_metadata m;
@@ -1019,7 +1019,7 @@
   }
 
   bool
-  emit (rich_location *rich_loc) final override
+  emit (rich_location *rich_loc, logger *) final override
   {
     switch (m_expected_type)
       {
diff --git a/gcc/analyzer/sm-file.cc b/gcc/analyzer/sm-file.cc
index d99a09b..0cfe682 100644
--- a/gcc/analyzer/sm-file.cc
+++ b/gcc/analyzer/sm-file.cc
@@ -176,7 +176,7 @@
     return OPT_Wanalyzer_double_fclose;
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *) final override
   {
     diagnostic_metadata m;
     /* CWE-1341: Multiple Releases of Same Resource or Handle.  */
@@ -224,7 +224,7 @@
     return OPT_Wanalyzer_file_leak;
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *) final override
   {
     diagnostic_metadata m;
     /* CWE-775: "Missing Release of File Descriptor or Handle after
diff --git a/gcc/analyzer/sm-malloc.cc b/gcc/analyzer/sm-malloc.cc
index 7470137..a8c63eb 100644
--- a/gcc/analyzer/sm-malloc.cc
+++ b/gcc/analyzer/sm-malloc.cc
@@ -835,7 +835,7 @@
     return OPT_Wanalyzer_mismatching_deallocation;
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *) final override
   {
     auto_diagnostic_group d;
     diagnostic_metadata m;
@@ -914,7 +914,7 @@
     return OPT_Wanalyzer_double_free;
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *) final override
   {
     auto_diagnostic_group d;
     diagnostic_metadata m;
@@ -1010,7 +1010,7 @@
     return OPT_Wanalyzer_possible_null_dereference;
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *) final override
   {
     /* CWE-690: Unchecked Return Value to NULL Pointer Dereference.  */
     diagnostic_metadata m;
@@ -1099,7 +1099,7 @@
     return OPT_Wanalyzer_possible_null_argument;
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *) final override
   {
     /* CWE-690: Unchecked Return Value to NULL Pointer Dereference.  */
     auto_diagnostic_group d;
@@ -1152,7 +1152,7 @@
 
   bool terminate_path_p () const final override { return true; }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *) final override
   {
     /* CWE-476: NULL Pointer Dereference.  */
     diagnostic_metadata m;
@@ -1207,7 +1207,7 @@
 
   bool terminate_path_p () const final override { return true; }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *) final override
   {
     /* CWE-476: NULL Pointer Dereference.  */
     auto_diagnostic_group d;
@@ -1264,7 +1264,7 @@
     return OPT_Wanalyzer_use_after_free;
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *) final override
   {
     /* CWE-416: Use After Free.  */
     diagnostic_metadata m;
@@ -1358,7 +1358,7 @@
     return OPT_Wanalyzer_malloc_leak;
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *) final override
   {
     /* "CWE-401: Missing Release of Memory after Effective Lifetime".  */
     diagnostic_metadata m;
@@ -1432,7 +1432,7 @@
     return OPT_Wanalyzer_free_of_non_heap;
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *) final override
   {
     auto_diagnostic_group d;
     diagnostic_metadata m;
@@ -1511,7 +1511,7 @@
     return OPT_Wanalyzer_deref_before_check;
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *) final override
   {
     /* Don't emit the warning if we can't show where the deref
        and the check occur.  */
diff --git a/gcc/analyzer/sm-pattern-test.cc b/gcc/analyzer/sm-pattern-test.cc
index 6c1c950..4c88bca 100644
--- a/gcc/analyzer/sm-pattern-test.cc
+++ b/gcc/analyzer/sm-pattern-test.cc
@@ -92,7 +92,7 @@
     return 0;
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *) final override
   {
     return warning_at (rich_loc, get_controlling_option (),
 		       "pattern match on %<%E %s %E%>",
diff --git a/gcc/analyzer/sm-sensitive.cc b/gcc/analyzer/sm-sensitive.cc
index d94d9e0..0597e39 100644
--- a/gcc/analyzer/sm-sensitive.cc
+++ b/gcc/analyzer/sm-sensitive.cc
@@ -95,7 +95,8 @@
     return OPT_Wanalyzer_exposure_through_output_file;
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc,
+	     logger *) final override
   {
     diagnostic_metadata m;
     /* CWE-532: Information Exposure Through Log Files */
diff --git a/gcc/analyzer/sm-signal.cc b/gcc/analyzer/sm-signal.cc
index ac01f6a..e3f9092 100644
--- a/gcc/analyzer/sm-signal.cc
+++ b/gcc/analyzer/sm-signal.cc
@@ -114,7 +114,7 @@
     return OPT_Wanalyzer_unsafe_call_within_signal_handler;
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *) final override
   {
     auto_diagnostic_group d;
     diagnostic_metadata m;
diff --git a/gcc/analyzer/sm-taint.cc b/gcc/analyzer/sm-taint.cc
index f72f194..6d28d1f 100644
--- a/gcc/analyzer/sm-taint.cc
+++ b/gcc/analyzer/sm-taint.cc
@@ -211,7 +211,7 @@
     return OPT_Wanalyzer_tainted_array_index;
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *) final override
   {
     diagnostic_metadata m;
     /* CWE-129: "Improper Validation of Array Index".  */
@@ -327,7 +327,7 @@
     return OPT_Wanalyzer_tainted_offset;
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *) final override
   {
     diagnostic_metadata m;
     /* CWE-823: "Use of Out-of-range Pointer Offset".  */
@@ -437,7 +437,7 @@
     return OPT_Wanalyzer_tainted_size;
   }
 
-  bool emit (rich_location *rich_loc) override
+  bool emit (rich_location *rich_loc, logger *) override
   {
     /* "CWE-129: Improper Validation of Array Index".  */
     diagnostic_metadata m;
@@ -547,9 +547,9 @@
     return "tainted_access_attrib_size";
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *logger) final override
   {
-    bool warned = tainted_size::emit (rich_loc);
+    bool warned = tainted_size::emit (rich_loc, logger);
     if (warned)
       {
 	inform (DECL_SOURCE_LOCATION (m_callee_fndecl),
@@ -583,7 +583,7 @@
     return OPT_Wanalyzer_tainted_divisor;
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *) final override
   {
     diagnostic_metadata m;
     /* CWE-369: "Divide By Zero".  */
@@ -645,7 +645,7 @@
     return OPT_Wanalyzer_tainted_allocation_size;
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *) final override
   {
     diagnostic_metadata m;
     /* "CWE-789: Memory Allocation with Excessive Size Value".  */
@@ -800,7 +800,7 @@
     return OPT_Wanalyzer_tainted_assertion;
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *) final override
   {
     diagnostic_metadata m;
     /* "CWE-617: Reachable Assertion".  */
diff --git a/gcc/analyzer/store.cc b/gcc/analyzer/store.cc
index 4d1de82..c7bc4b4 100644
--- a/gcc/analyzer/store.cc
+++ b/gcc/analyzer/store.cc
@@ -236,8 +236,8 @@
   pp_flush (&pp);
 }
 
-/* If OTHER is a subset of this, return true and write
-   to *OUT the relative range of OTHER within this.
+/* If OTHER is a subset of this, return true and, if OUT is
+   non-null, write to *OUT the relative range of OTHER within this.
    Otherwise return false.  */
 
 bool
@@ -246,8 +246,11 @@
   if (contains_p (other.get_start_bit_offset ())
       && contains_p (other.get_last_bit_offset ()))
     {
-      out->m_start_bit_offset = other.m_start_bit_offset - m_start_bit_offset;
-      out->m_size_in_bits = other.m_size_in_bits;
+      if (out)
+	{
+	  out->m_start_bit_offset = other.m_start_bit_offset - m_start_bit_offset;
+	  out->m_size_in_bits = other.m_size_in_bits;
+	}
       return true;
     }
   else
diff --git a/gcc/analyzer/store.h b/gcc/analyzer/store.h
index 7ded650..af6cc7e 100644
--- a/gcc/analyzer/store.h
+++ b/gcc/analyzer/store.h
@@ -350,6 +350,15 @@
 		      m_size_in_bytes * BITS_PER_UNIT);
   }
 
+  bit_offset_t get_start_bit_offset () const
+  {
+    return m_start_byte_offset * BITS_PER_UNIT;
+  }
+  bit_offset_t get_next_bit_offset () const
+  {
+    return get_next_byte_offset () * BITS_PER_UNIT;
+  }
+
   static int cmp (const byte_range &br1, const byte_range &br2);
 
   byte_offset_t m_start_byte_offset;
diff --git a/gcc/analyzer/varargs.cc b/gcc/analyzer/varargs.cc
index aeea73a..72e1b31 100644
--- a/gcc/analyzer/varargs.cc
+++ b/gcc/analyzer/varargs.cc
@@ -403,7 +403,7 @@
 	    && 0 == strcmp (m_usage_fnname, other.m_usage_fnname));
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *) final override
   {
     auto_diagnostic_group d;
     return warning_at (rich_loc, get_controlling_option (),
@@ -478,7 +478,7 @@
     return va_list_sm_diagnostic::subclass_equal_p (other);
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *) final override
   {
     auto_diagnostic_group d;
     return warning_at (rich_loc, get_controlling_option (),
@@ -892,7 +892,7 @@
     return OPT_Wanalyzer_va_arg_type_mismatch;
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *) final override
   {
     auto_diagnostic_group d;
     diagnostic_metadata m;
@@ -942,7 +942,7 @@
     return OPT_Wanalyzer_va_list_exhausted;
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *) final override
   {
     auto_diagnostic_group d;
     diagnostic_metadata m;
diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog
index c326324..1247f51 100644
--- a/gcc/c-family/ChangeLog
+++ b/gcc/c-family/ChangeLog
@@ -1,3 +1,21 @@
+2023-06-23  Marek Polacek  <polacek@redhat.com>
+
+	* c-common.h (cxx_dialect): Add cxx26 as a dialect.
+	* c-opts.cc (set_std_cxx26): New.
+	(c_common_handle_option): Set options when -std={c,gnu}++2{c,6} is
+	enabled.
+	(c_common_post_options): Adjust comments.
+	* c.opt: Add options for -std=c++26, std=c++2c, -std=gnu++26,
+	and -std=gnu++2c.
+	(std=c++2b): Mark as Undocumented.
+	(std=c++23): No longer Undocumented.
+
+2023-06-21  Alexander Monakov  <amonakov@ispras.ru>
+
+	* c-gimplify.cc (fma_supported_p): New helper.
+	(c_gimplify_expr) [PLUS_EXPR, MINUS_EXPR]: Implement FMA
+	contraction.
+
 2023-06-16  Alex Coplan  <alex.coplan@arm.com>
 
 	* c.opt (Welaborated-enum-base): New.
diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index 9c8eed5..34566a3 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -1338,6 +1338,10 @@
   int uns;
   tree type;
 
+  /* Do not shorten vector operations.  */
+  if (VECTOR_TYPE_P (result_type))
+    return result_type;
+
   /* Cast OP0 and OP1 to RESULT_TYPE.  Doing so prevents
      excessive narrowing when we call get_narrower below.  For
      example, suppose that OP0 is of unsigned int extended
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 336a09f..b5ef5ff 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -740,7 +740,9 @@
   /* C++20 */
   cxx20,
   /* C++23 */
-  cxx23
+  cxx23,
+  /* C++26 */
+  cxx26
 };
 
 /* The C++ dialect being used. C++98 is the default.  */
diff --git a/gcc/c-family/c-gimplify.cc b/gcc/c-family/c-gimplify.cc
index ef5c7d9..17b0610 100644
--- a/gcc/c-family/c-gimplify.cc
+++ b/gcc/c-family/c-gimplify.cc
@@ -41,6 +41,8 @@
 #include "c-ubsan.h"
 #include "tree-nested.h"
 #include "context.h"
+#include "tree-pass.h"
+#include "internal-fn.h"
 
 /*  The gimplification pass converts the language-dependent trees
     (ld-trees) emitted by the parser into language-independent trees
@@ -686,6 +688,14 @@
   return bind;
 }
 
+/* Helper for c_gimplify_expr: test if target supports fma-like FN.  */
+
+static bool
+fma_supported_p (enum internal_fn fn, tree type)
+{
+  return direct_internal_fn_supported_p (fn, type, OPTIMIZE_FOR_BOTH);
+}
+
 /* Gimplification of expression trees.  */
 
 /* Do C-specific gimplification on *EXPR_P.  PRE_P and POST_P are as in
@@ -739,6 +749,75 @@
 	break;
       }
 
+    case PLUS_EXPR:
+    case MINUS_EXPR:
+      {
+	tree type = TREE_TYPE (*expr_p);
+	/* For -ffp-contract=on we need to attempt FMA contraction only
+	   during initial gimplification.  Late contraction across statement
+	   boundaries would violate language semantics.  */
+	if (SCALAR_FLOAT_TYPE_P (type)
+	    && flag_fp_contract_mode == FP_CONTRACT_ON
+	    && cfun && !(cfun->curr_properties & PROP_gimple_any)
+	    && fma_supported_p (IFN_FMA, type))
+	  {
+	    bool neg_mul = false, neg_add = code == MINUS_EXPR;
+
+	    tree *op0_p = &TREE_OPERAND (*expr_p, 0);
+	    tree *op1_p = &TREE_OPERAND (*expr_p, 1);
+
+	    /* Look for ±(x * y) ± z, swapping operands if necessary.  */
+	    if (TREE_CODE (*op0_p) == NEGATE_EXPR
+		&& TREE_CODE (TREE_OPERAND (*op0_p, 0)) == MULT_EXPR)
+	      /* '*EXPR_P' is '-(x * y) ± z'.  This is fine.  */;
+	    else if (TREE_CODE (*op0_p) != MULT_EXPR)
+	      {
+		std::swap (op0_p, op1_p);
+		std::swap (neg_mul, neg_add);
+	      }
+	    if (TREE_CODE (*op0_p) == NEGATE_EXPR)
+	      {
+		op0_p = &TREE_OPERAND (*op0_p, 0);
+		neg_mul = !neg_mul;
+	      }
+	    if (TREE_CODE (*op0_p) != MULT_EXPR)
+	      break;
+	    auto_vec<tree, 3> ops (3);
+	    ops.quick_push (TREE_OPERAND (*op0_p, 0));
+	    ops.quick_push (TREE_OPERAND (*op0_p, 1));
+	    ops.quick_push (*op1_p);
+
+	    enum internal_fn ifn = IFN_FMA;
+	    if (neg_mul)
+	      {
+		if (fma_supported_p (IFN_FNMA, type))
+		  ifn = IFN_FNMA;
+		else
+		  ops[0] = build1 (NEGATE_EXPR, type, ops[0]);
+	      }
+	    if (neg_add)
+	      {
+		enum internal_fn ifn2 = ifn == IFN_FMA ? IFN_FMS : IFN_FNMS;
+		if (fma_supported_p (ifn2, type))
+		  ifn = ifn2;
+		else
+		  ops[2] = build1 (NEGATE_EXPR, type, ops[2]);
+	      }
+	    /* Avoid gimplify_arg: it emits all side effects into *PRE_P.  */
+	    for (auto &&op : ops)
+	      if (gimplify_expr (&op, pre_p, post_p, is_gimple_val, fb_rvalue)
+		  == GS_ERROR)
+		return GS_ERROR;
+
+	    gcall *call = gimple_build_call_internal_vec (ifn, ops);
+	    gimple_seq_add_stmt_without_update (pre_p, call);
+	    *expr_p = create_tmp_var (type);
+	    gimple_call_set_lhs (call, *expr_p);
+	    return GS_ALL_DONE;
+	  }
+	break;
+      }
+
     default:;
     }
 
diff --git a/gcc/c-family/c-opts.cc b/gcc/c-family/c-opts.cc
index c68a2a2..af19140 100644
--- a/gcc/c-family/c-opts.cc
+++ b/gcc/c-family/c-opts.cc
@@ -111,6 +111,7 @@
 static void set_std_cxx17 (int);
 static void set_std_cxx20 (int);
 static void set_std_cxx23 (int);
+static void set_std_cxx26 (int);
 static void set_std_c89 (int, int);
 static void set_std_c99 (int);
 static void set_std_c11 (int);
@@ -663,6 +664,12 @@
 	set_std_cxx23 (code == OPT_std_c__23 /* ISO */);
       break;
 
+    case OPT_std_c__26:
+    case OPT_std_gnu__26:
+      if (!preprocessing_asm_p)
+	set_std_cxx26 (code == OPT_std_c__26 /* ISO */);
+      break;
+
     case OPT_std_c90:
     case OPT_std_iso9899_199409:
       if (!preprocessing_asm_p)
@@ -1032,7 +1039,8 @@
 	warn_narrowing = 1;
 
       /* Unless -f{,no-}ext-numeric-literals has been used explicitly,
-	 for -std=c++{11,14,17,20,23} default to -fno-ext-numeric-literals.  */
+	 for -std=c++{11,14,17,20,23,26} default to
+	 -fno-ext-numeric-literals.  */
       if (flag_iso && !OPTION_SET_P (flag_ext_numeric_literals))
 	cpp_opts->ext_numeric_literals = 0;
     }
@@ -1820,6 +1828,24 @@
   lang_hooks.name = "GNU C++23";
 }
 
+/* Set the C++ 2026 standard (without GNU extensions if ISO).  */
+static void
+set_std_cxx26 (int iso)
+{
+  cpp_set_lang (parse_in, iso ? CLK_CXX26: CLK_GNUCXX26);
+  flag_no_gnu_keywords = iso;
+  flag_no_nonansi_builtin = iso;
+  flag_iso = iso;
+  /* C++26 includes the C11 standard library.  */
+  flag_isoc94 = 1;
+  flag_isoc99 = 1;
+  flag_isoc11 = 1;
+  /* C++26 includes coroutines.  */
+  flag_coroutines = true;
+  cxx_dialect = cxx26;
+  lang_hooks.name = "GNU C++26";
+}
+
 /* Args to -d specify what to dump.  Silently ignore
    unrecognized options; they may be aimed at toplev.cc.  */
 static void
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 0930a3c..c516091 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -2403,13 +2403,21 @@
 Conform to the ISO 2020 C++ standard (experimental and incomplete support).
 
 std=c++2b
-C++ ObjC++ Alias(std=c++23)
+C++ ObjC++ Alias(std=c++23) Undocumented
 Conform to the ISO 2023 C++ draft standard (experimental and incomplete support).
 
 std=c++23
-C++ ObjC++ Undocumented
+C++ ObjC++
 Conform to the ISO 2023 C++ draft standard (experimental and incomplete support).
 
+std=c++2c
+C++ ObjC++ Alias(std=c++26)
+Conform to the ISO 2026 C++ draft standard (experimental and incomplete support).
+
+std=c++26
+C++ ObjC++ Undocumented
+Conform to the ISO 2026 C++ draft standard (experimental and incomplete support).
+
 std=c11
 C ObjC
 Conform to the ISO 2011 C standard.
@@ -2489,13 +2497,21 @@
 Conform to the ISO 2020 C++ standard with GNU extensions (experimental and incomplete support).
 
 std=gnu++2b
-C++ ObjC++ Alias(std=gnu++23)
+C++ ObjC++ Alias(std=gnu++23) Undocumented
 Conform to the ISO 2023 C++ draft standard with GNU extensions (experimental and incomplete support).
 
 std=gnu++23
-C++ ObjC++ Undocumented
+C++ ObjC++
 Conform to the ISO 2023 C++ draft standard with GNU extensions (experimental and incomplete support).
 
+std=gnu++2c
+C++ ObjC++ Alias(std=gnu++26)
+Conform to the ISO 2026 C++ draft standard with GNU extensions (experimental and incomplete support).
+
+std=gnu++26
+C++ ObjC++ Undocumented
+Conform to the ISO 2026 C++ draft standard with GNU extensions (experimental and incomplete support).
+
 std=gnu11
 C ObjC
 Conform to the ISO 2011 C standard with GNU extensions.
diff --git a/gcc/color-macros.h b/gcc/color-macros.h
index fcd79d0..9688f92 100644
--- a/gcc/color-macros.h
+++ b/gcc/color-macros.h
@@ -92,6 +92,14 @@
 #define COLOR_FG_MAGENTA	"35"
 #define COLOR_FG_CYAN		"36"
 #define COLOR_FG_WHITE		"37"
+#define COLOR_FG_BRIGHT_BLACK	"90"
+#define COLOR_FG_BRIGHT_RED	"91"
+#define COLOR_FG_BRIGHT_GREEN	"92"
+#define COLOR_FG_BRIGHT_YELLOW	"93"
+#define COLOR_FG_BRIGHT_BLUE	"94"
+#define COLOR_FG_BRIGHT_MAGENTA	"95"
+#define COLOR_FG_BRIGHT_CYAN	"96"
+#define COLOR_FG_BRIGHT_WHITE	"97"
 #define COLOR_BG_BLACK		"40"
 #define COLOR_BG_RED		"41"
 #define COLOR_BG_GREEN		"42"
@@ -100,6 +108,14 @@
 #define COLOR_BG_MAGENTA	"45"
 #define COLOR_BG_CYAN		"46"
 #define COLOR_BG_WHITE		"47"
+#define COLOR_BG_BRIGHT_BLACK	"100"
+#define COLOR_BG_BRIGHT_RED	"101"
+#define COLOR_BG_BRIGHT_GREEN	"102"
+#define COLOR_BG_BRIGHT_YELLOW	"103"
+#define COLOR_BG_BRIGHT_BLUE	"104"
+#define COLOR_BG_BRIGHT_MAGENTA	"105"
+#define COLOR_BG_BRIGHT_CYAN	"106"
+#define COLOR_BG_BRIGHT_WHITE	"107"
 #define SGR_START		"\33["
 #define SGR_END			"m\33[K"
 #define SGR_SEQ(str)		SGR_START str SGR_END
diff --git a/gcc/common.opt b/gcc/common.opt
index a28ca13..25f650e 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1502,6 +1502,29 @@
 Common Var(flag_diagnostics_show_path_depths) Init(0)
 Show stack depths of events in paths.
 
+fdiagnostics-text-art-charset=
+Driver Common Joined RejectNegative Var(flag_diagnostics_text_art_charset) Enum(diagnostic_text_art_charset) Init(DIAGNOSTICS_TEXT_ART_CHARSET_EMOJI)
+-fdiagnostics-text-art-charset=[none|ascii|unicode|emoji]	Determine which characters to use in text arg diagrams.
+
+; Required for these enum values.
+SourceInclude
+diagnostic-text-art.h
+
+Enum
+Name(diagnostic_text_art_charset) Type(int)
+
+EnumValue
+Enum(diagnostic_text_art_charset) String(none) Value(DIAGNOSTICS_TEXT_ART_CHARSET_NONE)
+
+EnumValue
+Enum(diagnostic_text_art_charset) String(ascii) Value(DIAGNOSTICS_TEXT_ART_CHARSET_ASCII)
+
+EnumValue
+Enum(diagnostic_text_art_charset) String(unicode) Value(DIAGNOSTICS_TEXT_ART_CHARSET_UNICODE)
+
+EnumValue
+Enum(diagnostic_text_art_charset) String(emoji) Value(DIAGNOSTICS_TEXT_ART_CHARSET_EMOJI)
+
 fdiagnostics-minimum-margin-width=
 Common Joined UInteger Var(diagnostics_minimum_margin_width) Init(6)
 Set minimum width of left margin of source code when showing source.
@@ -1662,9 +1685,8 @@
 EnumValue
 Enum(fp_contract_mode) String(off) Value(FP_CONTRACT_OFF)
 
-; Not implemented, fall back to conservative FP_CONTRACT_OFF.
 EnumValue
-Enum(fp_contract_mode) String(on) Value(FP_CONTRACT_OFF)
+Enum(fp_contract_mode) String(on) Value(FP_CONTRACT_ON)
 
 EnumValue
 Enum(fp_contract_mode) String(fast) Value(FP_CONTRACT_FAST)
diff --git a/gcc/config/aarch64/aarch64-protos.h b/gcc/config/aarch64/aarch64-protos.h
index a20a20c..70303d6 100644
--- a/gcc/config/aarch64/aarch64-protos.h
+++ b/gcc/config/aarch64/aarch64-protos.h
@@ -759,7 +759,7 @@
 bool aarch64_const_vec_all_same_in_range_p (rtx, HOST_WIDE_INT,
 					    HOST_WIDE_INT);
 bool aarch64_const_vec_rnd_cst_p (rtx, rtx);
-bool aarch64_const_vec_rsra_rnd_imm_p (rtx);
+bool aarch64_rnd_imm_p (rtx);
 bool aarch64_constant_address_p (rtx);
 bool aarch64_emit_approx_div (rtx, rtx, rtx);
 bool aarch64_emit_approx_sqrt (rtx, rtx, bool);
diff --git a/gcc/config/aarch64/aarch64-simd.md b/gcc/config/aarch64/aarch64-simd.md
index 90118c6..4052ca9 100644
--- a/gcc/config/aarch64/aarch64-simd.md
+++ b/gcc/config/aarch64/aarch64-simd.md
@@ -1323,7 +1323,7 @@
 	      (plus:<V2XWIDE>
 		(<SHIFTEXTEND>:<V2XWIDE>
 		  (match_operand:VSDQ_I_DI 2 "register_operand" "w"))
-		(match_operand:<V2XWIDE> 4 "aarch64_simd_rsra_rnd_imm_vec"))
+		(match_operand:<V2XWIDE> 4 "aarch64_int_rnd_operand"))
 	      (match_operand:VSDQ_I_DI 3 "aarch64_simd_shift_imm_<vec_or_offset>_<Vel>")))
 	  (match_operand:VSDQ_I_DI 1 "register_operand" "0")))]
   "TARGET_SIMD
@@ -6437,7 +6437,7 @@
 	    (plus:<V2XWIDE>
 	      (<SHIFTEXTEND>:<V2XWIDE>
 		(match_operand:VSDQ_I_DI 1 "register_operand" "w"))
-	      (match_operand:<V2XWIDE> 3 "aarch64_simd_rsra_rnd_imm_vec"))
+	      (match_operand:<V2XWIDE> 3 "aarch64_int_rnd_operand"))
 	    (match_operand:VSDQ_I_DI 2 "aarch64_simd_shift_imm_<vec_or_offset>_<Vel>"))))]
   "TARGET_SIMD
    && aarch64_const_vec_rnd_cst_p (operands[3], operands[2])"
@@ -6557,7 +6557,7 @@
 	    (plus:<V2XWIDE>
 	      (<TRUNCEXTEND>:<V2XWIDE>
 	        (match_operand:VQN 1 "register_operand" "w"))
-	      (match_operand:<V2XWIDE> 3 "aarch64_simd_rsra_rnd_imm_vec"))
+	      (match_operand:<V2XWIDE> 3 "aarch64_int_rnd_operand"))
 	    (match_operand:VQN 2 "aarch64_simd_shift_imm_vec_<vn_mode>"))))]
   "TARGET_SIMD
    && aarch64_const_vec_rnd_cst_p (operands[3], operands[2])"
@@ -6572,7 +6572,7 @@
 	    (plus:<DWI>
 	      (<TRUNCEXTEND>:<DWI>
 	        (match_operand:SD_HSDI 1 "register_operand" "w"))
-	      (match_operand:<DWI> 3 "aarch64_simd_rsra_rnd_imm_vec"))
+	      (match_operand:<DWI> 3 "aarch64_int_rnd_operand"))
 	    (match_operand:SI 2 "aarch64_simd_shift_imm_offset_<ve_mode>"))))]
   "TARGET_SIMD
    && aarch64_const_vec_rnd_cst_p (operands[3], operands[2])"
@@ -6702,7 +6702,7 @@
 		(plus:<V2XWIDE>
 		  (sign_extend:<V2XWIDE>
 		    (match_operand:VQN 1 "register_operand" "w"))
-		  (match_operand:<V2XWIDE> 3 "aarch64_simd_rsra_rnd_imm_vec"))
+		  (match_operand:<V2XWIDE> 3 "aarch64_int_rnd_operand"))
 		(match_operand:VQN 2 "aarch64_simd_shift_imm_vec_<vn_mode>"))
 	      (match_operand:<V2XWIDE> 4 "aarch64_simd_imm_zero"))
 	    (match_operand:<V2XWIDE> 5 "aarch64_simd_umax_quarter_mode"))))]
@@ -6713,14 +6713,14 @@
 )
 
 (define_insn "aarch64_sqrshrun_n<mode>_insn"
-  [(set (match_operand:<V2XWIDE> 0 "register_operand" "=w")
-	(smin:<V2XWIDE>
-	  (smax:<V2XWIDE>
-	    (ashiftrt:<V2XWIDE>
-	      (plus:<V2XWIDE>
-		(sign_extend:<V2XWIDE>
+  [(set (match_operand:<DWI> 0 "register_operand" "=w")
+	(smin:<DWI>
+	  (smax:<DWI>
+	    (ashiftrt:<DWI>
+	      (plus:<DWI>
+		(sign_extend:<DWI>
 		  (match_operand:SD_HSDI 1 "register_operand" "w"))
-		(match_operand:<V2XWIDE> 3 "aarch64_simd_rsra_rnd_imm_vec"))
+		(match_operand:<DWI> 3 "aarch64_int_rnd_operand"))
 	      (match_operand:SI 2 "aarch64_simd_shift_imm_offset_<ve_mode>"))
 	    (const_int 0))
 	  (const_int <half_mask>)))]
@@ -6736,10 +6736,10 @@
    (match_operand:SI 2 "aarch64_simd_shift_imm_offset_<ve_mode>")]
   "TARGET_SIMD"
   {
-    int prec = GET_MODE_UNIT_PRECISION (<V2XWIDE>mode);
+    int prec = GET_MODE_UNIT_PRECISION (<DWI>mode);
     wide_int rnd_wi = wi::set_bit_in_zero (INTVAL (operands[2]) - 1, prec);
-    rtx rnd = immed_wide_int_const (rnd_wi, <V2XWIDE>mode);
-    rtx dst = gen_reg_rtx (<V2XWIDE>mode);
+    rtx rnd = immed_wide_int_const (rnd_wi, <DWI>mode);
+    rtx dst = gen_reg_rtx (<DWI>mode);
     emit_insn (gen_aarch64_sqrshrun_n<mode>_insn (dst, operands[1], operands[2], rnd));
     emit_move_insn (operands[0], gen_lowpart (<VNARROWQ>mode, dst));
     DONE;
@@ -6831,7 +6831,7 @@
 	      (plus:<V2XWIDE>
 		(<TRUNCEXTEND>:<V2XWIDE>
 		  (match_operand:VQN 2 "register_operand" "w"))
-		(match_operand:<V2XWIDE> 4 "aarch64_simd_rsra_rnd_imm_vec"))
+		(match_operand:<V2XWIDE> 4 "aarch64_int_rnd_operand"))
 	      (match_operand:VQN 3 "aarch64_simd_shift_imm_vec_<vn_mode>")))))]
   "TARGET_SIMD && !BYTES_BIG_ENDIAN
    && aarch64_const_vec_rnd_cst_p (operands[4], operands[3])"
@@ -6847,7 +6847,7 @@
 	      (plus:<V2XWIDE>
 		(<TRUNCEXTEND>:<V2XWIDE>
 		  (match_operand:VQN 2 "register_operand" "w"))
-		(match_operand:<V2XWIDE> 4 "aarch64_simd_rsra_rnd_imm_vec"))
+		(match_operand:<V2XWIDE> 4 "aarch64_int_rnd_operand"))
 	      (match_operand:VQN 3 "aarch64_simd_shift_imm_vec_<vn_mode>")))
 	  (match_operand:<VNARROWQ> 1 "register_operand" "0")))]
   "TARGET_SIMD && BYTES_BIG_ENDIAN
@@ -6965,7 +6965,7 @@
 		  (plus:<V2XWIDE>
 		    (sign_extend:<V2XWIDE>
 		      (match_operand:VQN 2 "register_operand" "w"))
-		    (match_operand:<V2XWIDE> 4 "aarch64_simd_rsra_rnd_imm_vec"))
+		    (match_operand:<V2XWIDE> 4 "aarch64_int_rnd_operand"))
 		  (match_operand:VQN 3 "aarch64_simd_shift_imm_vec_<vn_mode>"))
 		(match_operand:<V2XWIDE> 5 "aarch64_simd_imm_zero"))
 	      (match_operand:<V2XWIDE> 6 "aarch64_simd_umax_quarter_mode")))))]
@@ -6985,7 +6985,7 @@
 		  (plus:<V2XWIDE>
 		    (sign_extend:<V2XWIDE>
 		      (match_operand:VQN 2 "register_operand" "w"))
-		    (match_operand:<V2XWIDE> 4 "aarch64_simd_rsra_rnd_imm_vec"))
+		    (match_operand:<V2XWIDE> 4 "aarch64_int_rnd_operand"))
 		  (match_operand:VQN 3 "aarch64_simd_shift_imm_vec_<vn_mode>"))
 		(match_operand:<V2XWIDE> 5 "aarch64_simd_imm_zero"))
 	      (match_operand:<V2XWIDE> 6 "aarch64_simd_umax_quarter_mode")))
diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc
index b99f12c..560e543 100644
--- a/gcc/config/aarch64/aarch64.cc
+++ b/gcc/config/aarch64/aarch64.cc
@@ -1929,7 +1929,7 @@
   "32:16",	/* loop_align.  */
   2,	/* int_reassoc_width.  */
   4,	/* fp_reassoc_width.  */
-  1,	/* fma_reassoc_width.  */
+  4,	/* fma_reassoc_width.  */
   2,	/* vec_reassoc_width.  */
   2,	/* min_div_recip_mul_sf.  */
   2,	/* min_div_recip_mul_df.  */
@@ -11761,14 +11761,14 @@
   return true;
 }
 
-/* Return true if X is a TImode constant or a constant vector of integer
-   immediates that represent the rounding constant used in the RSRA
-   instructions.
-   The accepted form of the constant is (1 << (C - 1)) where C is within
+/* Return true if X is a scalar or a constant vector of integer
+   immediates that represent the rounding constant used in the fixed-point
+   arithmetic instructions.
+   The accepted form of the constant is (1 << (C - 1)) where C is in the range
    [1, MODE_WIDTH/2].  */
 
 bool
-aarch64_const_vec_rsra_rnd_imm_p (rtx x)
+aarch64_rnd_imm_p (rtx x)
 {
   wide_int rnd_cst;
   if (!aarch64_extract_vec_duplicate_wide_int (x, &rnd_cst))
diff --git a/gcc/config/aarch64/predicates.md b/gcc/config/aarch64/predicates.md
index b31ba6e..d5a4a1c 100644
--- a/gcc/config/aarch64/predicates.md
+++ b/gcc/config/aarch64/predicates.md
@@ -626,15 +626,11 @@
   (and (match_code "const_vector")
        (match_test "aarch64_const_vec_all_same_in_range_p (op, 1, 64)")))
 
-(define_predicate "aarch64_simd_rsra_rnd_imm_vec"
+;; A constant or vector of constants that represents an integer rounding
+;; constant added during fixed-point arithmetic calculations
+(define_predicate "aarch64_int_rnd_operand"
   (and (match_code "const_vector,const_int,const_wide_int")
-       (match_test "aarch64_const_vec_rsra_rnd_imm_p (op)")))
-
-(define_predicate "aarch64_simd_rshrn_imm_vec"
-  (and (match_code "const_vector")
-       (match_test "aarch64_const_vec_all_same_in_range_p (op, 1,
-				HOST_WIDE_INT_1U
-				<< (GET_MODE_UNIT_BITSIZE  (mode) - 1))")))
+       (match_test "aarch64_rnd_imm_p (op)")))
 
 (define_predicate "aarch64_simd_raddsubhn_imm_vec"
   (and (match_code "const_vector")
diff --git a/gcc/config/i386/i386-expand.cc b/gcc/config/i386/i386-expand.cc
index 7bb4d39..9a8d244 100644
--- a/gcc/config/i386/i386-expand.cc
+++ b/gcc/config/i386/i386-expand.cc
@@ -10234,6 +10234,18 @@
   machine_mode mode1 = insn_data[d->icode].operand[1].mode;
   enum rtx_code comparison = d->comparison;
 
+  /* ptest reg, reg sets the carry flag.  */
+  if (comparison == LTU
+      && (d->code == IX86_BUILTIN_PTESTC
+	  || d->code == IX86_BUILTIN_PTESTC256)
+      && rtx_equal_p (op0, op1))
+    {
+      if (!target)
+	target = gen_reg_rtx (SImode);
+      emit_move_insn (target, const1_rtx);
+      return target;
+    }
+
   if (VECTOR_MODE_P (mode0))
     op0 = safe_vector_operand (op0, mode0);
   if (VECTOR_MODE_P (mode1))
diff --git a/gcc/config/i386/i386-options.cc b/gcc/config/i386/i386-options.cc
index 2cb0bdd..7f593ce 100644
--- a/gcc/config/i386/i386-options.cc
+++ b/gcc/config/i386/i386-options.cc
@@ -1400,7 +1400,11 @@
       if (option_strings[IX86_FUNCTION_SPECIFIC_TUNE])
 	opts->x_ix86_tune_string
 	  = ggc_strdup (option_strings[IX86_FUNCTION_SPECIFIC_TUNE]);
-      else if (orig_tune_defaulted)
+      /* If we have explicit arch string and no tune string specified, set
+	 tune_string to NULL and later it will be overriden by arch_string
+	 so target clones can get proper optimization.  */
+      else if (option_strings[IX86_FUNCTION_SPECIFIC_ARCH]
+	       || orig_tune_defaulted)
 	opts->x_ix86_tune_string = NULL;
 
       /* If fpmath= is not set, and we now have sse2 on 32-bit, use it.  */
diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc
index 32851a5..0761965 100644
--- a/gcc/config/i386/i386.cc
+++ b/gcc/config/i386/i386.cc
@@ -21423,16 +21423,23 @@
       else if (XINT (x, 1) == UNSPEC_PTEST)
 	{
 	  *total = cost->sse_op;
-	  if (XVECLEN (x, 0) == 2
-	      && GET_CODE (XVECEXP (x, 0, 0)) == AND)
+	  rtx test_op0 = XVECEXP (x, 0, 0);
+	  if (!rtx_equal_p (test_op0, XVECEXP (x, 0, 1)))
+	    return false;
+	  if (GET_CODE (test_op0) == AND)
 	    {
-	      rtx andop = XVECEXP (x, 0, 0);
-	      *total += rtx_cost (XEXP (andop, 0), GET_MODE (andop),
-				  AND, opno, speed)
-			+ rtx_cost (XEXP (andop, 1), GET_MODE (andop),
-				    AND, opno, speed);
-	      return true;
+	      rtx and_op0 = XEXP (test_op0, 0);
+	      if (GET_CODE (and_op0) == NOT)
+		and_op0 = XEXP (and_op0, 0);
+	      *total += rtx_cost (and_op0, GET_MODE (and_op0),
+				  AND, 0, speed)
+			+ rtx_cost (XEXP (test_op0, 1), GET_MODE (and_op0),
+				    AND, 1, speed);
 	    }
+	  else
+	    *total = rtx_cost (test_op0, GET_MODE (test_op0),
+			       UNSPEC, 0, speed);
+	  return true;
 	}
       return false;
 
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index 95a6653c..15c0310 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -11380,6 +11380,8 @@
   [(set_attr "type" "alu")
    (set_attr "mode" "QI")])
 
+;; *andqi_ext<mode>_3 is defined via *<code>qi_ext<mode>_3 below.
+
 ;; Convert wide AND instructions with immediate operand to shorter QImode
 ;; equivalents when possible.
 ;; Don't do the splitting with memory operands, since it introduces risk
@@ -12092,6 +12094,26 @@
   [(set_attr "type" "alu")
    (set_attr "mode" "QI")])
 
+(define_insn "*<code>qi_ext<mode>_3"
+  [(set (zero_extract:SWI248
+	  (match_operand 0 "int248_register_operand" "+Q")
+	  (const_int 8)
+	  (const_int 8))
+	(zero_extract:SWI248
+	  (any_logic:SWI248
+	    (match_operand 1 "int248_register_operand" "%0")
+	    (match_operand 2 "int248_register_operand" "Q"))
+	  (const_int 8)
+	  (const_int 8)))
+   (clobber (reg:CC FLAGS_REG))]
+  "(!TARGET_PARTIAL_REG_STALL || optimize_function_for_size_p (cfun))
+   /* FIXME: without this LRA can't reload this pattern, see PR82524.  */
+   && (rtx_equal_p (operands[0], operands[1])
+       || rtx_equal_p (operands[0], operands[2]))"
+  "<logic>{b}\t{%h2, %h0|%h0, %h2}"
+  [(set_attr "type" "alu")
+   (set_attr "mode" "QI")])
+
 ;; Convert wide OR instructions with immediate operand to shorter QImode
 ;; equivalents when possible.
 ;; Don't do the splitting with memory operands, since it introduces risk
@@ -12206,6 +12228,18 @@
    (set_attr "type" "alu")
    (set_attr "mode" "QI")])
 
+;; Peephole2 rega = 0; rega op= regb into rega = regb.
+(define_peephole2
+  [(parallel [(set (match_operand:SWI 0 "general_reg_operand")
+		   (const_int 0))
+	      (clobber (reg:CC FLAGS_REG))])
+   (parallel [(set (match_dup 0)
+		   (any_or_plus:SWI (match_dup 0)
+				    (match_operand:SWI 1 "<general_operand>")))
+	      (clobber (reg:CC FLAGS_REG))])]
+  ""
+  [(set (match_dup 0) (match_dup 1))])
+		
 ;; Split DST = (HI<<32)|LO early to minimize register usage.
 (define_insn_and_split "*concat<mode><dwi>3_1"
   [(set (match_operand:<DWI> 0 "nonimmediate_operand" "=ro,r")
@@ -13365,6 +13399,28 @@
   [(const_int 0)]
   "ix86_split_ashl (operands, operands[3], <DWI>mode); DONE;")
 
+(define_insn_and_split "*ashl<dwi>3_doubleword_highpart"
+  [(set (match_operand:<DWI> 0 "register_operand" "=r")
+	(ashift:<DWI>
+	  (any_extend:<DWI> (match_operand:DWIH 1 "nonimmediate_operand" "rm"))
+	  (match_operand:QI 2 "const_int_operand")))
+   (clobber (reg:CC FLAGS_REG))]
+  "INTVAL (operands[2]) >= <MODE_SIZE> * BITS_PER_UNIT
+   && INTVAL (operands[2]) < <MODE_SIZE> * BITS_PER_UNIT * 2"
+  "#"
+  "&& reload_completed"
+  [(const_int 0)]
+{
+  split_double_mode (<DWI>mode, &operands[0], 1, &operands[0], &operands[3]);
+  int bits = INTVAL (operands[2]) - (<MODE_SIZE> * BITS_PER_UNIT);
+  if (!rtx_equal_p (operands[3], operands[1]))
+    emit_move_insn (operands[3], operands[1]);
+  if (bits > 0)
+    emit_insn (gen_ashl<mode>3 (operands[3], operands[3], GEN_INT (bits)));
+  ix86_expand_clear (operands[0]);
+  DONE;
+})
+
 (define_insn "x86_64_shld"
   [(set (match_operand:DI 0 "nonimmediate_operand" "+r*m")
         (ior:DI (ashift:DI (match_dup 0)
diff --git a/gcc/config/i386/sse.md b/gcc/config/i386/sse.md
index f793258..3b50c71 100644
--- a/gcc/config/i386/sse.md
+++ b/gcc/config/i386/sse.md
@@ -1465,12 +1465,12 @@
 })
 
 (define_insn "*<avx512>_load<mode>_mask"
-  [(set (match_operand:VI12_AVX512VL 0 "register_operand" "=v")
-	(vec_merge:VI12_AVX512VL
-	  (unspec:VI12_AVX512VL
-	    [(match_operand:VI12_AVX512VL 1 "memory_operand" "m")]
+  [(set (match_operand:VI12HFBF_AVX512VL 0 "register_operand" "=v")
+	(vec_merge:VI12HFBF_AVX512VL
+	  (unspec:VI12HFBF_AVX512VL
+	    [(match_operand:VI12HFBF_AVX512VL 1 "memory_operand" "m")]
 	    UNSPEC_MASKLOAD)
-	  (match_operand:VI12_AVX512VL 2 "nonimm_or_0_operand" "0C")
+	  (match_operand:VI12HFBF_AVX512VL 2 "nonimm_or_0_operand" "0C")
 	  (match_operand:<avx512fmaskmode> 3 "register_operand" "Yk")))]
   "TARGET_AVX512BW"
   "vmovdqu<ssescalarsize>\t{%1, %0%{%3%}%N2|%0%{%3%}%N2, %1}"
@@ -1479,9 +1479,9 @@
    (set_attr "mode" "<sseinsnmode>")])
 
 (define_insn_and_split "*<avx512>_load<mode>"
-  [(set (match_operand:VI12_AVX512VL 0 "register_operand" "=v")
-	(unspec:VI12_AVX512VL
-	  [(match_operand:VI12_AVX512VL 1 "memory_operand" "m")]
+  [(set (match_operand:VI12HFBF_AVX512VL 0 "register_operand" "=v")
+	(unspec:VI12HFBF_AVX512VL
+	  [(match_operand:VI12HFBF_AVX512VL 1 "memory_operand" "m")]
 	  UNSPEC_MASKLOAD))]
   "TARGET_AVX512BW"
   "#"
@@ -23490,6 +23490,70 @@
   [(set (reg:CCZ FLAGS_REG)
 	(unspec:CCZ [(match_dup 0) (match_dup 1)] UNSPEC_PTEST))])
 
+;; ptest reg,reg sets the carry flag.
+(define_split
+  [(set (reg:CCC FLAGS_REG)
+	(unspec:CCC [(match_operand:V_AVX 0 "register_operand")
+		     (match_operand:V_AVX 1 "register_operand")]
+		    UNSPEC_PTEST))]
+  "TARGET_SSE4_1
+   && rtx_equal_p (operands[0], operands[1])"
+  [(set (reg:CCC FLAGS_REG)
+	(unspec:CCC [(const_int 0)] UNSPEC_STC))])
+
+;; Changing the CCmode of FLAGS_REG requires updating both def and use.
+;; pandn/ptestz/set{n?}e -> ptestc/set{n?}c
+(define_split
+  [(set (match_operand:SWI 0 "register_operand")
+	(match_operator:SWI 3 "bt_comparison_operator"
+	  [(unspec:CCZ [
+	     (and:V_AVX (not:V_AVX (match_operand:V_AVX 1 "register_operand"))
+			(match_operand:V_AVX 2 "register_operand"))
+	     (and:V_AVX (not:V_AVX (match_dup 1)) (match_dup 2))]
+	     UNSPEC_PTEST)
+	   (const_int 0)]))]
+  "TARGET_SSE4_1"
+  [(set (reg:CCC FLAGS_REG)
+	(unspec:CCC [(match_dup 1) (match_dup 2)] UNSPEC_PTEST))
+   (set (match_dup 0)
+	(match_op_dup 3 [(reg:CCC FLAGS_REG) (const_int 0)]))])
+
+(define_split
+  [(set (strict_low_part (match_operand:QI 0 "register_operand"))
+	(match_operator:QI 3 "bt_comparison_operator"
+	  [(unspec:CCZ [
+	     (and:V_AVX (not:V_AVX (match_operand:V_AVX 1 "register_operand"))
+			(match_operand:V_AVX 2 "register_operand"))
+	     (and:V_AVX (not:V_AVX (match_dup 1)) (match_dup 2))]
+	     UNSPEC_PTEST)
+	   (const_int 0)]))]
+  "TARGET_SSE4_1"
+  [(set (reg:CCC FLAGS_REG)
+	(unspec:CCC [(match_dup 1) (match_dup 2)] UNSPEC_PTEST))
+   (set (strict_low_part (match_dup 0))
+	(match_op_dup 3 [(reg:CCC FLAGS_REG) (const_int 0)]))])
+
+;; pandn/ptestz/j{n?}e -> ptestc/j{n?}c
+(define_split
+  [(set (pc)
+	(if_then_else
+	  (match_operator 3 "bt_comparison_operator"
+	    [(unspec:CCZ [
+	       (and:V_AVX
+		 (not:V_AVX (match_operand:V_AVX 1 "register_operand"))
+		 (match_operand:V_AVX 2 "register_operand"))
+	       (and:V_AVX (not:V_AVX (match_dup 1)) (match_dup 2))]
+	       UNSPEC_PTEST)
+	     (const_int 0)])
+	  (match_operand 0)
+	  (pc)))]
+  "TARGET_SSE4_1"
+  [(set (reg:CCC FLAGS_REG)
+	(unspec:CCC [(match_dup 1) (match_dup 2)] UNSPEC_PTEST))
+   (set (pc) (if_then_else (match_op_dup 3 [(reg:CCC FLAGS_REG) (const_int 0)])
+			   (match_dup 0)
+			   (pc)))])
+
 (define_expand "nearbyint<mode>2"
   [(set (match_operand:VFH 0 "register_operand")
 	(unspec:VFH
@@ -26915,17 +26979,21 @@
   "TARGET_AVX")
 
 (define_expand "maskload<mode><avx512fmaskmodelower>"
-  [(set (match_operand:V48H_AVX512VL 0 "register_operand")
-	(vec_merge:V48H_AVX512VL
-	  (match_operand:V48H_AVX512VL 1 "memory_operand")
+  [(set (match_operand:V48_AVX512VL 0 "register_operand")
+	(vec_merge:V48_AVX512VL
+	  (unspec:V48_AVX512VL
+	    [(match_operand:V48_AVX512VL 1 "memory_operand")]
+	    UNSPEC_MASKLOAD)
 	  (match_dup 0)
 	  (match_operand:<avx512fmaskmode> 2 "register_operand")))]
   "TARGET_AVX512F")
 
 (define_expand "maskload<mode><avx512fmaskmodelower>"
-  [(set (match_operand:VI12_AVX512VL 0 "register_operand")
-	(vec_merge:VI12_AVX512VL
-	  (match_operand:VI12_AVX512VL 1 "memory_operand")
+  [(set (match_operand:VI12HFBF_AVX512VL 0 "register_operand")
+	(vec_merge:VI12HFBF_AVX512VL
+	  (unspec:VI12HFBF_AVX512VL
+	    [(match_operand:VI12HFBF_AVX512VL 1 "memory_operand")]
+	    UNSPEC_MASKLOAD)
 	  (match_dup 0)
 	  (match_operand:<avx512fmaskmode> 2 "register_operand")))]
   "TARGET_AVX512BW")
diff --git a/gcc/config/riscv/autovec.md b/gcc/config/riscv/autovec.md
index f1641d7..19100b5 100644
--- a/gcc/config/riscv/autovec.md
+++ b/gcc/config/riscv/autovec.md
@@ -22,29 +22,27 @@
 ;; == Loads/Stores
 ;; =========================================================================
 
-;; len_load/len_store is a sub-optimal pattern for RVV auto-vectorization support.
-;; We will replace them when len_maskload/len_maskstore is supported in loop vectorizer.
-(define_expand "len_load_<mode>"
+(define_expand "len_maskload<mode><vm>"
   [(match_operand:V 0 "register_operand")
    (match_operand:V 1 "memory_operand")
-   (match_operand 2 "vector_length_operand")
-   (match_operand 3 "const_0_operand")]
+   (match_operand 2 "autovec_length_operand")
+   (match_operand:<VM> 3 "vector_mask_operand")
+   (match_operand 4 "const_0_operand")]
   "TARGET_VECTOR"
 {
-  riscv_vector::emit_nonvlmax_insn (code_for_pred_mov (<MODE>mode),
-  				    riscv_vector::RVV_UNOP, operands, operands[2]);
+  riscv_vector::expand_load_store (operands, true);
   DONE;
 })
 
-(define_expand "len_store_<mode>"
+(define_expand "len_maskstore<mode><vm>"
   [(match_operand:V 0 "memory_operand")
    (match_operand:V 1 "register_operand")
-   (match_operand 2 "vector_length_operand")
-   (match_operand 3 "const_0_operand")]
+   (match_operand 2 "autovec_length_operand")
+   (match_operand:<VM> 3 "vector_mask_operand")
+   (match_operand 4 "const_0_operand")]
   "TARGET_VECTOR"
 {
-  riscv_vector::emit_nonvlmax_insn (code_for_pred_mov (<MODE>mode),
-  				    riscv_vector::RVV_UNOP, operands, operands[2]);
+  riscv_vector::expand_load_store (operands, false);
   DONE;
 })
 
@@ -314,44 +312,6 @@
 )
 
 ;; -------------------------------------------------------------------------
-;; ---- [INT,FP] Compare and select
-;; -------------------------------------------------------------------------
-;; The patterns in this section are synthetic.
-;; -------------------------------------------------------------------------
-
-(define_expand "vcond<V:mode><VI:mode>"
-  [(set (match_operand:V 0 "register_operand")
-	(if_then_else:V
-	  (match_operator 3 "comparison_operator"
-	    [(match_operand:VI 4 "register_operand")
-	     (match_operand:VI 5 "register_operand")])
-	  (match_operand:V 1 "register_operand")
-	  (match_operand:V 2 "register_operand")))]
-  "TARGET_VECTOR && known_eq (GET_MODE_NUNITS (<V:MODE>mode),
-  		GET_MODE_NUNITS (<VI:MODE>mode))"
-  {
-    riscv_vector::expand_vcond (operands);
-    DONE;
-  }
-)
-
-(define_expand "vcondu<V:mode><VI:mode>"
-  [(set (match_operand:V 0 "register_operand")
-	(if_then_else:V
-	  (match_operator 3 "comparison_operator"
-	    [(match_operand:VI 4 "register_operand")
-	     (match_operand:VI 5 "register_operand")])
-	  (match_operand:V 1 "register_operand")
-	  (match_operand:V 2 "register_operand")))]
-  "TARGET_VECTOR && known_eq (GET_MODE_NUNITS (<V:MODE>mode),
-  		GET_MODE_NUNITS (<VI:MODE>mode))"
-  {
-    riscv_vector::expand_vcond (operands);
-    DONE;
-  }
-)
-
-;; -------------------------------------------------------------------------
 ;; ---- [INT] Sign and zero extension
 ;; -------------------------------------------------------------------------
 ;; Includes:
@@ -596,40 +556,41 @@
 ;;    result after reload_completed.
 (define_expand "fma<mode>4"
   [(parallel
-    [(set (match_operand:VI 0 "register_operand"     "=vr")
+    [(set (match_operand:VI 0 "register_operand")
 	  (plus:VI
 	    (mult:VI
-	      (match_operand:VI 1 "register_operand" " vr")
-	      (match_operand:VI 2 "register_operand" " vr"))
-	    (match_operand:VI 3 "register_operand"   " vr")))
-     (clobber (match_scratch:SI 4))])]
+	      (match_operand:VI 1 "register_operand")
+	      (match_operand:VI 2 "register_operand"))
+	    (match_operand:VI 3 "register_operand")))
+     (clobber (match_dup 4))])]
   "TARGET_VECTOR"
-  {})
+  {
+    operands[4] = gen_reg_rtx (Pmode);
+  })
 
-(define_insn_and_split "*fma<mode>"
+(define_insn_and_split "*fma<VI:mode><P:mode>"
   [(set (match_operand:VI 0 "register_operand"     "=vr, vr, ?&vr")
 	(plus:VI
 	  (mult:VI
 	    (match_operand:VI 1 "register_operand" " %0, vr,   vr")
 	    (match_operand:VI 2 "register_operand" " vr, vr,   vr"))
 	  (match_operand:VI 3 "register_operand"   " vr,  0,   vr")))
-   (clobber (match_scratch:SI 4 "=r,r,r"))]
+   (clobber (match_operand:P 4 "register_operand" "=r,r,r"))]
   "TARGET_VECTOR"
   "#"
   "&& reload_completed"
   [(const_int 0)]
   {
-    PUT_MODE (operands[4], Pmode);
-    riscv_vector::emit_vlmax_vsetvl (<MODE>mode, operands[4]);
+    riscv_vector::emit_vlmax_vsetvl (<VI:MODE>mode, operands[4]);
     if (which_alternative == 2)
       emit_insn (gen_rtx_SET (operands[0], operands[3]));
     rtx ops[] = {operands[0], operands[1], operands[2], operands[3], operands[0]};
-    riscv_vector::emit_vlmax_ternary_insn (code_for_pred_mul_plus (<MODE>mode),
-					  riscv_vector::RVV_TERNOP, ops, operands[4]);
+    riscv_vector::emit_vlmax_ternary_insn (code_for_pred_mul_plus (<VI:MODE>mode),
+					   riscv_vector::RVV_TERNOP, ops, operands[4]);
     DONE;
   }
   [(set_attr "type" "vimuladd")
-   (set_attr "mode" "<MODE>")])
+   (set_attr "mode" "<VI:MODE>")])
 
 ;; -------------------------------------------------------------------------
 ;; ---- [INT] VNMSAC and VNMSUB
@@ -641,40 +602,225 @@
 
 (define_expand "fnma<mode>4"
   [(parallel
-    [(set (match_operand:VI 0 "register_operand"     "=vr")
+    [(set (match_operand:VI 0 "register_operand")
    (minus:VI
-     (match_operand:VI 3 "register_operand"   " vr")
+     (match_operand:VI 3 "register_operand")
      (mult:VI
-       (match_operand:VI 1 "register_operand" " vr")
-       (match_operand:VI 2 "register_operand" " vr"))))
-     (clobber (match_scratch:SI 4))])]
+       (match_operand:VI 1 "register_operand")
+       (match_operand:VI 2 "register_operand"))))
+     (clobber (match_dup 4))])]
   "TARGET_VECTOR"
-  {})
+  {
+    operands[4] = gen_reg_rtx (Pmode);
+  })
 
-(define_insn_and_split "*fnma<mode>"
+(define_insn_and_split "*fnma<VI:mode><P:mode>"
   [(set (match_operand:VI 0 "register_operand"     "=vr, vr, ?&vr")
  (minus:VI
    (match_operand:VI 3 "register_operand"   " vr,  0,   vr")
    (mult:VI
      (match_operand:VI 1 "register_operand" " %0, vr,   vr")
      (match_operand:VI 2 "register_operand" " vr, vr,   vr"))))
-   (clobber (match_scratch:SI 4 "=r,r,r"))]
+   (clobber (match_operand:P 4 "register_operand" "=r,r,r"))]
   "TARGET_VECTOR"
   "#"
   "&& reload_completed"
   [(const_int 0)]
   {
-    PUT_MODE (operands[4], Pmode);
-    riscv_vector::emit_vlmax_vsetvl (<MODE>mode, operands[4]);
+    riscv_vector::emit_vlmax_vsetvl (<VI:MODE>mode, operands[4]);
     if (which_alternative == 2)
       emit_insn (gen_rtx_SET (operands[0], operands[3]));
     rtx ops[] = {operands[0], operands[1], operands[2], operands[3], operands[0]};
-    riscv_vector::emit_vlmax_ternary_insn (code_for_pred_minus_mul (<MODE>mode),
-    riscv_vector::RVV_TERNOP, ops, operands[4]);
+    riscv_vector::emit_vlmax_ternary_insn (code_for_pred_minus_mul (<VI:MODE>mode),
+    					   riscv_vector::RVV_TERNOP, ops, operands[4]);
     DONE;
   }
   [(set_attr "type" "vimuladd")
-   (set_attr "mode" "<MODE>")])
+   (set_attr "mode" "<VI:MODE>")])
+
+;; -------------------------------------------------------------------------
+;; ---- [FP] VFMACC and VFMADD
+;; -------------------------------------------------------------------------
+;; Includes:
+;; - vfmacc
+;; - vfmadd
+;; -------------------------------------------------------------------------
+
+(define_expand "fma<mode>4"
+  [(parallel
+    [(set (match_operand:VF_AUTO 0 "register_operand")
+	  (fma:VF_AUTO
+	    (match_operand:VF_AUTO 1 "register_operand")
+	    (match_operand:VF_AUTO 2 "register_operand")
+	    (match_operand:VF_AUTO 3 "register_operand")))
+     (clobber (match_dup 4))])]
+  "TARGET_VECTOR"
+  {
+    operands[4] = gen_reg_rtx (Pmode);
+  })
+
+(define_insn_and_split "*fma<VF_AUTO:mode><P:mode>"
+  [(set (match_operand:VF_AUTO 0 "register_operand"   "=vr, vr, ?&vr")
+	(fma:VF_AUTO
+	  (match_operand:VF_AUTO 1 "register_operand" " %0, vr,   vr")
+	  (match_operand:VF_AUTO 2 "register_operand" " vr, vr,   vr")
+	  (match_operand:VF_AUTO 3 "register_operand" " vr,  0,   vr")))
+   (clobber (match_operand:P 4 "register_operand" "=r,r,r"))]
+  "TARGET_VECTOR"
+  "#"
+  "&& reload_completed"
+  [(const_int 0)]
+  {
+    riscv_vector::emit_vlmax_vsetvl (<VF_AUTO:MODE>mode, operands[4]);
+    if (which_alternative == 2)
+      emit_insn (gen_rtx_SET (operands[0], operands[3]));
+    rtx ops[] = {operands[0], operands[1], operands[2], operands[3], operands[0]};
+    riscv_vector::emit_vlmax_fp_ternary_insn (code_for_pred_mul (PLUS, <VF_AUTO:MODE>mode),
+					      riscv_vector::RVV_TERNOP, ops, operands[4]);
+    DONE;
+  }
+  [(set_attr "type" "vfmuladd")
+   (set_attr "mode" "<VF_AUTO:MODE>")])
+
+;; -------------------------------------------------------------------------
+;; ---- [FP] VFNMSAC and VFNMSUB
+;; -------------------------------------------------------------------------
+;; Includes:
+;; - vfnmsac
+;; - vfnmsub
+;; -------------------------------------------------------------------------
+
+(define_expand "fnma<mode>4"
+  [(parallel
+    [(set (match_operand:VF_AUTO 0 "register_operand")
+	  (fma:VF_AUTO
+	    (neg:VF_AUTO
+	      (match_operand:VF_AUTO 1 "register_operand"))
+	    (match_operand:VF_AUTO 2 "register_operand")
+	    (match_operand:VF_AUTO 3 "register_operand")))
+     (clobber (match_dup 4))])]
+  "TARGET_VECTOR"
+  {
+    operands[4] = gen_reg_rtx (Pmode);
+  })
+
+(define_insn_and_split "*fnma<VF_AUTO:mode><P:mode>"
+  [(set (match_operand:VF_AUTO 0 "register_operand"     "=vr, vr, ?&vr")
+	(fma:VF_AUTO
+	  (neg:VF_AUTO
+	    (match_operand:VF_AUTO 1 "register_operand" " %0, vr,   vr"))
+	  (match_operand:VF_AUTO 2 "register_operand"   " vr, vr,   vr")
+	  (match_operand:VF_AUTO 3 "register_operand"   " vr,  0,   vr")))
+   (clobber (match_operand:P 4 "register_operand" "=r,r,r"))]
+  "TARGET_VECTOR"
+  "#"
+  "&& reload_completed"
+  [(const_int 0)]
+  {
+    riscv_vector::emit_vlmax_vsetvl (<VF_AUTO:MODE>mode, operands[4]);
+    if (which_alternative == 2)
+      emit_insn (gen_rtx_SET (operands[0], operands[3]));
+    rtx ops[] = {operands[0], operands[1], operands[2], operands[3], operands[0]};
+    riscv_vector::emit_vlmax_fp_ternary_insn (code_for_pred_mul_neg (PLUS, <VF_AUTO:MODE>mode),
+					      riscv_vector::RVV_TERNOP, ops, operands[4]);
+    DONE;
+  }
+  [(set_attr "type" "vfmuladd")
+   (set_attr "mode" "<VF_AUTO:MODE>")])
+
+;; -------------------------------------------------------------------------
+;; ---- [FP] VFMSAC and VFMSUB
+;; -------------------------------------------------------------------------
+;; Includes:
+;; - vfmsac
+;; - vfmsub
+;; -------------------------------------------------------------------------
+
+(define_expand "fms<mode>4"
+  [(parallel
+    [(set (match_operand:VF_AUTO 0 "register_operand")
+	  (fma:VF_AUTO
+	    (match_operand:VF_AUTO 1 "register_operand")
+	    (match_operand:VF_AUTO 2 "register_operand")
+	    (neg:VF_AUTO
+	      (match_operand:VF_AUTO 3 "register_operand"))))
+     (clobber (match_dup 4))])]
+  "TARGET_VECTOR"
+  {
+    operands[4] = gen_reg_rtx (Pmode);
+  })
+
+(define_insn_and_split "*fms<VF_AUTO:mode><P:mode>"
+  [(set (match_operand:VF_AUTO 0 "register_operand"     "=vr, vr, ?&vr")
+	(fma:VF_AUTO
+	  (match_operand:VF_AUTO 1 "register_operand"   " %0, vr,   vr")
+	  (match_operand:VF_AUTO 2 "register_operand"   " vr, vr,   vr")
+	  (neg:VF_AUTO
+	    (match_operand:VF_AUTO 3 "register_operand" " vr,  0,   vr"))))
+   (clobber (match_operand:P 4 "register_operand" "=r,r,r"))]
+  "TARGET_VECTOR"
+  "#"
+  "&& reload_completed"
+  [(const_int 0)]
+  {
+    riscv_vector::emit_vlmax_vsetvl (<VF_AUTO:MODE>mode, operands[4]);
+    if (which_alternative == 2)
+      emit_insn (gen_rtx_SET (operands[0], operands[3]));
+    rtx ops[] = {operands[0], operands[1], operands[2], operands[3], operands[0]};
+    riscv_vector::emit_vlmax_fp_ternary_insn (code_for_pred_mul (MINUS, <VF_AUTO:MODE>mode),
+					      riscv_vector::RVV_TERNOP, ops, operands[4]);
+    DONE;
+  }
+  [(set_attr "type" "vfmuladd")
+   (set_attr "mode" "<VF_AUTO:MODE>")])
+
+;; -------------------------------------------------------------------------
+;; ---- [FP] VFMSAC and VFMSUB
+;; -------------------------------------------------------------------------
+;; Includes:
+;; - vfmsac
+;; - vfmsub
+;; -------------------------------------------------------------------------
+
+(define_expand "fnms<mode>4"
+  [(parallel
+    [(set (match_operand:VF_AUTO 0 "register_operand")
+	  (fma:VF_AUTO
+	    (neg:VF_AUTO
+	      (match_operand:VF_AUTO 1 "register_operand"))
+	    (match_operand:VF_AUTO 2 "register_operand")
+	    (neg:VF_AUTO
+	      (match_operand:VF_AUTO 3 "register_operand"))))
+     (clobber (match_dup 4))])]
+  "TARGET_VECTOR"
+  {
+    operands[4] = gen_reg_rtx (Pmode);
+  })
+
+(define_insn_and_split "*fnms<VF_AUTO:mode><P:mode>"
+  [(set (match_operand:VF_AUTO 0 "register_operand"     "=vr, vr, ?&vr")
+	(fma:VF_AUTO
+	  (neg:VF_AUTO
+	    (match_operand:VF_AUTO 1 "register_operand" " %0, vr,   vr"))
+	  (match_operand:VF_AUTO 2 "register_operand"   " vr, vr,   vr")
+	  (neg:VF_AUTO
+	    (match_operand:VF_AUTO 3 "register_operand" " vr,  0,   vr"))))
+   (clobber (match_operand:P 4 "register_operand" "=r,r,r"))]
+  "TARGET_VECTOR"
+  "#"
+  "&& reload_completed"
+  [(const_int 0)]
+  {
+    riscv_vector::emit_vlmax_vsetvl (<VF_AUTO:MODE>mode, operands[4]);
+    if (which_alternative == 2)
+      emit_insn (gen_rtx_SET (operands[0], operands[3]));
+    rtx ops[] = {operands[0], operands[1], operands[2], operands[3], operands[0]};
+    riscv_vector::emit_vlmax_fp_ternary_insn (code_for_pred_mul_neg (MINUS, <VF_AUTO:MODE>mode),
+					      riscv_vector::RVV_TERNOP, ops, operands[4]);
+    DONE;
+  }
+  [(set_attr "type" "vfmuladd")
+   (set_attr "mode" "<VF_AUTO:MODE>")])
 
 ;; =========================================================================
 ;; == SELECT_VL
diff --git a/gcc/config/riscv/genrvv-type-indexer.cc b/gcc/config/riscv/genrvv-type-indexer.cc
index a332a6a..8fc93ce 100644
--- a/gcc/config/riscv/genrvv-type-indexer.cc
+++ b/gcc/config/riscv/genrvv-type-indexer.cc
@@ -73,6 +73,9 @@
   if (nf > 8 || nf < 1)
     return false;
 
+  if (sew == 16 && nf != 1 && float_p) // Disable FP16 tuple in temporarily.
+    return false;
+
   switch (lmul_log2)
     {
     case 1:
diff --git a/gcc/config/riscv/predicates.md b/gcc/config/riscv/predicates.md
index 04ca6ce..eb975ea 100644
--- a/gcc/config/riscv/predicates.md
+++ b/gcc/config/riscv/predicates.md
@@ -276,6 +276,13 @@
   (ior (match_operand 0 "pmode_register_operand")
        (match_operand 0 "const_csr_operand")))
 
+(define_special_predicate "autovec_length_operand"
+  (ior (match_operand 0 "pmode_register_operand")
+       (ior (match_operand 0 "const_csr_operand")
+            (match_test "rtx_equal_p (op, gen_int_mode
+                         (GET_MODE_NUNITS (GET_MODE (op)),
+                                           Pmode))"))))
+
 (define_predicate "reg_or_mem_operand"
   (ior (match_operand 0 "register_operand")
        (match_operand 0 "memory_operand")))
diff --git a/gcc/config/riscv/riscv-modes.def b/gcc/config/riscv/riscv-modes.def
index 1d15270..19a4f9f 100644
--- a/gcc/config/riscv/riscv-modes.def
+++ b/gcc/config/riscv/riscv-modes.def
@@ -220,7 +220,6 @@
 #define RVV_TUPLE_MODES(NBYTES, NSUBPARTS, VB, VH, VS, VD)                     \
   VECTOR_MODE_WITH_PREFIX (VNx##NSUBPARTS##x, INT, QI, NBYTES, 1);             \
   VECTOR_MODE_WITH_PREFIX (VNx##NSUBPARTS##x, INT, HI, NBYTES / 2, 1);         \
-  VECTOR_MODE_WITH_PREFIX (VNx##NSUBPARTS##x, FLOAT, HF, NBYTES / 2, 1);       \
   VECTOR_MODE_WITH_PREFIX (VNx##NSUBPARTS##x, INT, SI, NBYTES / 4, 1);         \
   VECTOR_MODE_WITH_PREFIX (VNx##NSUBPARTS##x, FLOAT, SF, NBYTES / 4, 1);       \
   VECTOR_MODE_WITH_PREFIX (VNx##NSUBPARTS##x, INT, DI, NBYTES / 8, 1);         \
@@ -237,9 +236,6 @@
   ADJUST_NUNITS (VNx##NSUBPARTS##x##VD##DI,                                    \
 		 riscv_v_adjust_nunits (VNx##NSUBPARTS##x##VD##DI##mode,       \
 					VD * NSUBPARTS));                      \
-  ADJUST_NUNITS (VNx##NSUBPARTS##x##VH##HF,                                    \
-		 riscv_v_adjust_nunits (VNx##NSUBPARTS##x##VH##HF##mode,       \
-					VH * NSUBPARTS));                      \
   ADJUST_NUNITS (VNx##NSUBPARTS##x##VS##SF,                                    \
 		 riscv_v_adjust_nunits (VNx##NSUBPARTS##x##VS##SF##mode,       \
 					VS * NSUBPARTS));                      \
@@ -251,7 +247,6 @@
   ADJUST_ALIGNMENT (VNx##NSUBPARTS##x##VH##HI, 2);                             \
   ADJUST_ALIGNMENT (VNx##NSUBPARTS##x##VS##SI, 4);                             \
   ADJUST_ALIGNMENT (VNx##NSUBPARTS##x##VD##DI, 8);                             \
-  ADJUST_ALIGNMENT (VNx##NSUBPARTS##x##VH##HF, 2);                             \
   ADJUST_ALIGNMENT (VNx##NSUBPARTS##x##VS##SF, 4);                             \
   ADJUST_ALIGNMENT (VNx##NSUBPARTS##x##VD##DF, 8);
 
@@ -280,12 +275,10 @@
 #define RVV_TUPLE_PARTIAL_MODES(NSUBPARTS)                                     \
   VECTOR_MODE_WITH_PREFIX (VNx##NSUBPARTS##x, INT, QI, 1, 1);                  \
   VECTOR_MODE_WITH_PREFIX (VNx##NSUBPARTS##x, INT, HI, 1, 1);                  \
-  VECTOR_MODE_WITH_PREFIX (VNx##NSUBPARTS##x, FLOAT, HF, 1, 1);                \
   VECTOR_MODE_WITH_PREFIX (VNx##NSUBPARTS##x, INT, SI, 1, 1);                  \
   VECTOR_MODE_WITH_PREFIX (VNx##NSUBPARTS##x, FLOAT, SF, 1, 1);                \
   VECTOR_MODE_WITH_PREFIX (VNx##NSUBPARTS##x, INT, QI, 2, 1);                  \
   VECTOR_MODE_WITH_PREFIX (VNx##NSUBPARTS##x, INT, HI, 2, 1);                  \
-  VECTOR_MODE_WITH_PREFIX (VNx##NSUBPARTS##x, FLOAT, HF, 2, 1);                \
   VECTOR_MODE_WITH_PREFIX (VNx##NSUBPARTS##x, INT, QI, 4, 1);                  \
                                                                                \
   ADJUST_NUNITS (VNx##NSUBPARTS##x1QI,                                         \
@@ -294,9 +287,6 @@
   ADJUST_NUNITS (VNx##NSUBPARTS##x1HI,                                         \
 		 riscv_v_adjust_nunits (VNx##NSUBPARTS##x1HI##mode,            \
 					NSUBPARTS));                           \
-  ADJUST_NUNITS (VNx##NSUBPARTS##x1HF,                                         \
-		 riscv_v_adjust_nunits (VNx##NSUBPARTS##x1HF##mode,            \
-					NSUBPARTS));                           \
   ADJUST_NUNITS (VNx##NSUBPARTS##x1SI,                                         \
 		 riscv_v_adjust_nunits (VNx##NSUBPARTS##x1SI##mode,            \
 					NSUBPARTS));                           \
@@ -309,20 +299,15 @@
   ADJUST_NUNITS (VNx##NSUBPARTS##x2HI,                                         \
 		 riscv_v_adjust_nunits (VNx##NSUBPARTS##x2HI##mode,            \
 					2 * NSUBPARTS));                       \
-ADJUST_NUNITS (VNx##NSUBPARTS##x2HF,                                         \
-		 riscv_v_adjust_nunits (VNx##NSUBPARTS##x2HF##mode,            \
-					2 * NSUBPARTS));                       \
   ADJUST_NUNITS (VNx##NSUBPARTS##x4QI,                                         \
 		 riscv_v_adjust_nunits (VNx##NSUBPARTS##x4QI##mode,            \
 					4 * NSUBPARTS));                       \
   ADJUST_ALIGNMENT (VNx##NSUBPARTS##x1QI, 1);                                  \
   ADJUST_ALIGNMENT (VNx##NSUBPARTS##x1HI, 2);                                  \
-  ADJUST_ALIGNMENT (VNx##NSUBPARTS##x1HF, 2);                                  \
   ADJUST_ALIGNMENT (VNx##NSUBPARTS##x1SI, 4);                                  \
   ADJUST_ALIGNMENT (VNx##NSUBPARTS##x1SF, 4);                                  \
   ADJUST_ALIGNMENT (VNx##NSUBPARTS##x2QI, 1);                                  \
   ADJUST_ALIGNMENT (VNx##NSUBPARTS##x2HI, 2);                                  \
-  ADJUST_ALIGNMENT (VNx##NSUBPARTS##x2HF, 2);                                  \
   ADJUST_ALIGNMENT (VNx##NSUBPARTS##x4QI, 1);
 
 RVV_TUPLE_PARTIAL_MODES (2)
diff --git a/gcc/config/riscv/riscv-protos.h b/gcc/config/riscv/riscv-protos.h
index f052757..7265b1c 100644
--- a/gcc/config/riscv/riscv-protos.h
+++ b/gcc/config/riscv/riscv-protos.h
@@ -143,6 +143,7 @@
   RVV_CMP_OP = 4,
   RVV_CMP_MU_OP = RVV_CMP_OP + 2, /* +2 means mask and maskoff operand.  */
   RVV_UNOP_MU = RVV_UNOP + 2,	  /* Likewise.  */
+  RVV_UNOP_M = RVV_UNOP + 2,	  /* Likewise.  */
   RVV_TERNOP = 5,
   RVV_WIDEN_TERNOP = 4,
   RVV_SCALAR_MOV_OP = 4, /* +1 for VUNDEF according to vector.md.  */
@@ -187,6 +188,7 @@
 void emit_vlmax_insn (unsigned, int, rtx *, rtx = 0);
 void emit_vlmax_fp_insn (unsigned, int, rtx *, rtx = 0);
 void emit_vlmax_ternary_insn (unsigned, int, rtx *, rtx = 0);
+void emit_vlmax_fp_ternary_insn (unsigned, int, rtx *, rtx = 0);
 void emit_nonvlmax_insn (unsigned, int, rtx *, rtx);
 void emit_vlmax_slide_insn (unsigned, rtx *);
 void emit_nonvlmax_slide_tu_insn (unsigned, rtx *, rtx);
@@ -250,9 +252,9 @@
 opt_machine_mode get_mask_mode (machine_mode);
 void expand_vec_series (rtx, rtx, rtx);
 void expand_vec_init (rtx, rtx);
-void expand_vcond (rtx *);
 void expand_vec_perm (rtx, rtx, rtx, rtx);
 void expand_select_vl (rtx *);
+void expand_load_store (rtx *, bool);
 
 /* Rounding mode bitfield for fixed point VXRM.  */
 enum fixed_point_rounding_mode
diff --git a/gcc/config/riscv/riscv-v.cc b/gcc/config/riscv/riscv-v.cc
index 839a2c6..f6dd0d8 100644
--- a/gcc/config/riscv/riscv-v.cc
+++ b/gcc/config/riscv/riscv-v.cc
@@ -705,19 +705,42 @@
 {
   machine_mode dest_mode = GET_MODE (ops[0]);
   machine_mode mask_mode = get_mask_mode (dest_mode).require ();
-  /* We have a maximum of 11 operands for RVV instruction patterns according to
-   * vector.md.  */
-  insn_expander<11> e (/*OP_NUM*/ op_num, /*HAS_DEST_P*/ true,
-		       /*FULLY_UNMASKED_P*/ true,
-		       /*USE_REAL_MERGE_P*/ true, /*HAS_AVL_P*/ true,
-		       /*VLMAX_P*/ true,
-		       /*DEST_MODE*/ dest_mode, /*MASK_MODE*/ mask_mode);
+  insn_expander<RVV_INSN_OPERANDS_MAX> e (/*OP_NUM*/ op_num,
+					  /*HAS_DEST_P*/ true,
+					  /*FULLY_UNMASKED_P*/ true,
+					  /*USE_REAL_MERGE_P*/ true,
+					  /*HAS_AVL_P*/ true,
+					  /*VLMAX_P*/ true,
+					  /*DEST_MODE*/ dest_mode,
+					  /*MASK_MODE*/ mask_mode);
   e.set_policy (TAIL_ANY);
   e.set_policy (MASK_ANY);
   e.set_vl (vl);
   e.emit_insn ((enum insn_code) icode, ops);
 }
 
+/* This function emits a {VLMAX, TAIL_ANY, MASK_ANY} vsetvli followed by the
+ * ternary operation which always has a real merge operand.  */
+void
+emit_vlmax_fp_ternary_insn (unsigned icode, int op_num, rtx *ops, rtx vl)
+{
+  machine_mode dest_mode = GET_MODE (ops[0]);
+  machine_mode mask_mode = get_mask_mode (dest_mode).require ();
+  insn_expander<RVV_INSN_OPERANDS_MAX> e (/*OP_NUM*/ op_num,
+					  /*HAS_DEST_P*/ true,
+					  /*FULLY_UNMASKED_P*/ true,
+					  /*USE_REAL_MERGE_P*/ true,
+					  /*HAS_AVL_P*/ true,
+					  /*VLMAX_P*/ true,
+					  /*DEST_MODE*/ dest_mode,
+					  /*MASK_MODE*/ mask_mode);
+  e.set_policy (TAIL_ANY);
+  e.set_policy (MASK_ANY);
+  e.set_rounding_mode (FRM_DYN);
+  e.set_vl (vl);
+  e.emit_insn ((enum insn_code) icode, ops);
+}
+
 /* This function emits a {NONVLMAX, TAIL_ANY, MASK_ANY} vsetvli followed by the
  * actual operation.  */
 void
@@ -842,16 +865,55 @@
 }
 
 /* This function emits a masked instruction.  */
+static void
+emit_vlmax_masked_insn (unsigned icode, int op_num, rtx *ops)
+{
+  machine_mode dest_mode = GET_MODE (ops[0]);
+  machine_mode mask_mode = get_mask_mode (dest_mode).require ();
+  insn_expander<RVV_INSN_OPERANDS_MAX> e (/*OP_NUM*/ op_num,
+					  /*HAS_DEST_P*/ true,
+					  /*FULLY_UNMASKED_P*/ false,
+					  /*USE_REAL_MERGE_P*/ true,
+					  /*HAS_AVL_P*/ true,
+					  /*VLMAX_P*/ true, dest_mode,
+					  mask_mode);
+  e.set_policy (TAIL_ANY);
+  e.set_policy (MASK_ANY);
+  e.emit_insn ((enum insn_code) icode, ops);
+}
+
+/* This function emits a masked instruction.  */
+static void
+emit_nonvlmax_masked_insn (unsigned icode, int op_num, rtx *ops, rtx avl)
+{
+  machine_mode dest_mode = GET_MODE (ops[0]);
+  machine_mode mask_mode = get_mask_mode (dest_mode).require ();
+  insn_expander<RVV_INSN_OPERANDS_MAX> e (/*OP_NUM*/ op_num,
+					  /*HAS_DEST_P*/ true,
+					  /*FULLY_UNMASKED_P*/ false,
+					  /*USE_REAL_MERGE_P*/ true,
+					  /*HAS_AVL_P*/ true,
+					  /*VLMAX_P*/ false, dest_mode,
+					  mask_mode);
+  e.set_policy (TAIL_ANY);
+  e.set_policy (MASK_ANY);
+  e.set_vl (avl);
+  e.emit_insn ((enum insn_code) icode, ops);
+}
+
+/* This function emits a masked instruction.  */
 void
 emit_vlmax_masked_mu_insn (unsigned icode, int op_num, rtx *ops)
 {
   machine_mode dest_mode = GET_MODE (ops[0]);
   machine_mode mask_mode = get_mask_mode (dest_mode).require ();
-  insn_expander<11> e (/*OP_NUM*/ op_num, /*HAS_DEST_P*/ true,
-		       /*FULLY_UNMASKED_P*/ false,
-		       /*USE_REAL_MERGE_P*/ true,
-		       /*HAS_AVL_P*/ true,
-		       /*VLMAX_P*/ true, dest_mode, mask_mode);
+  insn_expander<RVV_INSN_OPERANDS_MAX> e (/*OP_NUM*/ op_num,
+					  /*HAS_DEST_P*/ true,
+					  /*FULLY_UNMASKED_P*/ false,
+					  /*USE_REAL_MERGE_P*/ true,
+					  /*HAS_AVL_P*/ true,
+					  /*VLMAX_P*/ true, dest_mode,
+					  mask_mode);
   e.set_policy (TAIL_ANY);
   e.set_policy (MASK_UNDISTURBED);
   e.emit_insn ((enum insn_code) icode, ops);
@@ -2359,28 +2421,6 @@
   return false;
 }
 
-/* Expand an RVV vcond pattern with operands OPS.  DATA_MODE is the mode
-   of the data being merged and CMP_MODE is the mode of the values being
-   compared.  */
-
-void
-expand_vcond (rtx *ops)
-{
-  machine_mode cmp_mode = GET_MODE (ops[4]);
-  machine_mode data_mode = GET_MODE (ops[1]);
-  machine_mode mask_mode = get_mask_mode (cmp_mode).require ();
-  rtx mask = gen_reg_rtx (mask_mode);
-  if (FLOAT_MODE_P (cmp_mode))
-    {
-      if (expand_vec_cmp_float (mask, GET_CODE (ops[3]), ops[4], ops[5], true))
-	std::swap (ops[1], ops[2]);
-    }
-  else
-    expand_vec_cmp (mask, GET_CODE (ops[3]), ops[4], ops[5]);
-  emit_insn (
-    gen_vcond_mask (data_mode, data_mode, ops[0], ops[1], ops[2], mask));
-}
-
 /* Implement vec_perm<mode>.  */
 
 void
@@ -2721,4 +2761,45 @@
   emit_insn (gen_no_side_effects_vsetvl_rtx (rvv_mode, ops[0], ops[1]));
 }
 
+/* Expand LEN_MASK_{LOAD,STORE}.  */
+void
+expand_load_store (rtx *ops, bool is_load)
+{
+  poly_int64 value;
+  rtx len = ops[2];
+  rtx mask = ops[3];
+  machine_mode mode = GET_MODE (ops[0]);
+
+  if (poly_int_rtx_p (len, &value) && known_eq (value, GET_MODE_NUNITS (mode)))
+    {
+      /* If the length operand is equal to VF, it is VLMAX load/store.  */
+      if (is_load)
+	{
+	  rtx m_ops[] = {ops[0], mask, RVV_VUNDEF (mode), ops[1]};
+	  emit_vlmax_masked_insn (code_for_pred_mov (mode), RVV_UNOP_M, m_ops);
+	}
+      else
+	{
+	  len = gen_reg_rtx (Pmode);
+	  emit_vlmax_vsetvl (mode, len);
+	  emit_insn (gen_pred_store (mode, ops[0], mask, ops[1], len,
+				     get_avl_type_rtx (VLMAX)));
+	}
+    }
+  else
+    {
+      if (!satisfies_constraint_K (len))
+	len = force_reg (Pmode, len);
+      if (is_load)
+	{
+	  rtx m_ops[] = {ops[0], mask, RVV_VUNDEF (mode), ops[1]};
+	  emit_nonvlmax_masked_insn (code_for_pred_mov (mode), RVV_UNOP_M,
+				     m_ops, len);
+	}
+      else
+	emit_insn (gen_pred_store (mode, ops[0], mask, ops[1], len,
+				   get_avl_type_rtx (NONVLMAX)));
+    }
+}
+
 } // namespace riscv_vector
diff --git a/gcc/config/riscv/riscv-vector-builtins-bases.cc b/gcc/config/riscv/riscv-vector-builtins-bases.cc
index c6c53dc..5c8deda 100644
--- a/gcc/config/riscv/riscv-vector-builtins-bases.cc
+++ b/gcc/config/riscv/riscv-vector-builtins-bases.cc
@@ -1567,7 +1567,7 @@
   {
     tree arg = CALL_EXPR_ARG (e.exp, 0);
     rtx src = expand_normal (arg);
-    emit_insn (gen_rtx_SET (gen_lowpart (e.vector_mode (), e.target), src));
+    emit_move_insn (gen_lowpart (e.vector_mode (), e.target), src);
     return e.target;
   }
 };
diff --git a/gcc/config/riscv/riscv-vector-builtins-bases.h b/gcc/config/riscv/riscv-vector-builtins-bases.h
index 62ff38a..fb95d6a 100644
--- a/gcc/config/riscv/riscv-vector-builtins-bases.h
+++ b/gcc/config/riscv/riscv-vector-builtins-bases.h
@@ -121,8 +121,6 @@
 extern const function_base *const vssra;
 extern const function_base *const vssrl;
 extern const function_base *const vnclip;
-extern const function_base *const vnclip;
-extern const function_base *const vnclipu;
 extern const function_base *const vnclipu;
 extern const function_base *const vmand;
 extern const function_base *const vmnand;
@@ -144,8 +142,6 @@
 extern const function_base *const viota;
 extern const function_base *const vid;
 extern const function_base *const vfadd;
-extern const function_base *const vfadd;
-extern const function_base *const vfsub;
 extern const function_base *const vfsub;
 extern const function_base *const vfrsub;
 extern const function_base *const vfwadd;
@@ -153,7 +149,6 @@
 extern const function_base *const vfmul;
 extern const function_base *const vfmul;
 extern const function_base *const vfdiv;
-extern const function_base *const vfdiv;
 extern const function_base *const vfrdiv;
 extern const function_base *const vfwmul;
 extern const function_base *const vfmacc;
diff --git a/gcc/config/riscv/riscv-vector-builtins-types.def b/gcc/config/riscv/riscv-vector-builtins-types.def
index 1c3cc0e..4926bd8 100644
--- a/gcc/config/riscv/riscv-vector-builtins-types.def
+++ b/gcc/config/riscv/riscv-vector-builtins-types.def
@@ -1291,31 +1291,6 @@
 DEF_RVV_TUPLE_OPS (vuint64m2x4_t, RVV_REQUIRE_ELEN_64)
 DEF_RVV_TUPLE_OPS (vint64m4x2_t, RVV_REQUIRE_ELEN_64)
 DEF_RVV_TUPLE_OPS (vuint64m4x2_t, RVV_REQUIRE_ELEN_64)
-DEF_RVV_TUPLE_OPS (vfloat16mf4x2_t, RVV_REQUIRE_ELEN_FP_16 | RVV_REQUIRE_MIN_VLEN_64)
-DEF_RVV_TUPLE_OPS (vfloat16mf4x3_t, RVV_REQUIRE_ELEN_FP_16 | RVV_REQUIRE_MIN_VLEN_64)
-DEF_RVV_TUPLE_OPS (vfloat16mf4x4_t, RVV_REQUIRE_ELEN_FP_16 | RVV_REQUIRE_MIN_VLEN_64)
-DEF_RVV_TUPLE_OPS (vfloat16mf4x5_t, RVV_REQUIRE_ELEN_FP_16 | RVV_REQUIRE_MIN_VLEN_64)
-DEF_RVV_TUPLE_OPS (vfloat16mf4x6_t, RVV_REQUIRE_ELEN_FP_16 | RVV_REQUIRE_MIN_VLEN_64)
-DEF_RVV_TUPLE_OPS (vfloat16mf4x7_t, RVV_REQUIRE_ELEN_FP_16 | RVV_REQUIRE_MIN_VLEN_64)
-DEF_RVV_TUPLE_OPS (vfloat16mf4x8_t, RVV_REQUIRE_ELEN_FP_16 | RVV_REQUIRE_MIN_VLEN_64)
-DEF_RVV_TUPLE_OPS (vfloat16mf2x2_t, RVV_REQUIRE_ELEN_FP_16)
-DEF_RVV_TUPLE_OPS (vfloat16mf2x3_t, RVV_REQUIRE_ELEN_FP_16)
-DEF_RVV_TUPLE_OPS (vfloat16mf2x4_t, RVV_REQUIRE_ELEN_FP_16)
-DEF_RVV_TUPLE_OPS (vfloat16mf2x5_t, RVV_REQUIRE_ELEN_FP_16)
-DEF_RVV_TUPLE_OPS (vfloat16mf2x6_t, RVV_REQUIRE_ELEN_FP_16)
-DEF_RVV_TUPLE_OPS (vfloat16mf2x7_t, RVV_REQUIRE_ELEN_FP_16)
-DEF_RVV_TUPLE_OPS (vfloat16mf2x8_t, RVV_REQUIRE_ELEN_FP_16)
-DEF_RVV_TUPLE_OPS (vfloat16m1x2_t, RVV_REQUIRE_ELEN_FP_16)
-DEF_RVV_TUPLE_OPS (vfloat16m1x3_t, RVV_REQUIRE_ELEN_FP_16)
-DEF_RVV_TUPLE_OPS (vfloat16m1x4_t, RVV_REQUIRE_ELEN_FP_16)
-DEF_RVV_TUPLE_OPS (vfloat16m1x5_t, RVV_REQUIRE_ELEN_FP_16)
-DEF_RVV_TUPLE_OPS (vfloat16m1x6_t, RVV_REQUIRE_ELEN_FP_16)
-DEF_RVV_TUPLE_OPS (vfloat16m1x7_t, RVV_REQUIRE_ELEN_FP_16)
-DEF_RVV_TUPLE_OPS (vfloat16m1x8_t, RVV_REQUIRE_ELEN_FP_16)
-DEF_RVV_TUPLE_OPS (vfloat16m2x2_t, RVV_REQUIRE_ELEN_FP_16)
-DEF_RVV_TUPLE_OPS (vfloat16m2x3_t, RVV_REQUIRE_ELEN_FP_16)
-DEF_RVV_TUPLE_OPS (vfloat16m2x4_t, RVV_REQUIRE_ELEN_FP_16)
-DEF_RVV_TUPLE_OPS (vfloat16m4x2_t, RVV_REQUIRE_ELEN_FP_16)
 DEF_RVV_TUPLE_OPS (vfloat32mf2x2_t, RVV_REQUIRE_ELEN_FP_32 | RVV_REQUIRE_MIN_VLEN_64)
 DEF_RVV_TUPLE_OPS (vfloat32mf2x3_t, RVV_REQUIRE_ELEN_FP_32 | RVV_REQUIRE_MIN_VLEN_64)
 DEF_RVV_TUPLE_OPS (vfloat32mf2x4_t, RVV_REQUIRE_ELEN_FP_32 | RVV_REQUIRE_MIN_VLEN_64)
diff --git a/gcc/config/riscv/riscv-vector-builtins.cc b/gcc/config/riscv/riscv-vector-builtins.cc
index 9e6dae9..466e36d 100644
--- a/gcc/config/riscv/riscv-vector-builtins.cc
+++ b/gcc/config/riscv/riscv-vector-builtins.cc
@@ -3636,6 +3636,7 @@
   for (int argno = arg_offset; argno < call_expr_nargs (exp); argno++)
     add_input_operand (argno);
 
+  add_input_operand (Pmode, get_avl_type_rtx (avl_type::NONVLMAX));
   return generate_insn (icode);
 }
 
diff --git a/gcc/config/riscv/riscv-vector-builtins.def b/gcc/config/riscv/riscv-vector-builtins.def
index 1e94579..310edea 100644
--- a/gcc/config/riscv/riscv-vector-builtins.def
+++ b/gcc/config/riscv/riscv-vector-builtins.def
@@ -494,48 +494,18 @@
 /* LMUL = 1/4.  */
 DEF_RVV_TYPE (vfloat16mf4_t, 18, __rvv_float16mf4_t, float16, VNx2HF, VNx1HF, VOID,
 	      _f16mf4, _f16, _e16mf4)
-/* Define tuple types for SEW = 16, LMUL = MF4. */
-DEF_RVV_TUPLE_TYPE (vfloat16mf4x2_t, 20, __rvv_float16mf4x2_t, vfloat16mf4_t, float, 2, _f16mf4x2)
-DEF_RVV_TUPLE_TYPE (vfloat16mf4x3_t, 20, __rvv_float16mf4x3_t, vfloat16mf4_t, float, 3, _f16mf4x3)
-DEF_RVV_TUPLE_TYPE (vfloat16mf4x4_t, 20, __rvv_float16mf4x4_t, vfloat16mf4_t, float, 4, _f16mf4x4)
-DEF_RVV_TUPLE_TYPE (vfloat16mf4x5_t, 20, __rvv_float16mf4x5_t, vfloat16mf4_t, float, 5, _f16mf4x5)
-DEF_RVV_TUPLE_TYPE (vfloat16mf4x6_t, 20, __rvv_float16mf4x6_t, vfloat16mf4_t, float, 6, _f16mf4x6)
-DEF_RVV_TUPLE_TYPE (vfloat16mf4x7_t, 20, __rvv_float16mf4x7_t, vfloat16mf4_t, float, 7, _f16mf4x7)
-DEF_RVV_TUPLE_TYPE (vfloat16mf4x8_t, 20, __rvv_float16mf4x8_t, vfloat16mf4_t, float, 8, _f16mf4x8)
 /* LMUL = 1/2.  */
 DEF_RVV_TYPE (vfloat16mf2_t, 18, __rvv_float16mf2_t, float16, VNx4HF, VNx2HF, VNx1HF,
 	      _f16mf2, _f16, _e16mf2)
-/* Define tuple types for SEW = 16, LMUL = MF2. */
-DEF_RVV_TUPLE_TYPE (vfloat16mf2x2_t, 20, __rvv_float16mf2x2_t, vfloat16mf2_t, float, 2, _f16mf2x2)
-DEF_RVV_TUPLE_TYPE (vfloat16mf2x3_t, 20, __rvv_float16mf2x3_t, vfloat16mf2_t, float, 3, _f16mf2x3)
-DEF_RVV_TUPLE_TYPE (vfloat16mf2x4_t, 20, __rvv_float16mf2x4_t, vfloat16mf2_t, float, 4, _f16mf2x4)
-DEF_RVV_TUPLE_TYPE (vfloat16mf2x5_t, 20, __rvv_float16mf2x5_t, vfloat16mf2_t, float, 5, _f16mf2x5)
-DEF_RVV_TUPLE_TYPE (vfloat16mf2x6_t, 20, __rvv_float16mf2x6_t, vfloat16mf2_t, float, 6, _f16mf2x6)
-DEF_RVV_TUPLE_TYPE (vfloat16mf2x7_t, 20, __rvv_float16mf2x7_t, vfloat16mf2_t, float, 7, _f16mf2x7)
-DEF_RVV_TUPLE_TYPE (vfloat16mf2x8_t, 20, __rvv_float16mf2x8_t, vfloat16mf2_t, float, 8, _f16mf2x8)
 /* LMUL = 1.  */
 DEF_RVV_TYPE (vfloat16m1_t, 17, __rvv_float16m1_t, float16, VNx8HF, VNx4HF, VNx2HF,
 	      _f16m1, _f16, _e16m1)
-/* Define tuple types for SEW = 16, LMUL = M1. */
-DEF_RVV_TUPLE_TYPE (vfloat16m1x2_t, 19, __rvv_float16m1x2_t, vfloat16m1_t, float, 2, _f16m1x2)
-DEF_RVV_TUPLE_TYPE (vfloat16m1x3_t, 19, __rvv_float16m1x3_t, vfloat16m1_t, float, 3, _f16m1x3)
-DEF_RVV_TUPLE_TYPE (vfloat16m1x4_t, 19, __rvv_float16m1x4_t, vfloat16m1_t, float, 4, _f16m1x4)
-DEF_RVV_TUPLE_TYPE (vfloat16m1x5_t, 19, __rvv_float16m1x5_t, vfloat16m1_t, float, 5, _f16m1x5)
-DEF_RVV_TUPLE_TYPE (vfloat16m1x6_t, 19, __rvv_float16m1x6_t, vfloat16m1_t, float, 6, _f16m1x6)
-DEF_RVV_TUPLE_TYPE (vfloat16m1x7_t, 19, __rvv_float16m1x7_t, vfloat16m1_t, float, 7, _f16m1x7)
-DEF_RVV_TUPLE_TYPE (vfloat16m1x8_t, 19, __rvv_float16m1x8_t, vfloat16m1_t, float, 8, _f16m1x8)
 /* LMUL = 2.  */
 DEF_RVV_TYPE (vfloat16m2_t, 17, __rvv_float16m2_t, float16, VNx16HF, VNx8HF, VNx4HF,
 	      _f16m2, _f16, _e16m2)
-/* Define tuple types for SEW = 16, LMUL = M2. */
-DEF_RVV_TUPLE_TYPE (vfloat16m2x2_t, 19, __rvv_float16m2x2_t, vfloat16m2_t, float, 2, _f16m2x2)
-DEF_RVV_TUPLE_TYPE (vfloat16m2x3_t, 19, __rvv_float16m2x3_t, vfloat16m2_t, float, 3, _f16m2x3)
-DEF_RVV_TUPLE_TYPE (vfloat16m2x4_t, 19, __rvv_float16m2x4_t, vfloat16m2_t, float, 4, _f16m2x4)
 /* LMUL = 4.  */
 DEF_RVV_TYPE (vfloat16m4_t, 17, __rvv_float16m4_t, float16, VNx32HF, VNx16HF, VNx8HF,
 	      _f16m4, _f16, _e16m4)
-/* Define tuple types for SEW = 16, LMUL = M4. */
-DEF_RVV_TUPLE_TYPE (vfloat16m4x2_t, 19, __rvv_float16m4x2_t, vfloat16m4_t, float, 2, _f16m4x2)
 /* LMUL = 8.  */
 DEF_RVV_TYPE (vfloat16m8_t, 16, __rvv_float16m8_t, float16, VNx64HF, VNx32HF, VNx16HF,
 	      _f16m8, _f16, _e16m8)
diff --git a/gcc/config/riscv/riscv-vector-switch.def b/gcc/config/riscv/riscv-vector-switch.def
index 7f14891..52f07709 100644
--- a/gcc/config/riscv/riscv-vector-switch.def
+++ b/gcc/config/riscv/riscv-vector-switch.def
@@ -248,38 +248,6 @@
 TUPLE_ENTRY (VNx6x1HI, TARGET_MIN_VLEN < 128, VNx1HI, 6, LMUL_F2, 32, LMUL_F4, 64, LMUL_RESERVED, 0)
 TUPLE_ENTRY (VNx7x1HI, TARGET_MIN_VLEN < 128, VNx1HI, 7, LMUL_F2, 32, LMUL_F4, 64, LMUL_RESERVED, 0)
 TUPLE_ENTRY (VNx8x1HI, TARGET_MIN_VLEN < 128, VNx1HI, 8, LMUL_F2, 32, LMUL_F4, 64, LMUL_RESERVED, 0)
-TUPLE_ENTRY (VNx2x32HF, TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN >= 128, VNx32HF, 2, LMUL_RESERVED, 0, LMUL_RESERVED, 0, LMUL_4, 4)
-TUPLE_ENTRY (VNx2x16HF, TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN >= 64, VNx16HF, 2, LMUL_RESERVED, 0, LMUL_4, 4, LMUL_2, 8)
-TUPLE_ENTRY (VNx3x16HF, TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN >= 128, VNx16HF, 3, LMUL_RESERVED, 0, LMUL_RESERVED, 0, LMUL_2, 8)
-TUPLE_ENTRY (VNx4x16HF, TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN >= 128, VNx16HF, 4, LMUL_RESERVED, 0, LMUL_RESERVED, 0, LMUL_2, 8)
-TUPLE_ENTRY (VNx2x8HF, TARGET_VECTOR_ELEN_FP_16, VNx8HF, 2, LMUL_4, 4, LMUL_2, 8, LMUL_1, 16)
-TUPLE_ENTRY (VNx3x8HF, TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN >= 64, VNx8HF, 3, LMUL_RESERVED, 0, LMUL_2, 8, LMUL_1, 16)
-TUPLE_ENTRY (VNx4x8HF, TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN >= 64, VNx8HF, 4, LMUL_RESERVED, 0, LMUL_2, 8, LMUL_1, 16)
-TUPLE_ENTRY (VNx5x8HF, TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN >= 128, VNx8HF, 5, LMUL_RESERVED, 0, LMUL_RESERVED, 0, LMUL_1, 16)
-TUPLE_ENTRY (VNx6x8HF, TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN >= 128, VNx8HF, 6, LMUL_RESERVED, 0, LMUL_RESERVED, 0, LMUL_1, 16)
-TUPLE_ENTRY (VNx7x8HF, TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN >= 128, VNx8HF, 7, LMUL_RESERVED, 0, LMUL_RESERVED, 0, LMUL_1, 16)
-TUPLE_ENTRY (VNx8x8HF, TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN >= 128, VNx8HF, 8, LMUL_RESERVED, 0, LMUL_RESERVED, 0, LMUL_1, 16)
-TUPLE_ENTRY (VNx2x4HF, TARGET_VECTOR_ELEN_FP_16, VNx4HF, 2, LMUL_2, 8, LMUL_1, 16, LMUL_F2, 32)
-TUPLE_ENTRY (VNx3x4HF, TARGET_VECTOR_ELEN_FP_16, VNx4HF, 3, LMUL_2, 8, LMUL_1, 16, LMUL_F2, 32)
-TUPLE_ENTRY (VNx4x4HF, TARGET_VECTOR_ELEN_FP_16, VNx4HF, 4, LMUL_2, 8, LMUL_1, 16, LMUL_F2, 32)
-TUPLE_ENTRY (VNx5x4HF, TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN >= 64, VNx4HF, 5, LMUL_RESERVED, 0, LMUL_1, 16, LMUL_F2, 32)
-TUPLE_ENTRY (VNx6x4HF, TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN >= 64, VNx4HF, 6, LMUL_RESERVED, 0, LMUL_1, 16, LMUL_F2, 32)
-TUPLE_ENTRY (VNx7x4HF, TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN >= 64, VNx4HF, 7, LMUL_RESERVED, 0, LMUL_1, 16, LMUL_F2, 32)
-TUPLE_ENTRY (VNx8x4HF, TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN >= 64, VNx4HF, 8, LMUL_RESERVED, 0, LMUL_1, 16, LMUL_F2, 32)
-TUPLE_ENTRY (VNx2x2HF, TARGET_VECTOR_ELEN_FP_16, VNx2HF, 2, LMUL_1, 16, LMUL_F2, 32, LMUL_F4, 64)
-TUPLE_ENTRY (VNx3x2HF, TARGET_VECTOR_ELEN_FP_16, VNx2HF, 3, LMUL_1, 16, LMUL_F2, 32, LMUL_F4, 64)
-TUPLE_ENTRY (VNx4x2HF, TARGET_VECTOR_ELEN_FP_16, VNx2HF, 4, LMUL_1, 16, LMUL_F2, 32, LMUL_F4, 64)
-TUPLE_ENTRY (VNx5x2HF, TARGET_VECTOR_ELEN_FP_16, VNx2HF, 5, LMUL_1, 16, LMUL_F2, 32, LMUL_F4, 64)
-TUPLE_ENTRY (VNx6x2HF, TARGET_VECTOR_ELEN_FP_16, VNx2HF, 6, LMUL_1, 16, LMUL_F2, 32, LMUL_F4, 64)
-TUPLE_ENTRY (VNx7x2HF, TARGET_VECTOR_ELEN_FP_16, VNx2HF, 7, LMUL_1, 16, LMUL_F2, 32, LMUL_F4, 64)
-TUPLE_ENTRY (VNx8x2HF, TARGET_VECTOR_ELEN_FP_16, VNx2HF, 8, LMUL_1, 16, LMUL_F2, 32, LMUL_F4, 64)
-TUPLE_ENTRY (VNx2x1HF, TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN < 128, VNx1HF, 2, LMUL_F2, 32, LMUL_F4, 64, LMUL_RESERVED, 0)
-TUPLE_ENTRY (VNx3x1HF, TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN < 128, VNx1HF, 3, LMUL_F2, 32, LMUL_F4, 64, LMUL_RESERVED, 0)
-TUPLE_ENTRY (VNx4x1HF, TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN < 128, VNx1HF, 4, LMUL_F2, 32, LMUL_F4, 64, LMUL_RESERVED, 0)
-TUPLE_ENTRY (VNx5x1HF, TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN < 128, VNx1HF, 5, LMUL_F2, 32, LMUL_F4, 64, LMUL_RESERVED, 0)
-TUPLE_ENTRY (VNx6x1HF, TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN < 128, VNx1HF, 6, LMUL_F2, 32, LMUL_F4, 64, LMUL_RESERVED, 0)
-TUPLE_ENTRY (VNx7x1HF, TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN < 128, VNx1HF, 7, LMUL_F2, 32, LMUL_F4, 64, LMUL_RESERVED, 0)
-TUPLE_ENTRY (VNx8x1HF, TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN < 128, VNx1HF, 8, LMUL_F2, 32, LMUL_F4, 64, LMUL_RESERVED, 0)
 
 /* Tuple modes for EEW = 32.  */
 TUPLE_ENTRY (VNx2x16SI, TARGET_MIN_VLEN >= 128, VNx16SI, 2, LMUL_RESERVED, 0, LMUL_RESERVED, 0, LMUL_4, 8)
diff --git a/gcc/config/riscv/riscv-vsetvl.cc b/gcc/config/riscv/riscv-vsetvl.cc
index 971c3f9..2d576e8 100644
--- a/gcc/config/riscv/riscv-vsetvl.cc
+++ b/gcc/config/riscv/riscv-vsetvl.cc
@@ -2003,9 +2003,51 @@
   new_info.parse_insn (def_insn);
   if (!same_vlmax_p (new_info) && !scalar_move_insn_p (insn->rtl ()))
     return;
-  /* TODO: Currently, we don't forward AVL for non-VLMAX vsetvl.  */
-  if (vlmax_avl_p (new_info.get_avl ()))
-    set_avl_info (avl_info (new_info.get_avl (), get_avl_source ()));
+
+  if (new_info.has_avl ())
+    {
+      if (new_info.has_avl_imm ())
+	set_avl_info (avl_info (new_info.get_avl (), nullptr));
+      else
+	{
+	  if (vlmax_avl_p (new_info.get_avl ()))
+	    set_avl_info (avl_info (new_info.get_avl (), get_avl_source ()));
+	  else
+	    {
+	      /* Conservatively propagate non-VLMAX AVL of user vsetvl:
+		 1. The user vsetvl should be same block with the rvv insn.
+		 2. The user vsetvl is the only def insn of rvv insn.
+		 3. The AVL is not modified between def-use chain.
+		 4. The VL is only used by insn within EBB.
+	       */
+	      bool modified_p = false;
+	      for (insn_info *i = def_insn->next_nondebug_insn ();
+		   real_insn_and_same_bb_p (i, get_insn ()->bb ());
+		   i = i->next_nondebug_insn ())
+		{
+		  if (find_access (i->defs (), REGNO (new_info.get_avl ())))
+		    {
+		      modified_p = true;
+		      break;
+		    }
+		}
+
+	      bool has_live_out_use = false;
+	      for (use_info *use : m_avl.get_source ()->all_uses ())
+		{
+		  if (use->is_live_out_use ())
+		    {
+		      has_live_out_use = true;
+		      break;
+		    }
+		}
+	      if (!modified_p && !has_live_out_use
+		  && def_insn == m_avl.get_source ()->insn ()
+		  && m_insn->bb () == def_insn->bb ())
+		set_avl_info (new_info.get_avl_info ());
+	    }
+	}
+    }
 
   if (scalar_move_insn_p (insn->rtl ()) && m_avl.has_non_zero_avl ())
     m_demands[DEMAND_NONZERO_AVL] = true;
diff --git a/gcc/config/riscv/riscv-vsetvl.h b/gcc/config/riscv/riscv-vsetvl.h
index 4257451..87cdd2e 100644
--- a/gcc/config/riscv/riscv-vsetvl.h
+++ b/gcc/config/riscv/riscv-vsetvl.h
@@ -180,6 +180,7 @@
   bool has_avl_reg () const { return get_value () && REG_P (get_value ()); }
   bool has_avl_no_reg () const { return !get_value (); }
   bool has_non_zero_avl () const;
+  bool has_avl () const { return get_value (); }
 };
 
 /* Basic structure to save VL/VTYPE information.  */
@@ -219,6 +220,7 @@
   bool has_avl_reg () const { return m_avl.has_avl_reg (); }
   bool has_avl_no_reg () const { return m_avl.has_avl_no_reg (); }
   bool has_non_zero_avl () const { return m_avl.has_non_zero_avl (); };
+  bool has_avl () const { return m_avl.has_avl (); }
 
   rtx get_avl () const { return m_avl.get_value (); }
   const avl_info &get_avl_info () const { return m_avl; }
diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md
index 245cace..565e8cd 100644
--- a/gcc/config/riscv/riscv.md
+++ b/gcc/config/riscv/riscv.md
@@ -191,11 +191,6 @@
   VNx2x4HI,VNx3x4HI,VNx4x4HI,VNx5x4HI,VNx6x4HI,VNx7x4HI,VNx8x4HI,
   VNx2x2HI,VNx3x2HI,VNx4x2HI,VNx5x2HI,VNx6x2HI,VNx7x2HI,VNx8x2HI,
   VNx2x1HI,VNx3x1HI,VNx4x1HI,VNx5x1HI,VNx6x1HI,VNx7x1HI,VNx8x1HI,
-  VNx2x32HF,VNx2x16HF,VNx3x16HF,VNx4x16HF,
-  VNx2x8HF,VNx3x8HF,VNx4x8HF,VNx5x8HF,VNx6x8HF,VNx7x8HF,VNx8x8HF,
-  VNx2x4HF,VNx3x4HF,VNx4x4HF,VNx5x4HF,VNx6x4HF,VNx7x4HF,VNx8x4HF,
-  VNx2x2HF,VNx3x2HF,VNx4x2HF,VNx5x2HF,VNx6x2HF,VNx7x2HF,VNx8x2HF,
-  VNx2x1HF,VNx3x1HF,VNx4x1HF,VNx5x1HF,VNx6x1HF,VNx7x1HF,VNx8x1HF,
   VNx2x16SI,VNx2x8SI,VNx3x8SI,VNx4x8SI,
   VNx2x4SI,VNx3x4SI,VNx4x4SI,VNx5x4SI,VNx6x4SI,VNx7x4SI,VNx8x4SI,
   VNx2x2SI,VNx3x2SI,VNx4x2SI,VNx5x2SI,VNx6x2SI,VNx7x2SI,VNx8x2SI,
diff --git a/gcc/config/riscv/vector-iterators.md b/gcc/config/riscv/vector-iterators.md
index 6ca1c54..26c1bb7 100644
--- a/gcc/config/riscv/vector-iterators.md
+++ b/gcc/config/riscv/vector-iterators.md
@@ -652,38 +652,6 @@
   (VNx6x1DI "TARGET_VECTOR_ELEN_64 && TARGET_MIN_VLEN < 128")
   (VNx7x1DI "TARGET_VECTOR_ELEN_64 && TARGET_MIN_VLEN < 128")
   (VNx8x1DI "TARGET_VECTOR_ELEN_64 && TARGET_MIN_VLEN < 128")
-  (VNx2x32HF "TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN >= 128")
-  (VNx2x16HF "TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN >= 64")
-  (VNx3x16HF "TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN >= 128")
-  (VNx4x16HF "TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN >= 128")
-  (VNx2x8HF "TARGET_VECTOR_ELEN_FP_16")
-  (VNx3x8HF "TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN >= 64")
-  (VNx4x8HF "TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN >= 64")
-  (VNx5x8HF "TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN >= 128")
-  (VNx6x8HF "TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN >= 128")
-  (VNx7x8HF "TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN >= 128")
-  (VNx8x8HF "TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN >= 128")
-  (VNx2x4HF "TARGET_VECTOR_ELEN_FP_16")
-  (VNx3x4HF "TARGET_VECTOR_ELEN_FP_16")
-  (VNx4x4HF "TARGET_VECTOR_ELEN_FP_16")
-  (VNx5x4HF "TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN >= 64")
-  (VNx6x4HF "TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN >= 64")
-  (VNx7x4HF "TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN >= 64")
-  (VNx8x4HF "TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN >= 64")
-  (VNx2x2HF "TARGET_VECTOR_ELEN_FP_16")
-  (VNx3x2HF "TARGET_VECTOR_ELEN_FP_16")
-  (VNx4x2HF "TARGET_VECTOR_ELEN_FP_16")
-  (VNx5x2HF "TARGET_VECTOR_ELEN_FP_16")
-  (VNx6x2HF "TARGET_VECTOR_ELEN_FP_16")
-  (VNx7x2HF "TARGET_VECTOR_ELEN_FP_16")
-  (VNx8x2HF "TARGET_VECTOR_ELEN_FP_16")
-  (VNx2x1HF "TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN < 128")
-  (VNx3x1HF "TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN < 128")
-  (VNx4x1HF "TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN < 128")
-  (VNx5x1HF "TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN < 128")
-  (VNx6x1HF "TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN < 128")
-  (VNx7x1HF "TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN < 128")
-  (VNx8x1HF "TARGET_VECTOR_ELEN_FP_16 && TARGET_MIN_VLEN < 128")
   (VNx2x16SF "TARGET_VECTOR_ELEN_FP_32 && TARGET_MIN_VLEN > 32")
   (VNx2x8SF "TARGET_VECTOR_ELEN_FP_32 && TARGET_MIN_VLEN >= 64")
   (VNx3x8SF "TARGET_VECTOR_ELEN_FP_32 && TARGET_MIN_VLEN >= 128")
@@ -1154,11 +1122,6 @@
   (VNx2x8DI "VNx8BI") (VNx2x4DI "VNx4BI") (VNx3x4DI "VNx4BI") (VNx4x4DI "VNx4BI")
   (VNx2x2DI "VNx2BI") (VNx3x2DI "VNx2BI") (VNx4x2DI "VNx2BI") (VNx5x2DI "VNx2BI") (VNx6x2DI "VNx2BI") (VNx7x2DI "VNx2BI") (VNx8x2DI "VNx2BI")
   (VNx2x1DI "VNx1BI") (VNx3x1DI "VNx1BI") (VNx4x1DI "VNx1BI") (VNx5x1DI "VNx1BI") (VNx6x1DI "VNx1BI") (VNx7x1DI "VNx1BI") (VNx8x1DI "VNx1BI")
-  (VNx2x32HF "VNx32BI") (VNx2x16HF "VNx16BI") (VNx3x16HF "VNx16BI") (VNx4x16HF "VNx16BI")
-  (VNx2x8HF "VNx8BI") (VNx3x8HF "VNx8BI") (VNx4x8HF "VNx8BI") (VNx5x8HF "VNx8BI") (VNx6x8HF "VNx8BI") (VNx7x8HF "VNx8BI") (VNx8x8HF "VNx8BI")
-  (VNx2x4HF "VNx4BI") (VNx3x4HF "VNx4BI") (VNx4x4HF "VNx4BI") (VNx5x4HF "VNx4BI") (VNx6x4HF "VNx4BI") (VNx7x4HF "VNx4BI") (VNx8x4HF "VNx4BI")
-  (VNx2x2HF "VNx2BI") (VNx3x2HF "VNx2BI") (VNx4x2HF "VNx2BI") (VNx5x2HF "VNx2BI") (VNx6x2HF "VNx2BI") (VNx7x2HF "VNx2BI") (VNx8x2HF "VNx2BI")
-  (VNx2x1HF "VNx1BI") (VNx3x1HF "VNx1BI") (VNx4x1HF "VNx1BI") (VNx5x1HF "VNx1BI") (VNx6x1HF "VNx1BI") (VNx7x1HF "VNx1BI") (VNx8x1HF "VNx1BI")
   (VNx2x16SF "VNx16BI") (VNx2x8SF "VNx8BI") (VNx3x8SF "VNx8BI") (VNx4x8SF "VNx8BI")
   (VNx2x4SF "VNx4BI") (VNx3x4SF "VNx4BI") (VNx4x4SF "VNx4BI") (VNx5x4SF "VNx4BI") (VNx6x4SF "VNx4BI") (VNx7x4SF "VNx4BI") (VNx8x4SF "VNx4BI")
   (VNx2x2SF "VNx2BI") (VNx3x2SF "VNx2BI") (VNx4x2SF "VNx2BI") (VNx5x2SF "VNx2BI") (VNx6x2SF "VNx2BI") (VNx7x2SF "VNx2BI") (VNx8x2SF "VNx2BI")
diff --git a/gcc/config/riscv/vector.md b/gcc/config/riscv/vector.md
index 884e7435..674e602 100644
--- a/gcc/config/riscv/vector.md
+++ b/gcc/config/riscv/vector.md
@@ -425,14 +425,14 @@
 	 (eq_attr "type" "vldux,vldox,vialu,vshift,viminmax,vimul,vidiv,vsalu,\
 			  viwalu,viwmul,vnshift,vaalu,vsmul,vsshift,\
 			  vnclip,vicmp,vfalu,vfmul,vfminmax,vfdiv,vfwalu,vfwmul,\
-			  vfsgnj,vfcmp,vfmuladd,vslideup,vslidedown,vislide1up,\
+			  vfsgnj,vfcmp,vslideup,vslidedown,vislide1up,\
 			  vislide1down,vfslide1up,vfslide1down,vgather,viwmuladd,vfwmuladd,\
 			  vlsegds,vlsegdux,vlsegdox")
 	   (symbol_ref "INTVAL (operands[8])")
 	 (eq_attr "type" "vstux,vstox,vssegts,vssegtux,vssegtox")
 	   (symbol_ref "INTVAL (operands[5])")
 
-	 (eq_attr "type" "vimuladd")
+	 (eq_attr "type" "vimuladd,vfmuladd")
 	   (symbol_ref "INTVAL (operands[9])")
 
 	 (eq_attr "type" "vmsfs,vmidx,vcompress")
@@ -1063,6 +1063,7 @@
 	  (unspec:<VM>
 	    [(match_operand:<VM> 1 "vector_mask_operand" "vmWc1")
 	     (match_operand 3 "vector_length_operand"    "   rK")
+	     (match_operand 4 "const_int_operand"        "    i")
 	     (reg:SI VL_REGNUM)
 	     (reg:SI VTYPE_REGNUM)] UNSPEC_VPREDICATE)
 	  (match_operand:V 2 "register_operand"         "    vr")
@@ -1071,7 +1072,7 @@
   "vse<sew>.v\t%2,%0%p1"
   [(set_attr "type" "vste")
    (set_attr "mode" "<MODE>")
-   (set (attr "avl_type") (symbol_ref "riscv_vector::NONVLMAX"))
+   (set (attr "avl_type") (symbol_ref "INTVAL (operands[4])"))
    (set_attr "vl_op_idx" "3")])
 
 ;; vlm.v/vsm.v/vmclr.m/vmset.m.
@@ -1113,6 +1114,7 @@
 	  (unspec:VB
 	    [(match_operand:VB 1 "vector_all_trues_mask_operand" "Wc1")
 	     (match_operand 3 "vector_length_operand"            " rK")
+	     (match_operand 4 "const_int_operand"                "  i")
 	     (reg:SI VL_REGNUM)
 	     (reg:SI VTYPE_REGNUM)] UNSPEC_VPREDICATE)
 	  (match_operand:VB 2 "register_operand"                 " vr")
@@ -1121,7 +1123,7 @@
   "vsm.v\t%2,%0"
   [(set_attr "type" "vstm")
    (set_attr "mode" "<MODE>")
-   (set (attr "avl_type") (symbol_ref "riscv_vector::NONVLMAX"))
+   (set (attr "avl_type") (symbol_ref "INTVAL (operands[4])"))
    (set_attr "vl_op_idx" "3")])
 
 (define_insn "@pred_merge<mode>"
@@ -1433,6 +1435,7 @@
 	  (unspec:<VM>
 	    [(match_operand:<VM> 1 "vector_mask_operand" "vmWc1")
 	     (match_operand 4 "vector_length_operand"    "   rK")
+	     (match_operand 5 "const_int_operand"        "    i")
 	     (reg:SI VL_REGNUM)
 	     (reg:SI VTYPE_REGNUM)] UNSPEC_VPREDICATE)
 	  (unspec:V
@@ -1442,7 +1445,8 @@
   "TARGET_VECTOR"
   "vsse<sew>.v\t%3,%0,%z2%p1"
   [(set_attr "type" "vsts")
-   (set_attr "mode" "<MODE>")])
+   (set_attr "mode" "<MODE>")
+   (set (attr "avl_type") (symbol_ref "INTVAL (operands[5])"))])
 
 ;; -------------------------------------------------------------------------------
 ;; ---- Predicated indexed loads/stores
diff --git a/gcc/config/rs6000/fusion.md b/gcc/config/rs6000/fusion.md
index d45fb13..e286bf5 100644
--- a/gcc/config/rs6000/fusion.md
+++ b/gcc/config/rs6000/fusion.md
@@ -22,7 +22,7 @@
 ;; load mode is DI result mode is clobber compare mode is CC extend is none
 (define_insn_and_split "*ld_cmpdi_cr0_DI_clobber_CC_none"
   [(set (match_operand:CC 2 "cc_reg_operand" "=x")
-        (compare:CC (match_operand:DI 1 "ds_form_mem_operand" "m")
+        (compare:CC (match_operand:DI 1 "non_update_memory_operand" "YZ")
                     (match_operand:DI 3 "const_m1_to_1_operand" "n")))
    (clobber (match_scratch:DI 0 "=r"))]
   "(TARGET_P10_FUSION)"
@@ -43,7 +43,7 @@
 ;; load mode is DI result mode is clobber compare mode is CCUNS extend is none
 (define_insn_and_split "*ld_cmpldi_cr0_DI_clobber_CCUNS_none"
   [(set (match_operand:CCUNS 2 "cc_reg_operand" "=x")
-        (compare:CCUNS (match_operand:DI 1 "ds_form_mem_operand" "m")
+        (compare:CCUNS (match_operand:DI 1 "non_update_memory_operand" "YZ")
                        (match_operand:DI 3 "const_0_to_1_operand" "n")))
    (clobber (match_scratch:DI 0 "=r"))]
   "(TARGET_P10_FUSION)"
@@ -64,7 +64,7 @@
 ;; load mode is DI result mode is DI compare mode is CC extend is none
 (define_insn_and_split "*ld_cmpdi_cr0_DI_DI_CC_none"
   [(set (match_operand:CC 2 "cc_reg_operand" "=x")
-        (compare:CC (match_operand:DI 1 "ds_form_mem_operand" "m")
+        (compare:CC (match_operand:DI 1 "non_update_memory_operand" "YZ")
                     (match_operand:DI 3 "const_m1_to_1_operand" "n")))
    (set (match_operand:DI 0 "gpc_reg_operand" "=r") (match_dup 1))]
   "(TARGET_P10_FUSION)"
@@ -85,7 +85,7 @@
 ;; load mode is DI result mode is DI compare mode is CCUNS extend is none
 (define_insn_and_split "*ld_cmpldi_cr0_DI_DI_CCUNS_none"
   [(set (match_operand:CCUNS 2 "cc_reg_operand" "=x")
-        (compare:CCUNS (match_operand:DI 1 "ds_form_mem_operand" "m")
+        (compare:CCUNS (match_operand:DI 1 "non_update_memory_operand" "YZ")
                        (match_operand:DI 3 "const_0_to_1_operand" "n")))
    (set (match_operand:DI 0 "gpc_reg_operand" "=r") (match_dup 1))]
   "(TARGET_P10_FUSION)"
@@ -104,17 +104,17 @@
 
 ;; load-cmpi fusion pattern generated by gen_ld_cmpi_p10
 ;; load mode is SI result mode is clobber compare mode is CC extend is none
-(define_insn_and_split "*lwa_cmpdi_cr0_SI_clobber_CC_none"
+(define_insn_and_split "*lwz_cmpwi_cr0_SI_clobber_CC_none"
   [(set (match_operand:CC 2 "cc_reg_operand" "=x")
-        (compare:CC (match_operand:SI 1 "ds_form_mem_operand" "m")
+        (compare:CC (match_operand:SI 1 "non_update_memory_operand" "m")
                     (match_operand:SI 3 "const_m1_to_1_operand" "n")))
    (clobber (match_scratch:SI 0 "=r"))]
   "(TARGET_P10_FUSION)"
-  "lwa%X1 %0,%1\;cmpdi %2,%0,%3"
+  "lwz%X1 %0,%1\;cmpwi %2,%0,%3"
   "&& reload_completed
    && (cc_reg_not_cr0_operand (operands[2], CCmode)
        || !address_is_non_pfx_d_or_x (XEXP (operands[1], 0),
-                                      SImode, NON_PREFIXED_DS))"
+                                      SImode, NON_PREFIXED_D))"
   [(set (match_dup 0) (match_dup 1))
    (set (match_dup 2)
         (compare:CC (match_dup 0) (match_dup 3)))]
@@ -146,17 +146,17 @@
 
 ;; load-cmpi fusion pattern generated by gen_ld_cmpi_p10
 ;; load mode is SI result mode is SI compare mode is CC extend is none
-(define_insn_and_split "*lwa_cmpdi_cr0_SI_SI_CC_none"
+(define_insn_and_split "*lwz_cmpwi_cr0_SI_SI_CC_none"
   [(set (match_operand:CC 2 "cc_reg_operand" "=x")
-        (compare:CC (match_operand:SI 1 "ds_form_mem_operand" "m")
+        (compare:CC (match_operand:SI 1 "non_update_memory_operand" "m")
                     (match_operand:SI 3 "const_m1_to_1_operand" "n")))
    (set (match_operand:SI 0 "gpc_reg_operand" "=r") (match_dup 1))]
   "(TARGET_P10_FUSION)"
-  "lwa%X1 %0,%1\;cmpdi %2,%0,%3"
+  "lwz%X1 %0,%1\;cmpwi %2,%0,%3"
   "&& reload_completed
    && (cc_reg_not_cr0_operand (operands[2], CCmode)
        || !address_is_non_pfx_d_or_x (XEXP (operands[1], 0),
-                                      SImode, NON_PREFIXED_DS))"
+                                      SImode, NON_PREFIXED_D))"
   [(set (match_dup 0) (match_dup 1))
    (set (match_dup 2)
         (compare:CC (match_dup 0) (match_dup 3)))]
@@ -190,7 +190,7 @@
 ;; load mode is SI result mode is EXTSI compare mode is CC extend is sign
 (define_insn_and_split "*lwa_cmpdi_cr0_SI_EXTSI_CC_sign"
   [(set (match_operand:CC 2 "cc_reg_operand" "=x")
-        (compare:CC (match_operand:SI 1 "ds_form_mem_operand" "m")
+        (compare:CC (match_operand:SI 1 "non_update_memory_operand" "YZ")
                     (match_operand:SI 3 "const_m1_to_1_operand" "n")))
    (set (match_operand:EXTSI 0 "gpc_reg_operand" "=r") (sign_extend:EXTSI (match_dup 1)))]
   "(TARGET_P10_FUSION)"
@@ -205,6 +205,7 @@
   ""
   [(set_attr "type" "fused_load_cmpi")
    (set_attr "cost" "8")
+   (set_attr "sign_extend" "yes")
    (set_attr "length" "8")])
 
 ;; load-cmpi fusion pattern generated by gen_ld_cmpi_p10
diff --git a/gcc/config/rs6000/genfusion.pl b/gcc/config/rs6000/genfusion.pl
index 82e8f86..4d1f825 100755
--- a/gcc/config/rs6000/genfusion.pl
+++ b/gcc/config/rs6000/genfusion.pl
@@ -61,20 +61,31 @@
   my $mempred = "non_update_memory_operand";
   my $extend;
 
+  # We need to special case lwa.  The prefixed_load_p function in rs6000.cc
+  # (which determines if a load instruction is prefixed) uses the fact that the
+  # register mode is different from the memory mode, and that the sign_extend
+  # attribute is set to use DS-form rules for the address instead of D-form.
+  # If the register size is the same, prefixed_load_p assumes we are doing a
+  # lwz.  We change to use an lwz and word compare if we don't need to sign
+  # extend the SImode value.  Otherwise if we need the value, we need to
+  # make sure the insn is marked as ds-form.
+  my $cmp_size_char = ($lmode eq "SI"
+		       && $ccmode eq "CC"
+		       && $result !~ /^EXT|^DI$/) ? "w" : "d";
+
   if ($ccmode eq "CC") {
     # ld and lwa are both DS-FORM.
-    ($lmode =~ /^[SD]I$/) and $np = "NON_PREFIXED_DS";
-    ($lmode =~ /^[SD]I$/) and $mempred = "ds_form_mem_operand";
+    ($lmode eq "DI") and $np = "NON_PREFIXED_DS";
+    ($lmode eq "SI" && $cmp_size_char eq "d") and $np = "NON_PREFIXED_DS";
   } else {
     if ($lmode eq "DI") {
       # ld is DS-form, but lwz is not.
       $np = "NON_PREFIXED_DS";
-      $mempred = "ds_form_mem_operand";
     }
   }
 
   my $cmpl = ($ccmode eq "CC") ? "" : "l";
-  my $echr = ($ccmode eq "CC") ? "a" : "z";
+  my $echr = ($ccmode eq "CC" && $cmp_size_char eq "d") ? "a" : "z";
   if ($lmode eq "DI") { $echr = ""; }
   my $constpred = ($ccmode eq "CC") ? "const_m1_to_1_operand"
   				    : "const_0_to_1_operand";
@@ -91,12 +102,15 @@
   }
 
   my $ldst = mode_to_ldst_char($lmode);
+
+  # DS-form addresses need YZ, and not m.
+  my $constraint = ($np eq "NON_PREFIXED_DS") ? "YZ" : "m";
   print <<HERE;
 ;; load-cmpi fusion pattern generated by gen_ld_cmpi_p10
 ;; load mode is $lmode result mode is $result compare mode is $ccmode extend is $extend
-(define_insn_and_split "*l${ldst}${echr}_cmp${cmpl}di_cr0_${lmode}_${result}_${ccmode}_${extend}"
+(define_insn_and_split "*l${ldst}${echr}_cmp${cmpl}${cmp_size_char}i_cr0_${lmode}_${result}_${ccmode}_${extend}"
   [(set (match_operand:${ccmode} 2 "cc_reg_operand" "=x")
-        (compare:${ccmode} (match_operand:${lmode} 1 "${mempred}" "m")
+        (compare:${ccmode} (match_operand:${lmode} 1 "${mempred}" "${constraint}")
 HERE
   print "   " if $ccmode eq "CCUNS";
 print <<HERE;
@@ -119,7 +133,7 @@
 
   print <<HERE;
   "(TARGET_P10_FUSION)"
-  "l${ldst}${echr}%X1 %0,%1\\;cmp${cmpl}di %2,%0,%3"
+  "l${ldst}${echr}%X1 %0,%1\\;cmp${cmpl}${cmp_size_char}i %2,%0,%3"
   "&& reload_completed
    && (cc_reg_not_cr0_operand (operands[2], CCmode)
        || !address_is_non_pfx_d_or_x (XEXP (operands[1], 0),
@@ -140,6 +154,15 @@
   ""
   [(set_attr "type" "fused_load_cmpi")
    (set_attr "cost" "8")
+HERE
+
+  if ($lmode eq "SI" && $ccmode eq "CC" && $cmp_size_char eq "d") {
+    # prefixed_load_p needs the sign_extend attribute to validate lwa as a
+    # DS-form instruction instead of D-form.
+    print "   (set_attr \"sign_extend\" \"yes\")\n";
+  }
+
+  print <<HERE
    (set_attr "length" "8")])
 
 HERE
diff --git a/gcc/config/rs6000/predicates.md b/gcc/config/rs6000/predicates.md
index a16ee30..6b56483 100644
--- a/gcc/config/rs6000/predicates.md
+++ b/gcc/config/rs6000/predicates.md
@@ -1125,20 +1125,6 @@
   return INTVAL (offset) % 4 == 0;
 })
 
-;; Return 1 if the operand is a memory operand that has a valid address for
-;; a DS-form instruction. I.e. the address has to be either just a register,
-;; or register + const where the two low order bits of const are zero.
-(define_predicate "ds_form_mem_operand"
-  (match_code "subreg,mem")
-{
-  if (!any_memory_operand (op, mode))
-    return false;
-
-  rtx addr = XEXP (op, 0);
-
-  return address_to_insn_form (addr, mode, NON_PREFIXED_DS) == INSN_FORM_DS;
-})
-
 ;; Return 1 if the operand, used inside a MEM, is a SYMBOL_REF.
 (define_predicate "symbol_ref_operand"
   (and (match_code "symbol_ref")
diff --git a/gcc/config/rs6000/rs6000.md b/gcc/config/rs6000/rs6000.md
index b0db8ae..75c5e5f 100644
--- a/gcc/config/rs6000/rs6000.md
+++ b/gcc/config/rs6000/rs6000.md
@@ -287,7 +287,7 @@
 ;; Whether this insn has a prefixed form and a non-prefixed form.
 (define_attr "maybe_prefixed" "no,yes"
   (if_then_else (eq_attr "type" "load,fpload,vecload,store,fpstore,vecstore,
-  				 integer,add")
+  				 integer,add,fused_load_cmpi")
 		(const_string "yes")
 		(const_string "no")))
 
@@ -302,7 +302,7 @@
 	      (eq_attr "maybe_prefixed" "no"))
 	 (const_string "no")
 
-	 (eq_attr "type" "load,fpload,vecload")
+	 (eq_attr "type" "load,fpload,vecload,fused_load_cmpi")
 	 (if_then_else (match_test "prefixed_load_p (insn)")
 		       (const_string "yes")
 		       (const_string "no"))
diff --git a/gcc/config/s390/s390.cc b/gcc/config/s390/s390.cc
index 9284477..d9f10542 100644
--- a/gcc/config/s390/s390.cc
+++ b/gcc/config/s390/s390.cc
@@ -13706,8 +13706,10 @@
     {
       /* Store the alignment to be able to check if we can use
 	 a larl/load-relative instruction.  We only handle the cases
-	 that can go wrong (i.e. no FUNC_DECLs).  */
-      if (DECL_ALIGN (decl) == 0 || DECL_ALIGN (decl) % 16)
+	 that can go wrong (i.e. no FUNC_DECLs).
+	 All symbols without an explicit alignment are assumed to be 2
+	 byte aligned as mandated by our ABI.  */
+      if (DECL_USER_ALIGN (decl) && DECL_ALIGN (decl) % 16)
 	SYMBOL_FLAG_SET_NOTALIGN2 (XEXP (rtl, 0));
       else if (DECL_ALIGN (decl) % 32)
 	SYMBOL_FLAG_SET_NOTALIGN4 (XEXP (rtl, 0));
diff --git a/gcc/config/sh/sh.md b/gcc/config/sh/sh.md
index 4622dba..5cb1795 100644
--- a/gcc/config/sh/sh.md
+++ b/gcc/config/sh/sh.md
@@ -9269,7 +9269,7 @@
 		 (match_operand:SF 3 "arith_reg_operand" "0")))
    (clobber (reg:SI FPSCR_STAT_REG))
    (use (reg:SI FPSCR_MODES_REG))]
-  "TARGET_SH2E && flag_fp_contract_mode != FP_CONTRACT_OFF"
+  "TARGET_SH2E && flag_fp_contract_mode == FP_CONTRACT_FAST"
   "fmac	%1,%2,%0"
   "&& can_create_pseudo_p ()"
   [(parallel [(set (match_dup 0)
diff --git a/gcc/configure b/gcc/configure
index f7b4b28..496da71 100755
--- a/gcc/configure
+++ b/gcc/configure
@@ -635,6 +635,7 @@
 LD_PICFLAG
 PICFLAG
 enable_default_pie
+enable_host_bind_now
 enable_host_pie
 enable_host_shared
 enable_plugin
@@ -1031,6 +1032,7 @@
 enable_plugin
 enable_host_shared
 enable_host_pie
+enable_host_bind_now
 enable_libquadmath_support
 with_linker_hash_style
 with_diagnostics_color
@@ -1794,6 +1796,7 @@
   --enable-plugin         enable plugin support
   --enable-host-shared    build host code as shared libraries
   --enable-host-pie       build host code as PIE
+  --enable-host-bind-now  link host code as BIND_NOW
   --disable-libquadmath-support
                           disable libquadmath support for Fortran
   --enable-default-pie    enable Position Independent Executable as default
@@ -19847,7 +19850,7 @@
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 19850 "configure"
+#line 19853 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -19953,7 +19956,7 @@
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 19956 "configure"
+#line 19959 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -32100,6 +32103,14 @@
 
 
 
+# Enable --enable-host-bind-now
+# Check whether --enable-host-bind-now was given.
+if test "${enable_host_bind_now+set}" = set; then :
+  enableval=$enable_host_bind_now;
+fi
+
+
+
 # Check whether --enable-libquadmath-support was given.
 if test "${enable_libquadmath_support+set}" = set; then :
   enableval=$enable_libquadmath_support; ENABLE_LIBQUADMATH_SUPPORT=$enableval
@@ -32286,6 +32297,8 @@
   PICFLAG=
 fi
 
+
+
 if test x$enable_host_pie = xyes; then
   LD_PICFLAG=-pie
 elif test x$gcc_cv_no_pie = xyes; then
@@ -32294,6 +32307,9 @@
   LD_PICFLAG=
 fi
 
+if test x$enable_host_bind_now = xyes; then
+  LD_PICFLAG="$LD_PICFLAG -Wl,-z,now"
+fi
 
 
 
@@ -34009,7 +34025,7 @@
     "depdir":C) $SHELL $ac_aux_dir/mkinstalldirs $DEPDIR ;;
     "gccdepdir":C)
   ${CONFIG_SHELL-/bin/sh} $ac_aux_dir/mkinstalldirs build/$DEPDIR
-  for lang in $subdirs c-family common analyzer rtl-ssa
+  for lang in $subdirs c-family common analyzer text-art rtl-ssa
   do
       ${CONFIG_SHELL-/bin/sh} $ac_aux_dir/mkinstalldirs $lang/$DEPDIR
   done ;;
diff --git a/gcc/configure.ac b/gcc/configure.ac
index 9c680ec..e91073b 100644
--- a/gcc/configure.ac
+++ b/gcc/configure.ac
@@ -1382,7 +1382,7 @@
 ZW_CREATE_DEPDIR
 AC_CONFIG_COMMANDS([gccdepdir],[
   ${CONFIG_SHELL-/bin/sh} $ac_aux_dir/mkinstalldirs build/$DEPDIR
-  for lang in $subdirs c-family common analyzer rtl-ssa
+  for lang in $subdirs c-family common analyzer text-art rtl-ssa
   do
       ${CONFIG_SHELL-/bin/sh} $ac_aux_dir/mkinstalldirs $lang/$DEPDIR
   done], [subdirs="$subdirs" ac_aux_dir=$ac_aux_dir DEPDIR=$DEPDIR])
@@ -7425,6 +7425,12 @@
 		[build host code as PIE])])
 AC_SUBST(enable_host_pie)
 
+# Enable --enable-host-bind-now
+AC_ARG_ENABLE(host-bind-now,
+[AS_HELP_STRING([--enable-host-bind-now],
+		[link host code as BIND_NOW])])
+AC_SUBST(enable_host_bind_now)
+
 AC_ARG_ENABLE(libquadmath-support,
 [AS_HELP_STRING([--disable-libquadmath-support],
   [disable libquadmath support for Fortran])],
@@ -7566,6 +7572,8 @@
   PICFLAG=
 fi
 
+AC_SUBST([PICFLAG])
+
 if test x$enable_host_pie = xyes; then
   LD_PICFLAG=-pie
 elif test x$gcc_cv_no_pie = xyes; then
@@ -7574,7 +7582,10 @@
   LD_PICFLAG=
 fi
 
-AC_SUBST([PICFLAG])
+if test x$enable_host_bind_now = xyes; then
+  LD_PICFLAG="$LD_PICFLAG -Wl,-z,now"
+fi
+
 AC_SUBST([LD_PICFLAG])
 
 # Enable Intel CET on Intel CET enabled host if jit is enabled.
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 7230564..280e373 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,12 @@
+2023-06-23  David Malcolm  <dmalcolm@redhat.com>
+
+	PR c++/110164
+	* cp-name-hint.h (maybe_suggest_missing_header): New decl.
+	* decl.cc: Define INCLUDE_MEMORY.  Add include of
+	"cp/cp-name-hint.h".
+	(start_decl_1): Call maybe_suggest_missing_header.
+	* name-lookup.cc (maybe_suggest_missing_header): Remove "static".
+
 2023-06-16  Alex Coplan  <alex.coplan@arm.com>
 
 	* parser.cc (cp_parser_enum_specifier): Don't reject
diff --git a/gcc/cp/cp-name-hint.h b/gcc/cp/cp-name-hint.h
index bfa7c53..7693980 100644
--- a/gcc/cp/cp-name-hint.h
+++ b/gcc/cp/cp-name-hint.h
@@ -32,6 +32,7 @@
 
 extern name_hint suggest_alternatives_for (location_t, tree, bool);
 extern name_hint suggest_alternatives_in_other_namespaces (location_t, tree);
+extern name_hint maybe_suggest_missing_header (location_t, tree, tree);
 extern name_hint suggest_alternative_in_explicit_scope (location_t, tree, tree);
 extern name_hint suggest_alternative_in_scoped_enum (tree, tree);
 
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index c07a4a8..60f107d 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -27,6 +27,7 @@
    line numbers.  For example, the CONST_DECLs for enum values.  */
 
 #include "config.h"
+#define INCLUDE_MEMORY
 #include "system.h"
 #include "coretypes.h"
 #include "target.h"
@@ -46,6 +47,7 @@
 #include "c-family/c-objc.h"
 #include "c-family/c-pragma.h"
 #include "c-family/c-ubsan.h"
+#include "cp/cp-name-hint.h"
 #include "debug.h"
 #include "plugin.h"
 #include "builtins.h"
@@ -5995,7 +5997,11 @@
 	; 			/* An auto type is ok.  */
       else if (TREE_CODE (type) != ARRAY_TYPE)
 	{
+	  auto_diagnostic_group d;
 	  error ("variable %q#D has initializer but incomplete type", decl);
+	  maybe_suggest_missing_header (input_location,
+					TYPE_IDENTIFIER (type),
+					CP_TYPE_CONTEXT (type));
 	  type = TREE_TYPE (decl) = error_mark_node;
 	}
       else if (!COMPLETE_TYPE_P (complete_type (TREE_TYPE (type))))
@@ -6011,8 +6017,12 @@
 	gcc_assert (CLASS_PLACEHOLDER_TEMPLATE (type));
       else
 	{
+	  auto_diagnostic_group d;
 	  error ("aggregate %q#D has incomplete type and cannot be defined",
 		 decl);
+	  maybe_suggest_missing_header (input_location,
+					TYPE_IDENTIFIER (type),
+					CP_TYPE_CONTEXT (type));
 	  /* Change the type so that assemble_variable will give
 	     DECL an rtl we can live with: (mem (const_int 0)).  */
 	  type = TREE_TYPE (decl) = error_mark_node;
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index 53b6870..7456518 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -6796,7 +6796,7 @@
    for NAME within SCOPE at LOCATION, or an empty name_hint if this isn't
    applicable.  */
 
-static name_hint
+name_hint
 maybe_suggest_missing_header (location_t location, tree name, tree scope)
 {
   if (scope == NULL_TREE)
diff --git a/gcc/d/ChangeLog b/gcc/d/ChangeLog
index 8748283..add8688 100644
--- a/gcc/d/ChangeLog
+++ b/gcc/d/ChangeLog
@@ -1,3 +1,8 @@
+2023-06-25  Iain Buclaw  <ibuclaw@gdcproject.org>
+
+	* dmd/MERGE: Merge upstream dmd a45f4e9f43.
+	* dmd/VERSION: Bump version to v2.103.1.
+
 2023-06-15  Marek Polacek  <polacek@redhat.com>
 
 	* Make-lang.in: Remove NO_PIE_CFLAGS.
diff --git a/gcc/d/d-convert.cc b/gcc/d/d-convert.cc
index cdbd69c..2b9d8e7 100644
--- a/gcc/d/d-convert.cc
+++ b/gcc/d/d-convert.cc
@@ -619,7 +619,7 @@
   return result ? result : convert (build_ctype (totype), exp);
 }
 
-/* Return a TREE represenwation of EXPR, whose type has been converted from
+/* Return a TREE representation of EXPR, whose type has been converted from
  * ETYPE to TOTYPE, and is being used in an rvalue context.  */
 
 tree
@@ -634,20 +634,27 @@
     {
       /* If casting from bool, the result is either 0 or 1, any other value
 	 violates @safe code, so enforce that it is never invalid.  */
-      if (CONSTANT_CLASS_P (expr))
-	result = d_truthvalue_conversion (expr);
-      else
+      for (tree ref = expr; TREE_CODE (ref) == COMPONENT_REF;
+	   ref = TREE_OPERAND (ref, 0))
 	{
-	  /* Reinterpret the boolean as an integer and test the first bit.
-	     The generated code should end up being equivalent to:
+	  /* If the expression is a field that's part of a union, reinterpret
+	     the boolean as an integer and test the first bit.  The generated
+	     code should end up being equivalent to:
 		*cast(ubyte *)&expr & 1;  */
-	  machine_mode bool_mode = TYPE_MODE (TREE_TYPE (expr));
-	  tree mtype = lang_hooks.types.type_for_mode (bool_mode, 1);
-	  result = fold_build2 (BIT_AND_EXPR, mtype,
-				build_vconvert (mtype, expr),
-				build_one_cst (mtype));
+	  if (TREE_CODE (TREE_TYPE (TREE_OPERAND (ref, 0))) == UNION_TYPE)
+	    {
+	      machine_mode bool_mode = TYPE_MODE (TREE_TYPE (expr));
+	      tree mtype = lang_hooks.types.type_for_mode (bool_mode, 1);
+	      result = fold_build2 (BIT_AND_EXPR, mtype,
+				    build_vconvert (mtype, expr),
+				    build_one_cst (mtype));
+	      break;
+	    }
 	}
 
+      if (result == NULL_TREE)
+	result = d_truthvalue_conversion (expr);
+
       result = convert (build_ctype (tbtype), result);
     }
 
@@ -844,7 +851,7 @@
       break;
 
     default:
-      result = expr;
+      result = convert_for_rvalue (expr, type, type);
       break;
     }
 
diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE
index 986925e..1205cd9 100644
--- a/gcc/d/dmd/MERGE
+++ b/gcc/d/dmd/MERGE
@@ -1,4 +1,4 @@
-5f7552bb2829b75d5e36cc767a476e1ab35147b7
+a45f4e9f43e9fdbf0b666175e5e66b1ce4f561f6
 
 The first line of this file holds the git revision number of the last
 merge done from the dlang/dmd repository.
diff --git a/gcc/d/dmd/VERSION b/gcc/d/dmd/VERSION
index da496a2..8316aaf 100644
--- a/gcc/d/dmd/VERSION
+++ b/gcc/d/dmd/VERSION
@@ -1 +1 @@
-v2.103.0-rc.1
+v2.103.1
diff --git a/gcc/d/dmd/aggregate.h b/gcc/d/dmd/aggregate.h
index 04e5eb2..03fe478 100644
--- a/gcc/d/dmd/aggregate.h
+++ b/gcc/d/dmd/aggregate.h
@@ -108,8 +108,8 @@
     Expression *getRTInfo;      // pointer to GC info generated by object.RTInfo(this)
 
     Visibility visibility;
-    bool noDefaultCtor;         // no default construction
-    bool disableNew;            // disallow allocations using `new`
+    d_bool noDefaultCtor;         // no default construction
+    d_bool disableNew;            // disallow allocations using `new`
     Sizeok sizeok;              // set when structsize contains valid data
 
     virtual Scope *newScope(Scope *sc);
@@ -269,10 +269,10 @@
                                         // their own vtbl[]
 
     TypeInfoClassDeclaration *vclassinfo;       // the ClassInfo object for this ClassDeclaration
-    bool com;                           // true if this is a COM class (meaning it derives from IUnknown)
-    bool stack;                         // true if this is a scope class
+    d_bool com;                           // true if this is a COM class (meaning it derives from IUnknown)
+    d_bool stack;                         // true if this is a scope class
     int cppDtorVtblIndex;               // slot reserved for the virtual destructor [extern(C++)]
-    bool inuse;                         // to prevent recursive attempts
+    d_bool inuse;                         // to prevent recursive attempts
 
     ThreeState isabstract;              // if abstract class
     Baseok baseok;                      // set the progress of base classes resolving
diff --git a/gcc/d/dmd/attrib.h b/gcc/d/dmd/attrib.h
index 44ceb12..113653e 100644
--- a/gcc/d/dmd/attrib.h
+++ b/gcc/d/dmd/attrib.h
@@ -132,7 +132,7 @@
 class AnonDeclaration final : public AttribDeclaration
 {
 public:
-    bool isunion;
+    d_bool isunion;
     int sem;                    // 1 if successful semantic()
     unsigned anonoffset;        // offset of anonymous struct
     unsigned anonstructsize;    // size of anonymous struct
@@ -175,8 +175,8 @@
 {
 public:
     ScopeDsymbol *scopesym;
-    bool addisdone;
-    bool onStack;
+    d_bool addisdone;
+    d_bool onStack;
 
     StaticIfDeclaration *syntaxCopy(Dsymbol *s) override;
     Dsymbols *include(Scope *sc) override;
@@ -193,8 +193,8 @@
 public:
     StaticForeach *sfe;
     ScopeDsymbol *scopesym;
-    bool onStack;
-    bool cached;
+    d_bool onStack;
+    d_bool cached;
     Dsymbols *cache;
 
     StaticForeachDeclaration *syntaxCopy(Dsymbol *s) override;
@@ -227,7 +227,7 @@
     Expressions *exps;
 
     ScopeDsymbol *scopesym;
-    bool compiled;
+    d_bool compiled;
 
     CompileDeclaration *syntaxCopy(Dsymbol *s) override;
     void addMember(Scope *sc, ScopeDsymbol *sds) override;
diff --git a/gcc/d/dmd/common/outbuffer.h b/gcc/d/dmd/common/outbuffer.h
index b672842..4c1dcee 100644
--- a/gcc/d/dmd/common/outbuffer.h
+++ b/gcc/d/dmd/common/outbuffer.h
@@ -21,11 +21,11 @@
 private:
     DArray<unsigned char> data;
     d_size_t offset;
-    bool notlinehead;
+    d_bool notlinehead;
     void *fileMapping;  // pointer to a file mapping object not used on the C++ side
 public:
-    bool doindent;
-    bool spaces;
+    d_bool doindent;
+    d_bool spaces;
     int level;
 
     OutBuffer()
diff --git a/gcc/d/dmd/cond.d b/gcc/d/dmd/cond.d
index c0c4cf1..c40936c 100644
--- a/gcc/d/dmd/cond.d
+++ b/gcc/d/dmd/cond.d
@@ -935,9 +935,6 @@
             import dmd.staticcond;
             bool errors;
 
-            if (!exp)
-                return errorReturn();
-
             bool result = evalStaticCondition(sc, exp, exp, errors);
 
             // Prevent repeated condition evaluation.
diff --git a/gcc/d/dmd/cond.h b/gcc/d/dmd/cond.h
index 422a715..45094d1 100644
--- a/gcc/d/dmd/cond.h
+++ b/gcc/d/dmd/cond.h
@@ -52,7 +52,7 @@
     ForeachStatement *aggrfe;
     ForeachRangeStatement *rangefe;
 
-    bool needExpansion;
+    d_bool needExpansion;
 
     StaticForeach *syntaxCopy();
 };
diff --git a/gcc/d/dmd/cppmangle.d b/gcc/d/dmd/cppmangle.d
index b015a64..32b3851 100644
--- a/gcc/d/dmd/cppmangle.d
+++ b/gcc/d/dmd/cppmangle.d
@@ -213,6 +213,11 @@
     {
         auto tf = cast(TypeFunction)this.context.res.asFuncDecl().type;
         Type rt = preSemantic.nextOf();
+        // https://issues.dlang.org/show_bug.cgi?id=22739
+        // auto return type means that rt is null.
+        // if so, just pick up the type from the instance
+        if (!rt)
+            rt = tf.nextOf();
         if (tf.isref)
             rt = rt.referenceTo();
         auto prev = this.context.push(tf.nextOf());
@@ -560,7 +565,11 @@
                 foreach (j; i .. (*ti.tiargs).length)
                 {
                     Type t = isType((*ti.tiargs)[j]);
-                    assert(t);
+                    if (t is null)
+                    {
+                        ti.error("internal compiler error: C++ `%s` template value parameter is not supported", (*ti.tiargs)[j].toChars());
+                        fatal();
+                    }
                     t.accept(this);
                 }
 
diff --git a/gcc/d/dmd/declaration.h b/gcc/d/dmd/declaration.h
index cd4155d..d75f64f 100644
--- a/gcc/d/dmd/declaration.h
+++ b/gcc/d/dmd/declaration.h
@@ -167,8 +167,8 @@
 public:
     Objects *objects;
     TypeTuple *tupletype;       // !=NULL if this is a type tuple
-    bool isexp;                 // true: expression tuple
-    bool building;              // it's growing in AliasAssign semantic
+    d_bool isexp;                 // true: expression tuple
+    d_bool building;              // it's growing in AliasAssign semantic
 
     TupleDeclaration *syntaxCopy(Dsymbol *) override;
     const char *kind() const override;
@@ -607,7 +607,7 @@
 
     // set if someone took the address of this function
     int tookAddressOf;
-    bool requiresClosure;               // this function needs a closure
+    d_bool requiresClosure;               // this function needs a closure
 
     // local variables in this function which are referenced by nested functions
     VarDeclarations closureVars;
@@ -742,7 +742,7 @@
 {
 public:
     FuncDeclaration *funcalias;
-    bool hasOverloads;
+    d_bool hasOverloads;
 
     FuncAliasDeclaration *isFuncAliasDeclaration() override { return this; }
     const char *kind() const override;
@@ -758,7 +758,7 @@
     Type *treq;                         // target of return type inference
 
     // backend
-    bool deferToObj;
+    d_bool deferToObj;
 
     FuncLiteralDeclaration *syntaxCopy(Dsymbol *) override;
     bool isNested() const override;
@@ -778,7 +778,7 @@
 class CtorDeclaration final : public FuncDeclaration
 {
 public:
-    bool isCpCtor;
+    d_bool isCpCtor;
     CtorDeclaration *syntaxCopy(Dsymbol *) override;
     const char *kind() const override;
     const char *toChars() const override;
diff --git a/gcc/d/dmd/dsymbol.h b/gcc/d/dmd/dsymbol.h
index 1cee456..039a288 100644
--- a/gcc/d/dmd/dsymbol.h
+++ b/gcc/d/dmd/dsymbol.h
@@ -172,7 +172,7 @@
     unsigned fieldAlign;
     unsigned bitOffset;
 
-    bool inFlight;
+    d_bool inFlight;
 };
 
 struct DsymbolAttributes;
@@ -189,7 +189,7 @@
 private:
     DsymbolAttributes* atts;
 public:
-    bool errors;                // this symbol failed to pass semantic()
+    d_bool errors;                // this symbol failed to pass semantic()
     PASS semanticRun;
     unsigned short localNum;        // perturb mangled name to avoid collisions with those in FuncDeclaration.localsymtab
     static Dsymbol *create(Identifier *);
diff --git a/gcc/d/dmd/dsymbolsem.d b/gcc/d/dmd/dsymbolsem.d
index 6697ad6..0f0ed2a 100644
--- a/gcc/d/dmd/dsymbolsem.d
+++ b/gcc/d/dmd/dsymbolsem.d
@@ -1383,10 +1383,14 @@
         imp.semanticRun = PASS.semantic;
 
         // Load if not already done so
-        bool loadErrored = false;
         if (!imp.mod)
         {
-            loadErrored = imp.load(sc);
+            // https://issues.dlang.org/show_bug.cgi?id=22857
+            // if parser errors occur when loading a module
+            // we should just stop compilation
+            if (imp.load(sc))
+                return;
+
             if (imp.mod)
             {
                 imp.mod.importAll(null);
@@ -1427,10 +1431,7 @@
                 imp.addPackageAccess(scopesym);
             }
 
-            if (!loadErrored)
-            {
-                imp.mod.dsymbolSemantic(null);
-            }
+            imp.mod.dsymbolSemantic(null);
 
             if (imp.mod.needmoduleinfo)
             {
diff --git a/gcc/d/dmd/expression.h b/gcc/d/dmd/expression.h
index e4716c8..1bc78e7 100644
--- a/gcc/d/dmd/expression.h
+++ b/gcc/d/dmd/expression.h
@@ -81,7 +81,7 @@
 public:
     EXP op;                     // to minimize use of dynamic_cast
     unsigned char size;         // # of bytes in Expression so we can copy() it
-    bool parens;                // if this is a parenthesized expression
+    d_bool parens;                // if this is a parenthesized expression
     Type *type;                 // !=NULL means that semantic() has been run
     Loc loc;                    // file location
 
@@ -331,7 +331,7 @@
 {
 public:
     Dsymbol *s;
-    bool hasOverloads;
+    d_bool hasOverloads;
 
     DsymbolExp *syntaxCopy() override;
     bool isLvalue() override;
@@ -422,7 +422,7 @@
     Expression *basis;
     Expressions *elements;
     OwnedBy ownedByCtfe;
-    bool onstack;
+    d_bool onstack;
 
     static ArrayLiteralExp *create(const Loc &loc, Expressions *elements);
     static void emplace(UnionExp *pue, const Loc &loc, Expressions *elements);
@@ -476,8 +476,8 @@
      */
     int stageflags;
 
-    bool useStaticInit;         // if this is true, use the StructDeclaration's init symbol
-    bool isOriginal;            // used when moving instances to indicate `this is this.origin`
+    d_bool useStaticInit;         // if this is true, use the StructDeclaration's init symbol
+    d_bool isOriginal;            // used when moving instances to indicate `this is this.origin`
     OwnedBy ownedByCtfe;
 
     static StructLiteralExp *create(const Loc &loc, StructDeclaration *sd, void *elements, Type *stype = NULL);
@@ -537,8 +537,8 @@
     Expression *argprefix;      // expression to be evaluated just before arguments[]
 
     CtorDeclaration *member;    // constructor function
-    bool onstack;               // allocate on stack
-    bool thrownew;              // this NewExp is the expression of a ThrowStatement
+    d_bool onstack;               // allocate on stack
+    d_bool thrownew;              // this NewExp is the expression of a ThrowStatement
 
     Expression *lowering;       // lowered druntime hook: `_d_newclass`
 
@@ -566,7 +566,7 @@
 public:
     Declaration *var;
     Dsymbol *originalScope;
-    bool hasOverloads;
+    d_bool hasOverloads;
 
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -588,7 +588,7 @@
 class VarExp final : public SymbolExp
 {
 public:
-    bool delegateWasExtracted;
+    d_bool delegateWasExtracted;
     static VarExp *create(const Loc &loc, Declaration *var, bool hasOverloads = true);
     bool equals(const RootObject * const o) const override;
     bool isLvalue() override;
@@ -764,9 +764,9 @@
 {
 public:
     Identifier *ident;
-    bool noderef;       // true if the result of the expression will never be dereferenced
-    bool wantsym;       // do not replace Symbol with its initializer during semantic()
-    bool arrow;         // ImportC: if -> instead of .
+    d_bool noderef;       // true if the result of the expression will never be dereferenced
+    d_bool wantsym;       // do not replace Symbol with its initializer during semantic()
+    d_bool arrow;         // ImportC: if -> instead of .
 
     static DotIdExp *create(const Loc &loc, Expression *e, Identifier *ident);
     void accept(Visitor *v) override { v->visit(this); }
@@ -786,7 +786,7 @@
 {
 public:
     Declaration *var;
-    bool hasOverloads;
+    d_bool hasOverloads;
 
     bool isLvalue() override;
     Expression *toLvalue(Scope *sc, Expression *e) override;
@@ -810,7 +810,7 @@
 {
 public:
     FuncDeclaration *func;
-    bool hasOverloads;
+    d_bool hasOverloads;
     VarDeclaration *vthis2;  // container for multi-context
 
 
@@ -831,9 +831,9 @@
     Expressions *arguments;     // function arguments
     Identifiers *names;
     FuncDeclaration *f;         // symbol to call
-    bool directcall;            // true if a virtual call is devirtualized
-    bool inDebugStatement;      // true if this was in a debug statement
-    bool ignoreAttributes;      // don't enforce attributes (e.g. call @gc function in @nogc code)
+    d_bool directcall;            // true if a virtual call is devirtualized
+    d_bool inDebugStatement;      // true if this was in a debug statement
+    d_bool ignoreAttributes;      // don't enforce attributes (e.g. call @gc function in @nogc code)
     VarDeclaration *vthis2;     // container for multi-context
 
     static CallExp *create(const Loc &loc, Expression *e, Expressions *exps);
@@ -892,7 +892,7 @@
 class DeleteExp final : public UnaExp
 {
 public:
-    bool isRAII;
+    d_bool isRAII;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
@@ -937,9 +937,9 @@
     Expression *upr;            // NULL if implicit 0
     Expression *lwr;            // NULL if implicit [length - 1]
     VarDeclaration *lengthVar;
-    bool upperIsInBounds;       // true if upr <= e1.length
-    bool lowerIsLessThanUpper;  // true if lwr <= upr
-    bool arrayop;               // an array operation, rather than a slice
+    d_bool upperIsInBounds;       // true if upr <= e1.length
+    d_bool lowerIsLessThanUpper;  // true if lwr <= upr
+    d_bool arrayop;               // an array operation, rather than a slice
 
     SliceExp *syntaxCopy() override;
     bool isLvalue() override;
@@ -1011,8 +1011,8 @@
 class CommaExp final : public BinExp
 {
 public:
-    bool isGenerated;
-    bool allowCommaExp;
+    d_bool isGenerated;
+    d_bool allowCommaExp;
     bool isLvalue() override;
     Expression *toLvalue(Scope *sc, Expression *e) override;
     Expression *modifiableLvalue(Scope *sc, Expression *e) override;
@@ -1025,8 +1025,8 @@
 {
 public:
     VarDeclaration *lengthVar;
-    bool modifiable;
-    bool indexIsInBounds;       // true if 0 <= e2 && e2 <= e1.length - 1
+    d_bool modifiable;
+    d_bool indexIsInBounds;       // true if 0 <= e2 && e2 <= e1.length - 1
 
     IndexExp *syntaxCopy() override;
     bool isLvalue() override;
diff --git a/gcc/d/dmd/expressionsem.d b/gcc/d/dmd/expressionsem.d
index 632ea11..45dcb97 100644
--- a/gcc/d/dmd/expressionsem.d
+++ b/gcc/d/dmd/expressionsem.d
@@ -12158,6 +12158,9 @@
 
         if (!needsArrayLowering)
         {
+            // https://issues.dlang.org/show_bug.cgi?id=23783
+            if (exp.e1.checkSharedAccess(sc) || exp.e2.checkSharedAccess(sc))
+                return setError();
             if (auto e = typeCombine(exp, sc))
             {
                 result = e;
@@ -13372,6 +13375,12 @@
 
         bool visitVar(VarExp e)
         {
+            // https://issues.dlang.org/show_bug.cgi?id=20908
+            // direct access to init symbols is ok as they
+            // cannot be modified.
+            if (e.var.isSymbolDeclaration())
+                return false;
+
             // https://issues.dlang.org/show_bug.cgi?id=22626
             // Synchronized functions don't need to use core.atomic
             // when accessing `this`.
@@ -13409,9 +13418,16 @@
             //printf("dotvarexp = %s\n", e.toChars());
             if (e.type.isShared())
             {
-                // / https://issues.dlang.org/show_bug.cgi?id=22626
-                if (e.e1.isThisExp() && sc.func && sc.func.isSynchronized())
-                    return false;
+                if (e.e1.isThisExp())
+                {
+                    // https://issues.dlang.org/show_bug.cgi?id=22626
+                    if (sc.func && sc.func.isSynchronized())
+                        return false;
+
+                    // https://issues.dlang.org/show_bug.cgi?id=23790
+                    if (e.e1.type.isTypeStruct())
+                        return false;
+                }
 
                 auto fd = e.var.isFuncDeclaration();
                 const sharedFunc = fd && fd.type.isShared;
diff --git a/gcc/d/dmd/globals.h b/gcc/d/dmd/globals.h
index ec8fc32..84fbec6 100644
--- a/gcc/d/dmd/globals.h
+++ b/gcc/d/dmd/globals.h
@@ -85,8 +85,8 @@
 struct Output
 {
     /// Configuration for the compiler generator
-    bool doOutput;      // Output is enabled
-    bool fullOutput;    // Generate comments for hidden declarations (for -HC),
+    d_bool doOutput;      // Output is enabled
+    d_bool fullOutput;    // Generate comments for hidden declarations (for -HC),
                         // and don't strip the bodies of plain (non-template) functions (for -H)
     DString dir;   // write to directory 'dir'
     DString name;  // write to file 'name'
@@ -99,71 +99,71 @@
 // Put command line switches in here
 struct Param
 {
-    bool obj;           // write object file
-    bool multiobj;      // break one object file into multiple ones
-    bool trace;         // insert profiling hooks
-    bool tracegc;       // instrument calls to 'new'
-    bool verbose;       // verbose compile
-    bool vcg_ast;       // write-out codegen-ast
-    bool showColumns;   // print character (column) numbers in diagnostics
-    bool vtls;          // identify thread local variables
-    bool vtemplates;    // collect and list statistics on template instantiations
-    bool vtemplatesListInstances; // collect and list statistics on template instantiations origins
-    bool vgc;           // identify gc usage
-    bool vfield;        // identify non-mutable field variables
-    bool vcomplex;      // identify complex/imaginary type usage
-    bool vin;           // identify 'in' parameters
+    d_bool obj;           // write object file
+    d_bool multiobj;      // break one object file into multiple ones
+    d_bool trace;         // insert profiling hooks
+    d_bool tracegc;       // instrument calls to 'new'
+    d_bool verbose;       // verbose compile
+    d_bool vcg_ast;       // write-out codegen-ast
+    d_bool showColumns;   // print character (column) numbers in diagnostics
+    d_bool vtls;          // identify thread local variables
+    d_bool vtemplates;    // collect and list statistics on template instantiations
+    d_bool vtemplatesListInstances; // collect and list statistics on template instantiations origins
+    d_bool vgc;           // identify gc usage
+    d_bool vfield;        // identify non-mutable field variables
+    d_bool vcomplex;      // identify complex/imaginary type usage
+    d_bool vin;           // identify 'in' parameters
     Diagnostic useDeprecated;
-    bool useUnitTests;  // generate unittest code
-    bool useInline;     // inline expand functions
-    bool release;       // build release version
-    bool preservePaths; // true means don't strip path from source file
+    d_bool useUnitTests;  // generate unittest code
+    d_bool useInline;     // inline expand functions
+    d_bool release;       // build release version
+    d_bool preservePaths; // true means don't strip path from source file
     Diagnostic warnings;
-    bool color;         // use ANSI colors in console output
-    bool cov;           // generate code coverage data
+    d_bool color;         // use ANSI colors in console output
+    d_bool cov;           // generate code coverage data
     unsigned char covPercent;   // 0..100 code coverage percentage required
-    bool ctfe_cov;      // generate coverage data for ctfe
-    bool ignoreUnsupportedPragmas;      // rather than error on them
-    bool useModuleInfo; // generate runtime module information
-    bool useTypeInfo;   // generate runtime type information
-    bool useExceptions; // support exception handling
-    bool betterC;       // be a "better C" compiler; no dependency on D runtime
-    bool addMain;       // add a default main() function
-    bool allInst;       // generate code for all template instantiations
-    bool bitfields;         // support C style bit fields
+    d_bool ctfe_cov;      // generate coverage data for ctfe
+    d_bool ignoreUnsupportedPragmas;      // rather than error on them
+    d_bool useModuleInfo; // generate runtime module information
+    d_bool useTypeInfo;   // generate runtime type information
+    d_bool useExceptions; // support exception handling
+    d_bool betterC;       // be a "better C" compiler; no dependency on D runtime
+    d_bool addMain;       // add a default main() function
+    d_bool allInst;       // generate code for all template instantiations
+    d_bool bitfields;         // support C style bit fields
     CppStdRevision cplusplus;  // version of C++ name mangling to support
-    bool showGaggedErrors;  // print gagged errors anyway
-    bool printErrorContext;  // print errors with the error context (the error line in the source file)
-    bool manual;            // open browser on compiler manual
-    bool usage;             // print usage and exit
-    bool mcpuUsage;         // print help on -mcpu switch
-    bool transitionUsage;   // print help on -transition switch
-    bool checkUsage;        // print help on -check switch
-    bool checkActionUsage;  // print help on -checkaction switch
-    bool revertUsage;       // print help on -revert switch
-    bool previewUsage;      // print help on -preview switch
-    bool externStdUsage;    // print help on -extern-std switch
-    bool hcUsage;           // print help on -HC switch
-    bool logo;              // print logo;
+    d_bool showGaggedErrors;  // print gagged errors anyway
+    d_bool printErrorContext;  // print errors with the error context (the error line in the source file)
+    d_bool manual;            // open browser on compiler manual
+    d_bool usage;             // print usage and exit
+    d_bool mcpuUsage;         // print help on -mcpu switch
+    d_bool transitionUsage;   // print help on -transition switch
+    d_bool checkUsage;        // print help on -check switch
+    d_bool checkActionUsage;  // print help on -checkaction switch
+    d_bool revertUsage;       // print help on -revert switch
+    d_bool previewUsage;      // print help on -preview switch
+    d_bool externStdUsage;    // print help on -extern-std switch
+    d_bool hcUsage;           // print help on -HC switch
+    d_bool logo;              // print logo;
 
     // Options for `-preview=/-revert=`
     FeatureState useDIP25;       // implement https://wiki.dlang.org/DIP25
     FeatureState useDIP1000;     // implement https://dlang.org/spec/memory-safe-d.html#scope-return-params
-    bool ehnogc;                 // use @nogc exception handling
-    bool useDIP1021;             // implement https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1021.md
-    bool fieldwise;              // do struct equality testing field-wise rather than by memcmp()
-    bool fixAliasThis;           // if the current scope has an alias this, check it before searching upper scopes
+    d_bool ehnogc;                 // use @nogc exception handling
+    d_bool useDIP1021;             // implement https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1021.md
+    d_bool fieldwise;              // do struct equality testing field-wise rather than by memcmp()
+    d_bool fixAliasThis;           // if the current scope has an alias this, check it before searching upper scopes
     FeatureState rvalueRefParam; // allow rvalues to be arguments to ref parameters
                                  // https://dconf.org/2019/talks/alexandrescu.html
                                  // https://gist.github.com/andralex/e5405a5d773f07f73196c05f8339435a
                                  // https://digitalmars.com/d/archives/digitalmars/D/Binding_rvalues_to_ref_parameters_redux_325087.html
                                  // Implementation: https://github.com/dlang/dmd/pull/9817
     FeatureState noSharedAccess; // read/write access to shared memory objects
-    bool previewIn;              // `in` means `[ref] scope const`, accepts rvalues
-    bool inclusiveInContracts;   // 'in' contracts of overridden methods must be a superset of parent contract
-    bool shortenedMethods;       // allow => in normal function declarations
-    bool fixImmutableConv;       // error on unsound immutable conversion - https://github.com/dlang/dmd/pull/14070
-    bool fix16997;               // fix integral promotions for unary + - ~ operators
+    d_bool previewIn;              // `in` means `[ref] scope const`, accepts rvalues
+    d_bool inclusiveInContracts;   // 'in' contracts of overridden methods must be a superset of parent contract
+    d_bool shortenedMethods;       // allow => in normal function declarations
+    d_bool fixImmutableConv;       // error on unsound immutable conversion - https://github.com/dlang/dmd/pull/14070
+    d_bool fix16997;               // fix integral promotions for unary + - ~ operators
                                  // https://issues.dlang.org/show_bug.cgi?id=16997
     FeatureState dtorFields;     // destruct fields of partially constructed objects
                                  // https://issues.dlang.org/show_bug.cgi?id=14246
@@ -208,7 +208,7 @@
 
     MessageStyle messageStyle;  // style of file/line annotations on messages
 
-    bool run;           // run resulting executable
+    d_bool run;           // run resulting executable
     Strings runargs;    // arguments for executable
 
     Array<const char *> cppswitches; // preprocessor switches
@@ -228,7 +228,7 @@
 struct structalign_t
 {
     unsigned short value;
-    bool pack;
+    d_bool pack;
 
     bool isDefault() const;
     void setDefault();
@@ -275,7 +275,7 @@
     Array<class Identifier*>* versionids; // command line versions and predefined versions
     Array<class Identifier*>* debugids;   // command line debug versions and predefined versions
 
-    bool hasMainFunction;
+    d_bool hasMainFunction;
     unsigned varSequenceNumber;
 
     FileManager* fileManager;
diff --git a/gcc/d/dmd/hdrgen.d b/gcc/d/dmd/hdrgen.d
index c7e5690..e0684e6 100644
--- a/gcc/d/dmd/hdrgen.d
+++ b/gcc/d/dmd/hdrgen.d
@@ -1586,7 +1586,10 @@
         if (hgs.hdrgen)
         {
             // if the return type is missing (e.g. ref functions or auto)
-            if (!tf.next || f.storage_class & STC.auto_)
+            // https://issues.dlang.org/show_bug.cgi?id=20090
+            // constructors are an exception: they don't have an explicit return
+            // type but we still don't output the body.
+            if ((!f.isCtorDeclaration() && !tf.next) || f.storage_class & STC.auto_)
             {
                 hgs.autoMember++;
                 bodyToBuffer(f);
diff --git a/gcc/d/dmd/identifier.h b/gcc/d/dmd/identifier.h
index c12c355..e7b3ba6 100644
--- a/gcc/d/dmd/identifier.h
+++ b/gcc/d/dmd/identifier.h
@@ -17,7 +17,7 @@
 {
 private:
     int value;
-    bool isAnonymous_;
+    d_bool isAnonymous_;
     DString string;
 
 public:
diff --git a/gcc/d/dmd/init.h b/gcc/d/dmd/init.h
index 66b874c..9a6a56b 100644
--- a/gcc/d/dmd/init.h
+++ b/gcc/d/dmd/init.h
@@ -77,8 +77,8 @@
     Initializers value; // of Initializer *'s
     unsigned dim;       // length of array being initialized
     Type *type;         // type that array will be used to initialize
-    bool sem;           // true if semantic() is run
-    bool isCarray;      // C array semantics
+    d_bool sem;           // true if semantic() is run
+    d_bool isCarray;      // C array semantics
 
     bool isAssociativeArray() const;
     Expression *toAssocArrayLiteral();
@@ -89,7 +89,7 @@
 class ExpInitializer final : public Initializer
 {
 public:
-    bool expandTuples;
+    d_bool expandTuples;
     Expression *exp;
 
     void accept(Visitor *v) override { v->visit(this); }
@@ -112,7 +112,7 @@
 public:
     DesigInits initializerList;
     Type *type;         // type that array will be used to initialize
-    bool sem;           // true if semantic() is run
+    d_bool sem;           // true if semantic() is run
 
     void accept(Visitor *v) override { v->visit(this); }
 };
diff --git a/gcc/d/dmd/lexer.d b/gcc/d/dmd/lexer.d
index c9ed35f..f0f7872 100644
--- a/gcc/d/dmd/lexer.d
+++ b/gcc/d/dmd/lexer.d
@@ -2664,14 +2664,19 @@
         eSink.error(loc, format, args);
     }
 
-    final void deprecation(const(char)* format)
+    void deprecation(T...)(const ref Loc loc, const(char)* format, T args)
     {
-        eSink.deprecation(token.loc, format);
+        eSink.deprecation(loc, format, args);
     }
 
-    final void deprecationSupplemental(const(char)* format)
+    void deprecation(T...)(const(char)* format, T args)
     {
-        eSink.deprecationSupplemental(token.loc, format);
+        eSink.deprecation(token.loc, format, args);
+    }
+
+    void deprecationSupplemental(T...)(const(char)* format, T args)
+    {
+        eSink.deprecationSupplemental(token.loc, format, args);
     }
 
     /***************************************
@@ -2695,12 +2700,21 @@
             else
             {
                 const locx = loc();
-                eSink.warning(locx, "C preprocessor directive `#%s` is not supported", n.ident.toChars());
+                // @@@DEPRECATED_2.103@@@
+                // Turn into an error in 2.113
+                if (inTokenStringConstant)
+                    deprecation(locx, "token string requires valid D tokens, not `#%s`", n.ident.toChars());
+                else
+                    error(locx, "C preprocessor directive `#%s` is not supported", n.ident.toChars());
             }
         }
         else if (n.value == TOK.if_)
         {
-            error("C preprocessor directive `#if` is not supported, use `version` or `static if`");
+            const locx = loc();
+            if (inTokenStringConstant)
+                error(locx, "token string requires valid D tokens, not `#if`");
+            else
+                error(locx, "C preprocessor directive `#if` is not supported, use `version` or `static if`");
         }
         return false;
     }
diff --git a/gcc/d/dmd/module.h b/gcc/d/dmd/module.h
index 002bb1a..8b48110 100644
--- a/gcc/d/dmd/module.h
+++ b/gcc/d/dmd/module.h
@@ -74,8 +74,8 @@
     unsigned errors;    // if any errors in file
     unsigned numlines;  // number of lines in source file
     FileType filetype;  // source file type
-    bool hasAlwaysInlines; // contains references to functions that must be inlined
-    bool isPackageFile; // if it is a package.d
+    d_bool hasAlwaysInlines; // contains references to functions that must be inlined
+    d_bool isPackageFile; // if it is a package.d
     Package *pkg;       // if isPackageFile is true, the Package that contains this package.d
     Strings contentImportedFiles;  // array of files whose content was imported
     int needmoduleinfo;
@@ -90,7 +90,7 @@
     Identifier *searchCacheIdent;
     Dsymbol *searchCacheSymbol; // cached value of search
     int searchCacheFlags;       // cached flags
-    bool insearch;
+    d_bool insearch;
 
     // module from command line we're imported from,
     // i.e. a module that will be taken all the
@@ -165,7 +165,7 @@
     Loc loc;
     Identifier *id;
     DArray<Identifier*> packages;  // array of Identifier's representing packages
-    bool isdeprecated;  // if it is a deprecated module
+    d_bool isdeprecated;  // if it is a deprecated module
     Expression *msg;
 
     const char *toChars() const;
diff --git a/gcc/d/dmd/mtype.h b/gcc/d/dmd/mtype.h
index d0775f2..fbfd766 100644
--- a/gcc/d/dmd/mtype.h
+++ b/gcc/d/dmd/mtype.h
@@ -589,7 +589,7 @@
     Parameters* parameters;
     StorageClass stc;
     VarArg varargs;
-    bool hasIdentifierList; // true if C identifier-list style
+    d_bool hasIdentifierList; // true if C identifier-list style
 
     size_t length();
     Parameter *operator[](size_t i) { return Parameter::getNth(parameters, i); }
@@ -779,7 +779,7 @@
 public:
     StructDeclaration *sym;
     AliasThisRec att;
-    bool inuse;
+    d_bool inuse;
 
     static TypeStruct *create(StructDeclaration *sym);
     const char *kind() override;
diff --git a/gcc/d/dmd/objc.h b/gcc/d/dmd/objc.h
index 305ce81..a5cc6f1 100644
--- a/gcc/d/dmd/objc.h
+++ b/gcc/d/dmd/objc.h
@@ -37,8 +37,8 @@
 
 struct ObjcClassDeclaration
 {
-    bool isMeta;
-    bool isExtern;
+    d_bool isMeta;
+    d_bool isExtern;
 
     Identifier* identifier;
     ClassDeclaration* classDeclaration;
@@ -52,7 +52,7 @@
 {
     ObjcSelector* selector;
     VarDeclaration* selectorParameter;
-    bool isOptional;
+    d_bool isOptional;
 };
 
 class Objc
diff --git a/gcc/d/dmd/root/dcompat.h b/gcc/d/dmd/root/dcompat.h
index 0bc23b7..1a49688 100644
--- a/gcc/d/dmd/root/dcompat.h
+++ b/gcc/d/dmd/root/dcompat.h
@@ -36,7 +36,7 @@
 };
 
 /// Corresponding C++ type that maps to D size_t
-#if __APPLE__ && __i386__
+#if __APPLE__ && (__i386__ || __ppc__)
 // size_t is 'unsigned long', which makes it mangle differently than D's 'uint'
 typedef unsigned d_size_t;
 #elif MARS && DMD_VERSION >= 2079 && DMD_VERSION <= 2081 && \
@@ -49,3 +49,11 @@
 #else
 typedef size_t d_size_t;
 #endif
+
+/// Corresponding C++ type that maps to D bool
+#if __APPLE__ && __ppc__
+// bool is defined as an 'int', which does not match same size as D
+typedef uint8_t d_bool;
+#else
+typedef bool d_bool;
+#endif
diff --git a/gcc/d/dmd/root/optional.h b/gcc/d/dmd/root/optional.h
index cc2ee79..353332c 100644
--- a/gcc/d/dmd/root/optional.h
+++ b/gcc/d/dmd/root/optional.h
@@ -11,6 +11,8 @@
  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/optional.h
  */
 
+#include "dcompat.h"    // for d_bool
+
 /// Optional type that is either `empty` or contains a value of type `T`
 template<typename T>
 struct Optional final
@@ -20,7 +22,7 @@
     T value;
 
     /** whether `value` is set **/
-    bool present;
+    d_bool present;
 
 public:
     /** Creates an `Optional` with the given value **/
diff --git a/gcc/d/dmd/scope.h b/gcc/d/dmd/scope.h
index b25c26a..da11428 100644
--- a/gcc/d/dmd/scope.h
+++ b/gcc/d/dmd/scope.h
@@ -81,8 +81,8 @@
     ForeachStatement *fes;      // if nested function for ForeachStatement, this is it
     Scope *callsc;              // used for __FUNCTION__, __PRETTY_FUNCTION__ and __MODULE__
     Dsymbol *inunion;           // !=null if processing members of a union
-    bool nofree;                // true if shouldn't free it
-    bool inLoop;                // true if inside a loop (where constructor calls aren't allowed)
+    d_bool nofree;                // true if shouldn't free it
+    d_bool inLoop;                // true if inside a loop (where constructor calls aren't allowed)
     int intypeof;               // in typeof(exp)
     VarDeclaration *lastVar;    // Previous symbol used to prevent goto-skips-init
 
diff --git a/gcc/d/dmd/statement.h b/gcc/d/dmd/statement.h
index 46cc4da..6d1f85b3 100644
--- a/gcc/d/dmd/statement.h
+++ b/gcc/d/dmd/statement.h
@@ -433,7 +433,7 @@
 public:
     Expression *condition;
     Statement *_body;
-    bool isFinal;
+    d_bool isFinal;
 
     DefaultStatement *sdefault;
     Statement *tryBody;            // set to TryCatchStatement or TryFinallyStatement if in _body portion
@@ -600,11 +600,11 @@
 
     VarDeclaration *var;
     // set if semantic processing errors
-    bool errors;
+    d_bool errors;
 
     // was generated by the compiler,
     // wasn't present in source code
-    bool internalCatch;
+    d_bool internalCatch;
 
     Catch *syntaxCopy();
 };
@@ -616,7 +616,7 @@
     Statement *finalbody;
 
     Statement *tryBody;   // set to enclosing TryCatchStatement or TryFinallyStatement if in _body portion
-    bool bodyFallsThru;   // true if _body falls through to finally
+    d_bool bodyFallsThru;   // true if _body falls through to finally
 
     static TryFinallyStatement *create(const Loc &loc, Statement *body, Statement *finalbody);
     TryFinallyStatement *syntaxCopy() override;
@@ -643,7 +643,7 @@
     Expression *exp;
     // was generated by the compiler,
     // wasn't present in source code
-    bool internalThrow;
+    d_bool internalThrow;
 
     ThrowStatement *syntaxCopy() override;
 
@@ -668,7 +668,7 @@
     TryFinallyStatement *tf;
     ScopeGuardStatement *os;
     VarDeclaration *lastVar;
-    bool inCtfeBlock;
+    d_bool inCtfeBlock;
     GotoStatement *syntaxCopy() override;
 
     void accept(Visitor *v) override { v->visit(this); }
@@ -685,8 +685,8 @@
     VarDeclaration *lastVar;
     Statement *gotoTarget;      // interpret
     void* extra;                // used by Statement_toIR()
-    bool breaks;                // someone did a 'break ident'
-    bool inCtfeBlock;
+    d_bool breaks;                // someone did a 'break ident'
+    d_bool inCtfeBlock;
     LabelStatement *syntaxCopy() override;
 
     void accept(Visitor *v) override { v->visit(this); }
@@ -697,8 +697,8 @@
 public:
     LabelStatement *statement;
 
-    bool deleted;           // set if rewritten to return in foreach delegate
-    bool iasm;              // set if used by inline assembler
+    d_bool deleted;           // set if rewritten to return in foreach delegate
+    d_bool iasm;              // set if used by inline assembler
 
     static LabelDsymbol *create(Identifier *ident);
     LabelDsymbol *isLabel() override;
@@ -722,8 +722,8 @@
     code *asmcode;
     unsigned asmalign;          // alignment of this statement
     unsigned regs;              // mask of registers modified (must match regm_t in back end)
-    bool refparam;              // true if function parameter is referenced
-    bool naked;                 // true if function is to be naked
+    d_bool refparam;              // true if function parameter is referenced
+    d_bool naked;                 // true if function is to be naked
 
     InlineAsmStatement *syntaxCopy() override;
     void accept(Visitor *v) override { v->visit(this); }
diff --git a/gcc/d/dmd/statementsem.d b/gcc/d/dmd/statementsem.d
index bbaee8e..694db28 100644
--- a/gcc/d/dmd/statementsem.d
+++ b/gcc/d/dmd/statementsem.d
@@ -1941,7 +1941,6 @@
         }
         if (checkNonAssignmentArrayOp(ifs.condition))
             ifs.condition = ErrorExp.get();
-        ifs.condition = checkGC(scd, ifs.condition);
 
         // Convert to boolean after declaring prm so this works:
         //  if (S prm = S()) {}
@@ -1953,6 +1952,10 @@
         // This feature allows a limited form of conditional compilation.
         ifs.condition = ifs.condition.optimize(WANTvalue);
 
+        // checkGC after optimizing the condition so that
+        // compile time constants are reduced.
+        ifs.condition = checkGC(scd, ifs.condition);
+
         // Save 'root' of two branches (then and else) at the point where it forks
         CtorFlow ctorflow_root = scd.ctorflow.clone();
 
@@ -4525,8 +4528,7 @@
             decls.append(Dsymbol.arraySyntaxCopy(dbody));
         else
         {
-            if (fs._body) // https://issues.dlang.org/show_bug.cgi?id=17646
-                stmts.push(fs._body.syntaxCopy());
+            stmts.push(fs._body.syntaxCopy());
             s = new CompoundStatement(loc, stmts);
         }
 
diff --git a/gcc/d/dmd/target.h b/gcc/d/dmd/target.h
index ef2c09d..561afa1 100644
--- a/gcc/d/dmd/target.h
+++ b/gcc/d/dmd/target.h
@@ -92,11 +92,11 @@
         Microsoft,
         Sun
     };
-    bool reverseOverloads;    // with dmc and cl, overloaded functions are grouped and in reverse order
-    bool exceptions;          // set if catching C++ exceptions is supported
-    bool twoDtorInVtable;     // target C++ ABI puts deleting and non-deleting destructor into vtable
-    bool splitVBasetable;     // set if C++ ABI uses separate tables for virtual functions and virtual bases
-    bool wrapDtorInExternD;   // set if C++ dtors require a D wrapper to be callable from runtime
+    d_bool reverseOverloads;    // with dmc and cl, overloaded functions are grouped and in reverse order
+    d_bool exceptions;          // set if catching C++ exceptions is supported
+    d_bool twoDtorInVtable;     // target C++ ABI puts deleting and non-deleting destructor into vtable
+    d_bool splitVBasetable;     // set if C++ ABI uses separate tables for virtual functions and virtual bases
+    d_bool wrapDtorInExternD;   // set if C++ dtors require a D wrapper to be callable from runtime
     Runtime runtime;
 
     const char *toMangle(Dsymbol *s);
@@ -110,7 +110,7 @@
 
 struct TargetObjC
 {
-    bool supported;     // set if compiler can interface with Objective-C
+    d_bool supported;     // set if compiler can interface with Objective-C
 };
 
 struct Target
@@ -156,15 +156,15 @@
 
     DString architectureName;    // name of the platform architecture (e.g. X86_64)
     CPU cpu;                // CPU instruction set to target
-    bool is64bit;           // generate 64 bit code for x86_64; true by default for 64 bit dmd
-    bool isLP64;            // pointers are 64 bits
+    d_bool is64bit;           // generate 64 bit code for x86_64; true by default for 64 bit dmd
+    d_bool isLP64;            // pointers are 64 bits
 
     // Environmental
     DString obj_ext;    /// extension for object files
     DString lib_ext;    /// extension for static library files
     DString dll_ext;    /// extension for dynamic library files
-    bool run_noext;     /// allow -run sources without extensions
-    bool omfobj;        /// for Win32: write OMF object files instead of COFF
+    d_bool run_noext;     /// allow -run sources without extensions
+    d_bool omfobj;        /// for Win32: write OMF object files instead of COFF
 
     template <typename T>
     struct FPTypeProperties
diff --git a/gcc/d/dmd/template.h b/gcc/d/dmd/template.h
index 12b2120..8622b5c 100644
--- a/gcc/d/dmd/template.h
+++ b/gcc/d/dmd/template.h
@@ -78,12 +78,12 @@
 
     Dsymbol *onemember;         // if !=NULL then one member of this template
 
-    bool literal;               // this template declaration is a literal
-    bool ismixin;               // template declaration is only to be used as a mixin
-    bool isstatic;              // this is static template declaration
-    bool isTrivialAliasSeq;     // matches `template AliasSeq(T...) { alias AliasSeq = T; }
-    bool isTrivialAlias;        // matches pattern `template Alias(T) { alias Alias = qualifiers(T); }`
-    bool deprecated_;           // this template declaration is deprecated
+    d_bool literal;               // this template declaration is a literal
+    d_bool ismixin;               // template declaration is only to be used as a mixin
+    d_bool isstatic;              // this is static template declaration
+    d_bool isTrivialAliasSeq;     // matches `template AliasSeq(T...) { alias AliasSeq = T; }
+    d_bool isTrivialAlias;        // matches pattern `template Alias(T) { alias Alias = qualifiers(T); }`
+    d_bool deprecated_;           // this template declaration is deprecated
     Visibility visibility;
 
     TemplatePrevious *previous;         // threaded list of previous instantiation attempts on stack
@@ -133,7 +133,7 @@
      * A dependent template parameter should return MATCHexact in matchArg()
      * to respect the match level of the corresponding precedent parameter.
      */
-    bool dependent;
+    d_bool dependent;
 
     virtual TemplateTypeParameter  *isTemplateTypeParameter();
     virtual TemplateValueParameter *isTemplateValueParameter();
diff --git a/gcc/d/dmd/visitor.h b/gcc/d/dmd/visitor.h
index f8cbdb4..ed9f9ce 100644
--- a/gcc/d/dmd/visitor.h
+++ b/gcc/d/dmd/visitor.h
@@ -10,6 +10,7 @@
 #pragma once
 
 #include "root/dsystem.h"
+#include "root/dcompat.h"   // for d_bool
 
 class Statement;
 class ErrorStatement;
@@ -663,6 +664,6 @@
 class StoppableVisitor : public Visitor
 {
 public:
-    bool stop;
+    d_bool stop;
     StoppableVisitor() : stop(false) {}
 };
diff --git a/gcc/diagnostic-diagram.h b/gcc/diagnostic-diagram.h
new file mode 100644
index 0000000..fc923c5
--- /dev/null
+++ b/gcc/diagnostic-diagram.h
@@ -0,0 +1,51 @@
+/* Support for diagrams within diagnostics.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_DIAGNOSTIC_DIAGRAM_H
+#define GCC_DIAGNOSTIC_DIAGRAM_H
+
+namespace text_art
+{
+  class canvas;
+} // namespace text_art
+
+/* A text art diagram, along with an "alternative text" string
+   describing it.  */
+
+class diagnostic_diagram
+{
+ public:
+  diagnostic_diagram (const text_art::canvas &canvas,
+		      const char *alt_text)
+  : m_canvas (canvas),
+    m_alt_text (alt_text)
+  {
+    gcc_assert (alt_text);
+  }
+
+  const text_art::canvas &get_canvas () const { return m_canvas; }
+  const char *get_alt_text () const { return m_alt_text; }
+
+ private:
+  const text_art::canvas &m_canvas;
+  const char *const m_alt_text;
+};
+
+#endif /* ! GCC_DIAGNOSTIC_DIAGRAM_H */
diff --git a/gcc/diagnostic-format-json.cc b/gcc/diagnostic-format-json.cc
index 694dddc..539b98b 100644
--- a/gcc/diagnostic-format-json.cc
+++ b/gcc/diagnostic-format-json.cc
@@ -324,6 +324,15 @@
   free (filename);
 }
 
+/* Callback for diagnostic_context::m_diagrams.m_emission_cb.  */
+
+static void
+json_emit_diagram (diagnostic_context *,
+		   const diagnostic_diagram &)
+{
+  /* No-op.  */
+}
+
 /* Populate CONTEXT in preparation for JSON output (either to stderr, or
    to a file).  */
 
@@ -340,6 +349,7 @@
   context->begin_group_cb = json_begin_group;
   context->end_group_cb =  json_end_group;
   context->print_path = NULL; /* handled in json_end_diagnostic.  */
+  context->m_diagrams.m_emission_cb = json_emit_diagram;
 
   /* The metadata is handled in JSON format, rather than as text.  */
   context->show_cwe = false;
diff --git a/gcc/diagnostic-format-sarif.cc b/gcc/diagnostic-format-sarif.cc
index fd29ac2..5e48398 100644
--- a/gcc/diagnostic-format-sarif.cc
+++ b/gcc/diagnostic-format-sarif.cc
@@ -20,6 +20,7 @@
 
 
 #include "config.h"
+#define INCLUDE_VECTOR
 #include "system.h"
 #include "coretypes.h"
 #include "diagnostic.h"
@@ -29,6 +30,8 @@
 #include "cpplib.h"
 #include "logical-location.h"
 #include "diagnostic-client-data-hooks.h"
+#include "diagnostic-diagram.h"
+#include "text-art/canvas.h"
 
 class sarif_builder;
 
@@ -66,8 +69,13 @@
 			diagnostic_info *diagnostic,
 			diagnostic_t orig_diag_kind,
 			sarif_builder *builder);
+  void on_diagram (diagnostic_context *context,
+		   const diagnostic_diagram &diagram,
+		   sarif_builder *builder);
 
 private:
+  void add_related_location (json::object *location_obj);
+
   json::array *m_related_locations_arr;
 };
 
@@ -135,7 +143,8 @@
 
   void end_diagnostic (diagnostic_context *context, diagnostic_info *diagnostic,
 		       diagnostic_t orig_diag_kind);
-
+  void emit_diagram (diagnostic_context *context,
+		     const diagnostic_diagram &diagram);
   void end_group ();
 
   void flush_to_file (FILE *outf);
@@ -144,6 +153,9 @@
   json::object *make_location_object (const rich_location &rich_loc,
 				      const logical_location *logical_loc);
   json::object *make_message_object (const char *msg) const;
+  json::object *
+  make_message_object_for_diagram (diagnostic_context *context,
+				   const diagnostic_diagram &diagram);
 
 private:
   sarif_result *make_result_object (diagnostic_context *context,
@@ -261,12 +273,6 @@
 				    diagnostic_t /*orig_diag_kind*/,
 				    sarif_builder *builder)
 {
-  if (!m_related_locations_arr)
-    {
-      m_related_locations_arr = new json::array ();
-      set ("relatedLocations", m_related_locations_arr);
-    }
-
   /* We don't yet generate meaningful logical locations for notes;
      sometimes these will related to current_function_decl, but
      often they won't.  */
@@ -277,6 +283,39 @@
   pp_clear_output_area (context->printer);
   location_obj->set ("message", message_obj);
 
+  add_related_location (location_obj);
+}
+
+/* Handle diagrams that occur within a diagnostic group.
+   The closest thing in SARIF seems to be to add a location to the
+   "releatedLocations" property  (SARIF v2.1.0 section 3.27.22),
+   and to put the diagram into the "message" property of that location
+   (SARIF v2.1.0 section 3.28.5).  */
+
+void
+sarif_result::on_diagram (diagnostic_context *context,
+			  const diagnostic_diagram &diagram,
+			  sarif_builder *builder)
+{
+  json::object *location_obj = new json::object ();
+  json::object *message_obj
+    = builder->make_message_object_for_diagram (context, diagram);
+  location_obj->set ("message", message_obj);
+
+  add_related_location (location_obj);
+}
+
+/* Add LOCATION_OBJ to this result's "relatedLocations" array,
+   creating it if it doesn't yet exist.  */
+
+void
+sarif_result::add_related_location (json::object *location_obj)
+{
+  if (!m_related_locations_arr)
+    {
+      m_related_locations_arr = new json::array ();
+      set ("relatedLocations", m_related_locations_arr);
+    }
   m_related_locations_arr->append (location_obj);
 }
 
@@ -348,6 +387,18 @@
     }
 }
 
+/* Implementation of diagnostic_context::m_diagrams.m_emission_cb
+   for SARIF output.  */
+
+void
+sarif_builder::emit_diagram (diagnostic_context *context,
+			     const diagnostic_diagram &diagram)
+{
+  /* We must be within the emission of a top-level diagnostic.  */
+  gcc_assert (m_cur_group_result);
+  m_cur_group_result->on_diagram (context, diagram, this);
+}
+
 /* Implementation of "end_group_cb" for SARIF output.  */
 
 void
@@ -1115,6 +1166,37 @@
   return message_obj;
 }
 
+/* Make a message object (SARIF v2.1.0 section 3.11) for DIAGRAM.
+   We emit the diagram as a code block within the Markdown part
+   of the message.  */
+
+json::object *
+sarif_builder::make_message_object_for_diagram (diagnostic_context *context,
+						const diagnostic_diagram &diagram)
+{
+  json::object *message_obj = new json::object ();
+
+  /* "text" property (SARIF v2.1.0 section 3.11.8).  */
+  message_obj->set ("text", new json::string (diagram.get_alt_text ()));
+
+  char *saved_prefix = pp_take_prefix (context->printer);
+  pp_set_prefix (context->printer, NULL);
+
+  /* "To produce a code block in Markdown, simply indent every line of
+     the block by at least 4 spaces or 1 tab."
+     Here we use 4 spaces.  */
+  diagram.get_canvas ().print_to_pp (context->printer, "    ");
+  pp_set_prefix (context->printer, saved_prefix);
+
+  /* "markdown" property (SARIF v2.1.0 section 3.11.9).  */
+  message_obj->set ("markdown",
+		    new json::string (pp_formatted_text (context->printer)));
+
+  pp_clear_output_area (context->printer);
+
+  return message_obj;
+}
+
 /* Make a multiformatMessageString object (SARIF v2.1.0 section 3.12)
    for MSG.  */
 
@@ -1630,6 +1712,16 @@
   fnotice (stderr, "Internal compiler error:\n");
 }
 
+/* Callback for diagnostic_context::m_diagrams.m_emission_cb.  */
+
+static void
+sarif_emit_diagram (diagnostic_context *context,
+		    const diagnostic_diagram &diagram)
+{
+  gcc_assert (the_builder);
+  the_builder->emit_diagram (context, diagram);
+}
+
 /* Populate CONTEXT in preparation for SARIF output (either to stderr, or
    to a file).  */
 
@@ -1645,6 +1737,7 @@
   context->end_group_cb =  sarif_end_group;
   context->print_path = NULL; /* handled in sarif_end_diagnostic.  */
   context->ice_handler_cb = sarif_ice_handler;
+  context->m_diagrams.m_emission_cb = sarif_emit_diagram;
 
   /* The metadata is handled in SARIF format, rather than as text.  */
   context->show_cwe = false;
diff --git a/gcc/diagnostic-text-art.h b/gcc/diagnostic-text-art.h
new file mode 100644
index 0000000..a0d8a78
--- /dev/null
+++ b/gcc/diagnostic-text-art.h
@@ -0,0 +1,49 @@
+/* Copyright (C) 2023 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_DIAGNOSTIC_TEXT_ART_H
+#define GCC_DIAGNOSTIC_TEXT_ART_H
+
+/* Values for -fdiagnostics-text-art-charset=.  */
+
+enum diagnostic_text_art_charset
+{
+  /* No text art diagrams shall be emitted.  */
+  DIAGNOSTICS_TEXT_ART_CHARSET_NONE,
+
+  /* Use pure ASCII for text art diagrams.  */
+  DIAGNOSTICS_TEXT_ART_CHARSET_ASCII,
+
+  /* Use ASCII + conservative use of other unicode characters
+     in text art diagrams.  */
+  DIAGNOSTICS_TEXT_ART_CHARSET_UNICODE,
+
+  /* Use Emoji.  */
+  DIAGNOSTICS_TEXT_ART_CHARSET_EMOJI
+};
+
+const enum diagnostic_text_art_charset DIAGNOSTICS_TEXT_ART_CHARSET_DEFAULT
+  = DIAGNOSTICS_TEXT_ART_CHARSET_EMOJI;
+
+extern void
+diagnostics_text_art_charset_init (diagnostic_context *context,
+				  enum diagnostic_text_art_charset charset);
+
+
+#endif /* ! GCC_DIAGNOSTIC_TEXT_ART_H */
diff --git a/gcc/diagnostic.cc b/gcc/diagnostic.cc
index 0f09308..c523f21 100644
--- a/gcc/diagnostic.cc
+++ b/gcc/diagnostic.cc
@@ -23,6 +23,7 @@
    message module.  */
 
 #include "config.h"
+#define INCLUDE_VECTOR
 #include "system.h"
 #include "coretypes.h"
 #include "version.h"
@@ -35,11 +36,14 @@
 #include "diagnostic-metadata.h"
 #include "diagnostic-path.h"
 #include "diagnostic-client-data-hooks.h"
+#include "diagnostic-text-art.h"
+#include "diagnostic-diagram.h"
 #include "edit-context.h"
 #include "selftest.h"
 #include "selftest-diagnostic.h"
 #include "opts.h"
 #include "cpplib.h"
+#include "text-art/theme.h"
 
 #ifdef HAVE_TERMIOS_H
 # include <termios.h>
@@ -244,6 +248,10 @@
   context->ice_handler_cb = NULL;
   context->includes_seen = NULL;
   context->m_client_data_hooks = NULL;
+  context->m_diagrams.m_theme = NULL;
+  context->m_diagrams.m_emission_cb = NULL;
+  diagnostics_text_art_charset_init (context,
+				     DIAGNOSTICS_TEXT_ART_CHARSET_DEFAULT);
 }
 
 /* Maybe initialize the color support. We require clients to do this
@@ -320,6 +328,12 @@
   if (context->final_cb)
     context->final_cb (context);
 
+  if (context->m_diagrams.m_theme)
+    {
+      delete context->m_diagrams.m_theme;
+      context->m_diagrams.m_theme = NULL;
+    }
+
   diagnostic_file_cache_fini ();
 
   XDELETEVEC (context->classify_diagnostic);
@@ -2174,6 +2188,33 @@
 
   gcc_unreachable ();
 }
+
+/* Emit DIAGRAM to CONTEXT, respecting the output format.  */
+
+void
+diagnostic_emit_diagram (diagnostic_context *context,
+			 const diagnostic_diagram &diagram)
+{
+  if (context->m_diagrams.m_theme == nullptr)
+    return;
+
+  if (context->m_diagrams.m_emission_cb)
+    {
+      context->m_diagrams.m_emission_cb (context, diagram);
+      return;
+    }
+
+  /* Default implementation.  */
+  char *saved_prefix = pp_take_prefix (context->printer);
+  pp_set_prefix (context->printer, NULL);
+  /* Use a newline before and after and a two-space indent
+     to make the diagram stand out a little from the wall of text.  */
+  pp_newline (context->printer);
+  diagram.get_canvas ().print_to_pp (context->printer, "  ");
+  pp_newline (context->printer);
+  pp_set_prefix (context->printer, saved_prefix);
+  pp_flush (context->printer);
+}
 
 /* Special case error functions.  Most are implemented in terms of the
    above, or should be.  */
@@ -2316,6 +2357,38 @@
     }
 }
 
+/* Initialize CONTEXT->m_diagrams based on CHARSET.
+   Specifically, make a text_art::theme object for m_diagrams.m_theme,
+   (or NULL for "no diagrams").  */
+
+void
+diagnostics_text_art_charset_init (diagnostic_context *context,
+				   enum diagnostic_text_art_charset charset)
+{
+  delete context->m_diagrams.m_theme;
+  switch (charset)
+    {
+    default:
+      gcc_unreachable ();
+
+    case DIAGNOSTICS_TEXT_ART_CHARSET_NONE:
+      context->m_diagrams.m_theme = NULL;
+      break;
+
+    case DIAGNOSTICS_TEXT_ART_CHARSET_ASCII:
+      context->m_diagrams.m_theme = new text_art::ascii_theme ();
+      break;
+
+    case DIAGNOSTICS_TEXT_ART_CHARSET_UNICODE:
+      context->m_diagrams.m_theme = new text_art::unicode_theme ();
+      break;
+
+    case DIAGNOSTICS_TEXT_ART_CHARSET_EMOJI:
+      context->m_diagrams.m_theme = new text_art::emoji_theme ();
+      break;
+    }
+}
+
 /* Implementation of diagnostic_path::num_events vfunc for
    simple_diagnostic_path: simply get the number of events in the vec.  */
 
diff --git a/gcc/diagnostic.h b/gcc/diagnostic.h
index 9a51097..00b828f 100644
--- a/gcc/diagnostic.h
+++ b/gcc/diagnostic.h
@@ -24,6 +24,11 @@
 #include "pretty-print.h"
 #include "diagnostic-core.h"
 
+namespace text_art
+{
+  class theme;
+} // namespace text_art
+
 /* An enum for controlling what units to use for the column number
    when diagnostics are output, used by the -fdiagnostics-column-unit option.
    Tabs will be expanded or not according to the value of -ftabstop.  The origin
@@ -170,6 +175,7 @@
 namespace json { class value; }
 class diagnostic_client_data_hooks;
 class logical_location;
+class diagnostic_diagram;
 
 /* This data structure bundles altogether any information relevant to
    the context of a diagnostic message.  */
@@ -417,6 +423,18 @@
      Used by SARIF output to give metadata about the client that's
      producing diagnostics.  */
   diagnostic_client_data_hooks *m_client_data_hooks;
+
+  /* Support for diagrams.  */
+  struct
+  {
+    /* Theme to use when generating diagrams.
+       Can be NULL (if text art is disabled).  */
+    text_art::theme *m_theme;
+
+    /* Callback for emitting diagrams.  */
+    void (*m_emission_cb) (diagnostic_context *context,
+			   const diagnostic_diagram &diagram);
+  } m_diagrams;
 };
 
 inline void
@@ -619,4 +637,7 @@
 
 extern char *get_cwe_url (int cwe);
 
+extern void diagnostic_emit_diagram (diagnostic_context *context,
+				     const diagnostic_diagram &diagram);
+
 #endif /* ! GCC_DIAGNOSTIC_H */
diff --git a/gcc/doc/cpp.texi b/gcc/doc/cpp.texi
index b0a2ce3..3f492b3 100644
--- a/gcc/doc/cpp.texi
+++ b/gcc/doc/cpp.texi
@@ -1908,9 +1908,10 @@
 @code{201402L} for the 2014 C++ standard,
 @code{201703L} for the 2017 C++ standard,
 @code{202002L} for the 2020 C++ standard,
-or an unspecified value strictly larger than @code{202002L} for the
-experimental languages enabled by @option{-std=c++23} and
-@option{-std=gnu++23}.
+@code{202302L} for the 2023 C++ standard,
+or an unspecified value strictly larger than @code{202302L} for the
+experimental languages enabled by @option{-std=c++26} and
+@option{-std=gnu++26}.
 
 @item __OBJC__
 This macro is defined, with value 1, when the Objective-C compiler is in
diff --git a/gcc/doc/install.texi b/gcc/doc/install.texi
index a38d293..e099cd0 100644
--- a/gcc/doc/install.texi
+++ b/gcc/doc/install.texi
@@ -1095,6 +1095,12 @@
 in which case @option{-fPIC} is used when compiling, and @option{-pie} when
 linking.
 
+@item --enable-host-bind-now
+Specify that the @emph{host} executables should be linked with the option
+@option{-Wl,-z,now}, which means that the dynamic linker will resolve all
+symbols when the executables are started, and that in turn allows RELRO to
+mark the GOT read-only, resulting in better security.
+
 @item @anchor{with-gnu-as}--with-gnu-as
 Specify that the compiler should assume that the
 assembler it finds is the GNU assembler.  However, this does not modify
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 215ab0d..efcf3bf 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -317,7 +317,8 @@
 -fno-show-column
 -fdiagnostics-column-unit=@r{[}display@r{|}byte@r{]}
 -fdiagnostics-column-origin=@var{origin}
--fdiagnostics-escape-format=@r{[}unicode@r{|}bytes@r{]}}
+-fdiagnostics-escape-format=@r{[}unicode@r{|}bytes@r{]}
+-fdiagnostics-text-art-charset=@r{[}none@r{|}ascii@r{|}unicode@r{|}emoji@r{]}}
 
 @item Warning Options
 @xref{Warning Options,,Options to Request or Suppress Warnings}.
@@ -2506,6 +2507,18 @@
 GNU dialect of @option{-std=c++2b}.  Support is highly experimental,
 and will almost certainly change in incompatible ways in future
 releases.
+
+@item c++2c
+@itemx c++26
+The next revision of the ISO C++ standard, planned for
+2026.  Support is highly experimental, and will almost certainly
+change in incompatible ways in future releases.
+
+@item gnu++2c
+@itemx gnu++26
+GNU dialect of @option{-std=c++2c}.  Support is highly experimental,
+and will almost certainly change in incompatible ways in future
+releases.
 @end table
 
 @opindex aux-info
@@ -5078,7 +5091,8 @@
 -fno-diagnostics-show-line-numbers
 -fdiagnostics-color=never
 -fdiagnostics-urls=never
--fdiagnostics-path-format=separate-events}
+-fdiagnostics-path-format=separate-events
+-fdiagnostics-text-art-charset=none}
 In the future, if GCC changes the default appearance of its diagnostics, the
 corresponding option to disable the new behavior will be added to this list.
 
@@ -5604,6 +5618,25 @@
  before<CF><80><BF>after
 @end smallexample
 
+@opindex fdiagnostics-text-art-charset
+@item -fdiagnostics-text-art-charset=@var{CHARSET}
+Some diagnostics can contain ``text art'' diagrams: visualizations created
+from text, intended to be viewed in a monospaced font.
+
+This option selects which characters should be used for printing such
+diagrams, if any.  @var{CHARSET} is @samp{none}, @samp{ascii}, @samp{unicode},
+or @samp{emoji}.
+
+The @samp{none} value suppresses the printing of such diagrams.
+The @samp{ascii} value will ensure that such diagrams are pure ASCII
+(``ASCII art'').  The @samp{unicode} value will allow for conservative use of
+unicode drawing characters (such as box-drawing characters).  The @samp{emoji}
+value further adds the possibility of emoji in the output (such as emitting
+U+26A0 WARNING SIGN followed by U+FE0F VARIATION SELECTOR-16 to select the
+emoji variant of the character).
+
+The default is @samp{emoji}.
+
 @opindex fdiagnostics-format
 @item -fdiagnostics-format=@var{FORMAT}
 Select a different format for printing diagnostics.
@@ -10591,6 +10624,15 @@
 
 See @uref{https://cwe.mitre.org/data/definitions/119.html, CWE-119: Improper Restriction of Operations within the Bounds of a Memory Buffer}.
 
+For cases where the analyzer is able, it will emit a text art diagram
+visualizing the spatial relationship between the memory region that the
+analyzer predicts would be accessed, versus the range of memory that is
+valid to access: whether they overlap, are touching, are close or far
+apart; which one is before or after in memory, the relative sizes
+involved, the direction of the access (read vs write), and, in some
+cases, the values of data involved.  This diagram can be suppressed
+using @option{-fdiagnostics-text-art-charset=none}.
+
 @opindex Wanalyzer-possible-null-argument
 @opindex Wno-analyzer-possible-null-argument
 @item -Wno-analyzer-possible-null-argument
@@ -11101,6 +11143,12 @@
 -Wanalyzer-va-list-use-after-va-end
 }
 
+@opindex fanalyzer-debug-text-art
+@opindex fno-analyzer-debug-text-art
+@item -fanalyzer-debug-text-art-headings
+This option is intended for analyzer developers.  If enabled,
+the analyzer will add extra annotations to any diagrams it generates.
+
 @opindex fanalyzer-feasibility
 @opindex fno-analyzer-feasibility
 @item -fno-analyzer-feasibility
@@ -12077,10 +12125,12 @@
 such as forming of fused multiply-add operations if the target has
 native support for them.
 @option{-ffp-contract=on} enables floating-point expression contraction
-if allowed by the language standard.  This is currently not implemented
-and treated equal to @option{-ffp-contract=off}.
+if allowed by the language standard.  This is implemented for C and C++,
+where it enables contraction within one expression, but not across
+different statements.
 
-The default is @option{-ffp-contract=fast}.
+The default is @option{-ffp-contract=off} for C in a standards compliant mode
+(@option{-std=c11} or similar), @option{-ffp-contract=fast} otherwise.
 
 @opindex fomit-frame-pointer
 @item -fomit-frame-pointer
diff --git a/gcc/doc/optinfo.texi b/gcc/doc/optinfo.texi
index b91bba7..5e8c97e 100644
--- a/gcc/doc/optinfo.texi
+++ b/gcc/doc/optinfo.texi
@@ -100,7 +100,7 @@
 respectively.
 
 @item @code{alt_stream}
-This steam is used for printing optimization specific output in
+This stream is used for printing optimization specific output in
 response to the @option{-fopt-info}. Again a file name can be given. If
 the file name is not given, it defaults to @code{stderr}.
 @end table
diff --git a/gcc/dwarf2out.cc b/gcc/dwarf2out.cc
index e70c47c..9112fc0 100644
--- a/gcc/dwarf2out.cc
+++ b/gcc/dwarf2out.cc
@@ -25105,6 +25105,8 @@
 static const char *
 highest_c_language (const char *lang1, const char *lang2)
 {
+  if (strcmp ("GNU C++26", lang1) == 0 || strcmp ("GNU C++26", lang2) == 0)
+    return "GNU C++26";
   if (strcmp ("GNU C++23", lang1) == 0 || strcmp ("GNU C++23", lang2) == 0)
     return "GNU C++23";
   if (strcmp ("GNU C++20", lang1) == 0 || strcmp ("GNU C++20", lang2) == 0)
@@ -25215,7 +25217,8 @@
 	    language = DW_LANG_C_plus_plus_14;
 	  else if (strcmp (language_string, "GNU C++17") == 0
 		   || strcmp (language_string, "GNU C++20") == 0
-		   || strcmp (language_string, "GNU C++23") == 0)
+		   || strcmp (language_string, "GNU C++23") == 0
+		   || strcmp (language_string, "GNU C++26") == 0)
 	    /* For now.  */
 	    language = DW_LANG_C_plus_plus_14;
 	}
diff --git a/gcc/fold-const.cc b/gcc/fold-const.cc
index 3aa6851..ac90a59 100644
--- a/gcc/fold-const.cc
+++ b/gcc/fold-const.cc
@@ -12564,10 +12564,10 @@
 	tree targ1 = strip_float_extensions (arg1);
 	tree newtype = TREE_TYPE (targ0);
 
-	if (TYPE_PRECISION (TREE_TYPE (targ1)) > TYPE_PRECISION (newtype))
+	if (element_precision (TREE_TYPE (targ1)) > element_precision (newtype))
 	  newtype = TREE_TYPE (targ1);
 
-	if (TYPE_PRECISION (newtype) < TYPE_PRECISION (TREE_TYPE (arg0)))
+	if (element_precision (newtype) < element_precision (TREE_TYPE (arg0)))
 	  return fold_build2_loc (loc, code, type,
 			      fold_convert_loc (loc, newtype, targ0),
 			      fold_convert_loc (loc, newtype, targ1));
@@ -14530,7 +14530,8 @@
 static bool
 tree_simple_nonnegative_warnv_p (enum tree_code code, tree type)
 {
-  if ((TYPE_PRECISION (type) != 1 || TYPE_UNSIGNED (type))
+  if (!VECTOR_TYPE_P (type)
+      && (TYPE_PRECISION (type) != 1 || TYPE_UNSIGNED (type))
       && truth_value_p (code))
     /* Truth values evaluate to 0 or 1, which is nonnegative unless we
        have a signed:1 type (where the value is -1 and 0).  */
diff --git a/gcc/fortran/ChangeLog b/gcc/fortran/ChangeLog
index 147fb1d..b452bc9 100644
--- a/gcc/fortran/ChangeLog
+++ b/gcc/fortran/ChangeLog
@@ -1,3 +1,49 @@
+2023-06-24  Harald Anlauf  <anlauf@gmx.de>
+
+	PR fortran/110360
+	* trans-expr.cc (gfc_conv_procedure_call): Truncate constant string
+	argument of length > 1 passed to scalar CHARACTER(1),VALUE dummy.
+
+2023-06-23  Harald Anlauf  <anlauf@gmx.de>
+
+	PR fortran/110360
+	* trans-expr.cc (gfc_conv_procedure_call): Pass actual argument
+	to scalar CHARACTER(1),VALUE dummy argument by value.
+
+2023-06-21  Paul Thomas  <pault@gcc.gnu.org>
+
+	PR fortran/87477
+	PR fortran/88688
+	PR fortran/94380
+	PR fortran/107900
+	PR fortran/110224
+	* decl.cc (char_len_param_value): Fix memory leak.
+	(resolve_block_construct): Remove unnecessary static decls.
+	* expr.cc (gfc_is_ptr_fcn): New function.
+	(gfc_check_vardef_context): Use it to permit pointer function
+	result selectors to be used for associate names in variable
+	definition context.
+	* gfortran.h: Prototype for gfc_is_ptr_fcn.
+	* match.cc (build_associate_name): New function.
+	(gfc_match_select_type): Use the new function to replace inline
+	version and to build a new associate name for the case where
+	the supplied associate name is already used for that purpose.
+	* resolve.cc (resolve_assoc_var): Call gfc_is_ptr_fcn to allow
+	associate names with pointer function targets to be used in
+	variable definition context.
+	* trans-decl.cc (gfc_get_symbol_decl): Unlimited polymorphic
+	variables need deferred initialisation of the vptr.
+	(gfc_trans_deferred_vars): Do the vptr initialisation.
+	* trans-stmt.cc (trans_associate_var): Ensure that a pointer
+	associate name points to the target of the selector and not
+	the selector itself.
+
+2023-06-21  Paul Thomas  <pault@gcc.gnu.org>
+
+	PR fortran/108961
+	* trans-expr.cc (gfc_conv_procedure_call): The hidden string
+	length must not be passed to a formal arg of type(cptr).
+
 2023-06-20  Tobias Burnus  <tobias@codesourcery.com>
 
 	* dump-parse-tree.cc (show_omp_namelist): Fix dump of the allocator
diff --git a/gcc/fortran/trans-expr.cc b/gcc/fortran/trans-expr.cc
index 3c209bc..63e3cf9 100644
--- a/gcc/fortran/trans-expr.cc
+++ b/gcc/fortran/trans-expr.cc
@@ -6392,6 +6392,30 @@
 		  else
 		    {
 		    gfc_conv_expr (&parmse, e);
+
+		    /* ABI: actual arguments to CHARACTER(len=1),VALUE
+		       dummy arguments are actually passed by value.
+		       Constant strings are truncated to length 1.
+		       The BIND(C) case is handled elsewhere.  */
+		    if (fsym->ts.type == BT_CHARACTER
+			&& !fsym->ts.is_c_interop
+			&& fsym->ts.u.cl->length->expr_type == EXPR_CONSTANT
+			&& fsym->ts.u.cl->length->ts.type == BT_INTEGER
+			&& (mpz_cmp_ui
+			    (fsym->ts.u.cl->length->value.integer, 1) == 0))
+		      {
+			if (e->expr_type != EXPR_CONSTANT)
+			  parmse.expr = gfc_string_to_single_character
+			    (build_int_cst (gfc_charlen_type_node, 1),
+			     parmse.expr,
+			     e->ts.kind);
+			else if (e->value.character.length > 1)
+			  {
+			    e->value.character.length = 1;
+			    gfc_conv_expr (&parmse, e);
+			  }
+		      }
+
 		    if (fsym->attr.optional
 			&& fsym->ts.type != BT_CLASS
 			&& fsym->ts.type != BT_DERIVED)
diff --git a/gcc/function.cc b/gcc/function.cc
index 82102ed..6a79a82 100644
--- a/gcc/function.cc
+++ b/gcc/function.cc
@@ -578,8 +578,8 @@
   tree type;
   /* The alignment (in bits) of the slot.  */
   unsigned int align;
-  /* Nonzero if this temporary is currently in use.  */
-  char in_use;
+  /* True if this temporary is currently in use.  */
+  bool in_use;
   /* Nesting level at which this slot is being used.  */
   int level;
   /* The offset of the slot from the frame_pointer, including extra space
@@ -674,7 +674,7 @@
 {
   cut_slot_from_list (temp, temp_slots_at_level (temp->level));
   insert_slot_to_list (temp, &avail_temp_slots);
-  temp->in_use = 0;
+  temp->in_use = false;
   temp->level = -1;
   n_temp_slots_in_use--;
 }
@@ -848,7 +848,7 @@
 	  if (known_ge (best_p->size - rounded_size, alignment))
 	    {
 	      p = ggc_alloc<temp_slot> ();
-	      p->in_use = 0;
+	      p->in_use = false;
 	      p->size = best_p->size - rounded_size;
 	      p->base_offset = best_p->base_offset + rounded_size;
 	      p->full_size = best_p->full_size - rounded_size;
@@ -918,7 +918,7 @@
     }
 
   p = selected;
-  p->in_use = 1;
+  p->in_use = true;
   p->type = type;
   p->level = temp_slot_level;
   n_temp_slots_in_use++;
@@ -1340,7 +1340,7 @@
   return NULL_RTX;
 }
 
-unsigned int
+void
 emit_initial_value_sets (void)
 {
   struct initial_value_struct *ivs = crtl->hard_reg_initial_vals;
@@ -1348,7 +1348,7 @@
   rtx_insn *seq;
 
   if (ivs == 0)
-    return 0;
+    return;
 
   start_sequence ();
   for (i = 0; i < ivs->num_entries; i++)
@@ -1357,7 +1357,6 @@
   end_sequence ();
 
   emit_insn_at_entry (seq);
-  return 0;
 }
 
 /* Return the hardreg-pseudoreg initial values pair entry I and
@@ -1535,7 +1534,7 @@
 /* A subroutine of instantiate_virtual_regs_in_insn.  Return true if X
    matches the predicate for insn CODE operand OPERAND.  */
 
-static int
+static bool
 safe_insn_predicate (int code, int operand, rtx x)
 {
   return code < 0 || insn_operand_matches ((enum insn_code) code, operand, x);
@@ -1947,7 +1946,7 @@
 /* Pass through the INSNS of function FNDECL and convert virtual register
    references to hard register references.  */
 
-static unsigned int
+static void
 instantiate_virtual_regs (void)
 {
   rtx_insn *insn;
@@ -2001,8 +2000,6 @@
   /* Indicate that, from now on, assign_stack_local should use
      frame_pointer_rtx.  */
   virtuals_instantiated = 1;
-
-  return 0;
 }
 
 namespace {
@@ -2030,7 +2027,8 @@
   /* opt_pass methods: */
   unsigned int execute (function *) final override
     {
-      return instantiate_virtual_regs ();
+      instantiate_virtual_regs ();
+      return 0;
     }
 
 }; // class pass_instantiate_virtual_regs
@@ -2044,12 +2042,12 @@
 }
 
 
-/* Return 1 if EXP is an aggregate type (or a value with aggregate type).
+/* Return true if EXP is an aggregate type (or a value with aggregate type).
    This means a type for which function calls must pass an address to the
    function or get an address back from the function.
    EXP may be a type node or an expression (whose type is tested).  */
 
-int
+bool
 aggregate_value_p (const_tree exp, const_tree fntype)
 {
   const_tree type = (TYPE_P (exp)) ? exp : TREE_TYPE (exp);
@@ -2069,7 +2067,7 @@
 	  else
 	    /* For internal functions, assume nothing needs to be
 	       returned in memory.  */
-	    return 0;
+	    return false;
 	}
 	break;
       case FUNCTION_DECL:
@@ -2087,10 +2085,10 @@
       }
 
   if (VOID_TYPE_P (type))
-    return 0;
+    return false;
 
   if (error_operand_p (fntype))
-    return 0;
+    return false;
 
   /* If a record should be passed the same as its first (and only) member
      don't pass it as an aggregate.  */
@@ -2101,25 +2099,25 @@
      reference, do so.  */
   if ((TREE_CODE (exp) == PARM_DECL || TREE_CODE (exp) == RESULT_DECL)
       && DECL_BY_REFERENCE (exp))
-    return 1;
+    return true;
 
   /* Function types that are TREE_ADDRESSABLE force return in memory.  */
   if (fntype && TREE_ADDRESSABLE (fntype))
-    return 1;
+    return true;
 
   /* Types that are TREE_ADDRESSABLE must be constructed in memory,
      and thus can't be returned in registers.  */
   if (TREE_ADDRESSABLE (type))
-    return 1;
+    return true;
 
   if (TYPE_EMPTY_P (type))
-    return 0;
+    return false;
 
   if (flag_pcc_struct_return && AGGREGATE_TYPE_P (type))
-    return 1;
+    return true;
 
   if (targetm.calls.return_in_memory (type, fntype))
-    return 1;
+    return true;
 
   /* Make sure we have suitable call-clobbered regs to return
      the value in; if not, we must return it in memory.  */
@@ -2128,7 +2126,7 @@
   /* If we have something other than a REG (e.g. a PARALLEL), then assume
      it is OK.  */
   if (!REG_P (reg))
-    return 0;
+    return false;
 
   /* Use the default ABI if the type of the function isn't known.
      The scheme for handling interoperability between different ABIs
@@ -2141,9 +2139,9 @@
   nregs = hard_regno_nregs (regno, TYPE_MODE (type));
   for (i = 0; i < nregs; i++)
     if (!fixed_regs[regno + i] && !abi.clobbers_full_reg_p (regno + i))
-      return 1;
+      return true;
 
-  return 0;
+  return false;
 }
 
 /* Return true if we should assign DECL a pseudo register; false if it
@@ -5733,26 +5731,26 @@
   return hash->find (const_cast<rtx_insn *> (insn)) != NULL;
 }
 
-int
+bool
 prologue_contains (const rtx_insn *insn)
 {
   return contains (insn, prologue_insn_hash);
 }
 
-int
+bool
 epilogue_contains (const rtx_insn *insn)
 {
   return contains (insn, epilogue_insn_hash);
 }
 
-int
+bool
 prologue_epilogue_contains (const rtx_insn *insn)
 {
   if (contains (insn, prologue_insn_hash))
-    return 1;
+    return true;
   if (contains (insn, epilogue_insn_hash))
-    return 1;
-  return 0;
+    return true;
+  return false;
 }
 
 void
@@ -6386,14 +6384,13 @@
 }
 
 
-static unsigned int
+static void
 rest_of_handle_check_leaf_regs (void)
 {
 #ifdef LEAF_REGISTERS
   crtl->uses_only_leaf_regs
     = optimize > 0 && only_leaf_regs_used () && leaf_function_p ();
 #endif
-  return 0;
 }
 
 /* Insert a TYPE into the used types hash table of CFUN.  */
@@ -6518,7 +6515,8 @@
   /* opt_pass methods: */
   unsigned int execute (function *) final override
     {
-      return rest_of_handle_check_leaf_regs ();
+      rest_of_handle_check_leaf_regs ();
+      return 0;
     }
 
 }; // class pass_leaf_regs
@@ -6531,7 +6529,7 @@
   return new pass_leaf_regs (ctxt);
 }
 
-static unsigned int
+static void
 rest_of_handle_thread_prologue_and_epilogue (function *fun)
 {
   /* prepare_shrink_wrap is sensitive to the block structure of the control
@@ -6563,8 +6561,6 @@
   /* The stack usage info is finalized during prologue expansion.  */
   if (flag_stack_usage_info || flag_callgraph_info)
     output_stack_usage ();
-
-  return 0;
 }
 
 /* Record a final call to CALLEE at LOCATION.  */
@@ -6626,7 +6622,8 @@
   /* opt_pass methods: */
   unsigned int execute (function * fun) final override
     {
-      return rest_of_handle_thread_prologue_and_epilogue (fun);
+      rest_of_handle_thread_prologue_and_epilogue (fun);
+      return 0;
     }
 
 }; // class pass_thread_prologue_and_epilogue
diff --git a/gcc/function.h b/gcc/function.h
index d4ce8a7..e290ff5 100644
--- a/gcc/function.h
+++ b/gcc/function.h
@@ -657,11 +657,11 @@
 extern rtx has_hard_reg_initial_val (machine_mode, unsigned int);
 
 /* Called from gimple_expand_cfg.  */
-extern unsigned int emit_initial_value_sets (void);
+extern void emit_initial_value_sets (void);
 
 extern bool initial_value_entry (int i, rtx *, rtx *);
 extern void instantiate_decl_rtl (rtx x);
-extern int aggregate_value_p (const_tree, const_tree);
+extern bool aggregate_value_p (const_tree, const_tree);
 extern bool use_register_for_decl (const_tree);
 extern gimple_seq gimplify_parameters (gimple_seq *);
 extern void locate_and_pad_parm (machine_mode, tree, int, int, int,
@@ -702,9 +702,9 @@
 extern void expand_function_end (void);
 extern rtx get_arg_pointer_save_area (void);
 extern void maybe_copy_prologue_epilogue_insn (rtx, rtx);
-extern int prologue_contains (const rtx_insn *);
-extern int epilogue_contains (const rtx_insn *);
-extern int prologue_epilogue_contains (const rtx_insn *);
+extern bool prologue_contains (const rtx_insn *);
+extern bool epilogue_contains (const rtx_insn *);
+extern bool prologue_epilogue_contains (const rtx_insn *);
 extern void record_prologue_seq (rtx_insn *);
 extern void record_epilogue_seq (rtx_insn *);
 extern void emit_return_into_block (bool simple_p, basic_block bb);
diff --git a/gcc/gcc.cc b/gcc/gcc.cc
index 08bdf28..fdfac0b 100644
--- a/gcc/gcc.cc
+++ b/gcc/gcc.cc
@@ -46,6 +46,7 @@
 #include "spellcheck.h"
 #include "opts-jobserver.h"
 #include "common/common-target.h"
+#include "diagnostic-text-art.h"
 
 #ifndef MATH_LIBRARY
 #define MATH_LIBRARY "m"
@@ -4344,6 +4345,11 @@
 	  break;
 	}
 
+    case OPT_fdiagnostics_text_art_charset_:
+      diagnostics_text_art_charset_init (dc,
+					 (enum diagnostic_text_art_charset)value);
+      break;
+
     case OPT_Wa_:
       {
 	int prev, j;
diff --git a/gcc/gimple-fold.cc b/gcc/gimple-fold.cc
index 55e8056..6d167b1 100644
--- a/gcc/gimple-fold.cc
+++ b/gcc/gimple-fold.cc
@@ -5370,10 +5370,10 @@
   return wi::min_precision (wres, sign) > TYPE_PRECISION (type);
 }
 
-/* If IFN_{MASK,LEN}_LOAD/STORE call CALL is unconditional, return a MEM_REF
-   for the memory it references, otherwise return null.  VECTYPE is the
-   type of the memory vector.  MASK_P indicates it's for MASK if true,
-   otherwise it's for LEN.  */
+/* If IFN_{MASK,LEN,LEN_MASK}_LOAD/STORE call CALL is unconditional,
+   return a MEM_REF for the memory it references, otherwise return null.
+   VECTYPE is the type of the memory vector.  MASK_P indicates it's for
+   MASK if true, otherwise it's for LEN.  */
 
 static tree
 gimple_fold_partial_load_store_mem_ref (gcall *call, tree vectype, bool mask_p)
@@ -5397,9 +5397,21 @@
       unsigned int nargs = gimple_call_num_args (call);
       tree bias = gimple_call_arg (call, nargs - 1);
       gcc_assert (TREE_CODE (bias) == INTEGER_CST);
-      if (maybe_ne (wi::to_poly_widest (basic_len) - wi::to_widest (bias),
-		    GET_MODE_SIZE (TYPE_MODE (vectype))))
+      /* For LEN_LOAD/LEN_STORE/LEN_MASK_LOAD/LEN_MASK_STORE,
+	 we don't fold when (bias + len) != VF.  */
+      if (maybe_ne (wi::to_poly_widest (basic_len) + wi::to_widest (bias),
+		    GET_MODE_NUNITS (TYPE_MODE (vectype))))
 	return NULL_TREE;
+
+      /* For LEN_MASK_{LOAD,STORE}, we should also check whether
+	  the mask is all ones mask.  */
+      internal_fn ifn = gimple_call_internal_fn (call);
+      if (ifn == IFN_LEN_MASK_LOAD || ifn == IFN_LEN_MASK_STORE)
+	{
+	  tree mask = gimple_call_arg (call, internal_fn_mask_index (ifn));
+	  if (!integer_all_onesp (mask))
+	    return NULL_TREE;
+	}
     }
 
   unsigned HOST_WIDE_INT align = tree_to_uhwi (alias_align);
@@ -5438,7 +5450,8 @@
 gimple_fold_partial_store (gimple_stmt_iterator *gsi, gcall *call,
 			   bool mask_p)
 {
-  tree rhs = gimple_call_arg (call, 3);
+  internal_fn ifn = gimple_call_internal_fn (call);
+  tree rhs = gimple_call_arg (call, internal_fn_stored_value_index (ifn));
   if (tree lhs
       = gimple_fold_partial_load_store_mem_ref (call, TREE_TYPE (rhs), mask_p))
     {
@@ -5676,9 +5689,11 @@
 	  changed |= gimple_fold_partial_store (gsi, stmt, true);
 	  break;
 	case IFN_LEN_LOAD:
+	case IFN_LEN_MASK_LOAD:
 	  changed |= gimple_fold_partial_load (gsi, stmt, false);
 	  break;
 	case IFN_LEN_STORE:
+	case IFN_LEN_MASK_STORE:
 	  changed |= gimple_fold_partial_store (gsi, stmt, false);
 	  break;
 	default:
diff --git a/gcc/gimple-predicate-analysis.cc b/gcc/gimple-predicate-analysis.cc
index 7f20f81..373163b 100644
--- a/gcc/gimple-predicate-analysis.cc
+++ b/gcc/gimple-predicate-analysis.cc
@@ -2216,11 +2216,11 @@
     return false;
 
   use_preds.simplify (use_stmt, /*is_use=*/true);
+  use_preds.normalize (use_stmt, /*is_use=*/true);
   if (use_preds.is_false ())
     return true;
   if (use_preds.is_true ())
     return false;
-  use_preds.normalize (use_stmt, /*is_use=*/true);
 
   /* Try to prune the dead incoming phi edges.  */
   if (!overlap (phi, opnds, visited, use_preds))
@@ -2238,11 +2238,11 @@
 	return false;
 
       m_phi_def_preds.simplify (phi);
+      m_phi_def_preds.normalize (phi);
       if (m_phi_def_preds.is_false ())
 	return false;
       if (m_phi_def_preds.is_true ())
 	return true;
-      m_phi_def_preds.normalize (phi);
     }
 
   /* Return true if the predicate guarding the valid definition (i.e.,
diff --git a/gcc/gimple-range-gori.cc b/gcc/gimple-range-gori.cc
index abc70cd..4ee0ae3 100644
--- a/gcc/gimple-range-gori.cc
+++ b/gcc/gimple-range-gori.cc
@@ -1291,13 +1291,26 @@
 {
   Value_Range op_range (TREE_TYPE (name));
 
-  // Calculate a good a range for op2.  Since op1 == op2, this will
-  // have already included whatever the actual range of name is.
-  if (!compute_operand2_range (op_range, handler, lhs, name, src, rel))
+  // If op1 is in the def chain of op2, we'll do the work twice to evalaute
+  // op1.  This can result in an exponential time calculation.
+  // Instead just evaluate op2, which will eventualy get to op1.
+  if (in_chain_p (handler.operand1 (), handler.operand2 ()))
+    return compute_operand2_range (r, handler, lhs, name, src, rel);
+
+  // Likewise if op2 is in the def chain of op1.
+  if (in_chain_p (handler.operand2 (), handler.operand1 ()))
+    return compute_operand1_range (r, handler, lhs, name, src, rel);
+
+  // Calculate a good a range through op2.
+  if (!compute_operand2_range (r, handler, lhs, name, src, rel))
     return false;
 
+  // If op1 == op2 there is again no need to go further.
+  if (handler.operand1 () == handler.operand2 ())
+    return true;
+
   // Now get the range thru op1.
-  if (!compute_operand1_range (r, handler, lhs, name, src, rel))
+  if (!compute_operand1_range (op_range, handler, lhs, name, src, rel))
     return false;
 
   // Both operands have to be simultaneously true, so perform an intersection.
diff --git a/gcc/go/ChangeLog b/gcc/go/ChangeLog
index 507e2d3..931c3be 100644
--- a/gcc/go/ChangeLog
+++ b/gcc/go/ChangeLog
@@ -1,3 +1,8 @@
+2023-06-22  Paul E. Murphy  <murphyp@linux.ibm.com>
+
+	* go-backend.cc [TARGET_AIX]: Rename and update usage to TARGET_AIX_OS.
+	* go-lang.cc: Likewise.
+
 2023-03-24  Jakub Jelinek  <jakub@redhat.com>
 
 	PR middle-end/109258
diff --git a/gcc/go/gccgo.texi b/gcc/go/gccgo.texi
index 4ab1a76..90651af 100644
--- a/gcc/go/gccgo.texi
+++ b/gcc/go/gccgo.texi
@@ -271,6 +271,14 @@
 to a full path to the file.  This option is intended for use by the
 @command{go} command to implement @code{//go:embed}.
 
+@cindex @option{-fgo-importcfg}
+@item -fgo-importcfg=@var{file}
+Identify a file that provides mappings for import package paths found
+in the Go source files.  The file can contain two commands:
+@code{importpath} to rename import paths for vendoring and
+@code{packagefile} to map from package path to files containing export
+data.  This option is intended for use by the @command{go} command.
+
 @cindex @option{-g for gccgo}
 @item -g
 This is the standard @command{gcc} option (@pxref{Debugging Options, ,
diff --git a/gcc/go/go-backend.cc b/gcc/go/go-backend.cc
index c6a1a2b..6e2c919 100644
--- a/gcc/go/go-backend.cc
+++ b/gcc/go/go-backend.cc
@@ -45,8 +45,8 @@
 #define GO_EXPORT_SECTION_NAME ".go_export"
 #endif
 
-#ifndef TARGET_AIX
-#define TARGET_AIX 0
+#ifndef TARGET_AIX_OS
+#define TARGET_AIX_OS 0
 #endif
 
 /* This file holds all the cases where the Go frontend needs
@@ -107,7 +107,7 @@
     {
       gcc_assert (targetm_common.have_named_sections);
       sec = get_section (GO_EXPORT_SECTION_NAME,
-			 TARGET_AIX ? SECTION_EXCLUDE : SECTION_DEBUG,
+			 TARGET_AIX_OS ? SECTION_EXCLUDE : SECTION_DEBUG,
 			 NULL);
     }
 
diff --git a/gcc/go/go-c.h b/gcc/go/go-c.h
index c605038..6a2b57b 100644
--- a/gcc/go/go-c.h
+++ b/gcc/go/go-c.h
@@ -41,6 +41,7 @@
   const char* prefix;
   const char* relative_import_path;
   const char* c_header;
+  const char* importcfg;
   const char* embedcfg;
   Backend* backend;
   Linemap* linemap;
diff --git a/gcc/go/go-lang.cc b/gcc/go/go-lang.cc
index b6e8c37..e85a4bf 100644
--- a/gcc/go/go-lang.cc
+++ b/gcc/go/go-lang.cc
@@ -39,8 +39,8 @@
 #include "go-c.h"
 #include "go-gcc.h"
 
-#ifndef TARGET_AIX
-#define TARGET_AIX 0
+#ifndef TARGET_AIX_OS
+#define TARGET_AIX_OS 0
 #endif
 
 /* Language-dependent contents of a type.  */
@@ -90,6 +90,7 @@
 static const char *go_relative_import_path = NULL;
 static const char *go_c_header = NULL;
 static const char *go_embedcfg = NULL;
+static const char *go_importcfg = NULL;
 
 /* Language hooks.  */
 
@@ -111,14 +112,15 @@
   args.relative_import_path = go_relative_import_path;
   args.c_header = go_c_header;
   args.embedcfg = go_embedcfg;
+  args.importcfg = go_importcfg;
   args.check_divide_by_zero = go_check_divide_zero;
   args.check_divide_overflow = go_check_divide_overflow;
   args.compiling_runtime = go_compiling_runtime;
   args.debug_escape_level = go_debug_escape_level;
   args.debug_escape_hash = go_debug_escape_hash;
-  args.nil_check_size_threshold = TARGET_AIX ? -1 : 4096;
+  args.nil_check_size_threshold = TARGET_AIX_OS ? -1 : 4096;
   args.debug_optimization = go_debug_optimization;
-  args.need_eqtype = TARGET_AIX ? true : false;
+  args.need_eqtype = TARGET_AIX_OS ? true : false;
   args.linemap = go_get_linemap();
   args.backend = go_get_backend();
   go_create_gogo (&args);
@@ -286,6 +288,10 @@
       go_embedcfg = arg;
       break;
 
+    case OPT_fgo_importcfg_:
+      go_importcfg = arg;
+      break;
+
     default:
       /* Just return 1 to indicate that the option is valid.  */
       break;
diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE
index dbb2d68..c44cdc2 100644
--- a/gcc/go/gofrontend/MERGE
+++ b/gcc/go/gofrontend/MERGE
@@ -1,4 +1,4 @@
-6a1d165c2218cd127ee937a1f45599075762f716
+92152c88ea8e2dd9e8c67e91bf4ae5e3edf1b506
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
diff --git a/gcc/go/gofrontend/embed.cc b/gcc/go/gofrontend/embed.cc
index 0584f70..6dada5e 100644
--- a/gcc/go/gofrontend/embed.cc
+++ b/gcc/go/gofrontend/embed.cc
@@ -19,8 +19,8 @@
 
 // Read a file into *DATA.  Returns false on error.
 
-static bool
-read_file(const char* filename, Location loc, std::string* data)
+bool
+Gogo::read_file(const char* filename, Location loc, std::string* data)
 {
   int fd = open(filename, O_RDONLY | O_BINARY);
   if (fd < 0)
@@ -346,7 +346,8 @@
 bool
 Embedcfg_reader::initialize_from_file()
 {
-  if (!read_file(this->filename_, Linemap::unknown_location(), &this->data_))
+  if (!Gogo::read_file(this->filename_, Linemap::unknown_location(),
+		       &this->data_))
     return false;
   if (this->data_.empty())
     {
@@ -849,7 +850,7 @@
 	}
 
       std::string data;
-      if (!read_file(this->embed_files_[paths[0]].c_str(), loc, &data))
+      if (!Gogo::read_file(this->embed_files_[paths[0]].c_str(), loc, &data))
 	return Expression::make_error(loc);
 
       Expression* e = Expression::make_string(data, loc);
@@ -909,7 +910,7 @@
       std::string data;
       if ((*pp)[pp->size() - 1] != '/')
 	{
-	  if (!read_file(this->embed_files_[*pp].c_str(), loc, &data))
+	  if (!Gogo::read_file(this->embed_files_[*pp].c_str(), loc, &data))
 	    return Expression::make_error(loc);
 	}
 
diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc
index 4ac55af..d276bd8 100644
--- a/gcc/go/gofrontend/expressions.cc
+++ b/gcc/go/gofrontend/expressions.cc
@@ -12272,7 +12272,8 @@
           return Runtime::make_call(code, loc, 3, a1, a2, a3);
         }
     }
-  else if (package == "internal/abi")
+  else if (package == "internal/abi"
+	   || package == "bootstrap/internal/abi") // for bootstrapping gc
     {
       if (is_method)
 	return NULL;
@@ -18307,6 +18308,16 @@
   return TRAVERSE_CONTINUE;
 }
 
+// Determine type of a slice value.
+
+void
+Slice_value_expression::do_determine_type(const Type_context*)
+{
+  this->valmem_->determine_type_no_context();
+  this->len_->determine_type_no_context();
+  this->cap_->determine_type_no_context();
+}
+
 Expression*
 Slice_value_expression::do_copy()
 {
diff --git a/gcc/go/gofrontend/expressions.h b/gcc/go/gofrontend/expressions.h
index 3d7e787..bdb7ccd 100644
--- a/gcc/go/gofrontend/expressions.h
+++ b/gcc/go/gofrontend/expressions.h
@@ -4364,8 +4364,7 @@
   { return this->type_; }
 
   void
-  do_determine_type(const Type_context*)
-  { }
+  do_determine_type(const Type_context*);
 
   Expression*
   do_copy();
@@ -4419,7 +4418,7 @@
 
   void
   do_determine_type(const Type_context*)
-  { }
+  { this->slice_->determine_type_no_context(); }
 
   Expression*
   do_copy()
diff --git a/gcc/go/gofrontend/go.cc b/gcc/go/gofrontend/go.cc
index 1512770..66d4816 100644
--- a/gcc/go/gofrontend/go.cc
+++ b/gcc/go/gofrontend/go.cc
@@ -40,6 +40,8 @@
     ::gogo->set_compiling_runtime(args->compiling_runtime);
   if (args->c_header != NULL)
     ::gogo->set_c_header(args->c_header);
+  if (args->importcfg != NULL)
+    ::gogo->read_importcfg(args->importcfg);
   if (args->embedcfg != NULL)
     ::gogo->read_embedcfg(args->embedcfg);
   ::gogo->set_debug_escape_level(args->debug_escape_level);
diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc
index 9197eef..fa3cd6e 100644
--- a/gcc/go/gofrontend/gogo.cc
+++ b/gcc/go/gofrontend/gogo.cc
@@ -52,6 +52,10 @@
     prefix_from_option_(false),
     relative_import_path_(),
     c_header_(),
+    import_map_(),
+    package_file_(),
+    embed_patterns_(),
+    embed_files_(),
     check_divide_by_zero_(true),
     check_divide_overflow_(true),
     compiling_runtime_(false),
@@ -517,7 +521,20 @@
       return;
     }
 
-  Import::Stream* stream = Import::open_package(filename, location,
+  // If we are using an importcfg file we have to check two mappings.
+  // IMPORT_MAP_ is a mapping from package path to real package path,
+  // for vendoring.  PACKAGE_FILE_ is a mapping from package path to
+  // file name, to find the file in the build cache.
+  std::string path = filename;
+  Unordered_map(std::string, std::string)::const_iterator pi;
+  pi = this->import_map_.find(filename);
+  if (pi != this->import_map_.end())
+    path = pi->second;
+  pi = this->package_file_.find(path);
+  if (pi != this->package_file_.end())
+    path = pi->second;
+
+  Import::Stream* stream = Import::open_package(path, location,
 						this->relative_import_path_);
   if (stream == NULL)
     {
@@ -3296,6 +3313,9 @@
   int
   expression(Expression**);
 
+  static bool
+  skip_descriptor(Gogo* gogo, const Named_object*);
+
  private:
   Gogo* gogo_;
 };
@@ -3306,6 +3326,9 @@
 int
 Create_function_descriptors::function(Named_object* no)
 {
+  if (Create_function_descriptors::skip_descriptor(this->gogo_, no))
+    return TRAVERSE_CONTINUE;
+
   if (no->is_function()
       && no->func_value()->enclosing() == NULL
       && !no->func_value()->is_method()
@@ -3393,6 +3416,28 @@
   return TRAVERSE_CONTINUE;
 }
 
+// The gc compiler has some special cases that it always compiles as
+// intrinsics.  For those we don't want to generate a function
+// descriptor, as there will be no code for it to refer to.
+
+bool
+Create_function_descriptors::skip_descriptor(Gogo* gogo,
+					     const Named_object* no)
+{
+  const std::string& pkgpath(no->package() == NULL
+			     ? gogo->pkgpath()
+			     : no->package()->pkgpath());
+
+  // internal/abi is the standard library package,
+  // bootstrap/internal/abi is the name used when bootstrapping the gc
+  // compiler.
+
+  return ((pkgpath == "internal/abi"
+	   || pkgpath == "bootstrap/internal/abi")
+	  && (no->name() == "FuncPCABI0"
+	      || no->name() == "FuncPCABIInternal"));
+}
+
 // Create function descriptors as needed.  We need a function
 // descriptor for all exported functions and for all functions that
 // are referenced without being called.
@@ -3414,7 +3459,8 @@
       if (no->is_function_declaration()
 	  && !no->func_declaration_value()->type()->is_method()
 	  && !Linemap::is_predeclared_location(no->location())
-	  && !Gogo::is_hidden_name(no->name()))
+	  && !Gogo::is_hidden_name(no->name())
+	  && !Create_function_descriptors::skip_descriptor(this, no))
 	fndecls.push_back(no);
     }
   for (std::vector<Named_object*>::const_iterator p = fndecls.begin();
diff --git a/gcc/go/gofrontend/gogo.h b/gcc/go/gofrontend/gogo.h
index c08a16b..4fd45bf 100644
--- a/gcc/go/gofrontend/gogo.h
+++ b/gcc/go/gofrontend/gogo.h
@@ -393,6 +393,10 @@
   set_c_header(const std::string& s)
   { this->c_header_ = s; }
 
+  // Read an importcfg file.
+  void
+  read_importcfg(const char* filename);
+
   // Read an embedcfg file.
   void
   read_embedcfg(const char* filename);
@@ -1126,6 +1130,10 @@
   static size_t
   special_name_pos(const std::string& name);
 
+  // Read a file into memory.
+  static bool
+  read_file(const char* filename, Location loc, std::string* data);
+
  private:
   // During parsing, we keep a stack of functions.  Each function on
   // the stack is one that we are currently parsing.  For each
@@ -1295,6 +1303,10 @@
   std::string relative_import_path_;
   // The C header file to write, from the -fgo-c-header option.
   std::string c_header_;
+  // Mapping from imports in the source file to the real import paths.
+  Unordered_map(std::string, std::string) import_map_;
+  // Mapping from import paths to files to read.
+  Unordered_map(std::string, std::string) package_file_;
   // Patterns from an embedcfg file.
   Embed_patterns embed_patterns_;
   // Mapping from file to full path from an embedcfg file.
diff --git a/gcc/go/gofrontend/import.cc b/gcc/go/gofrontend/import.cc
index 6a5491b..21691fa 100644
--- a/gcc/go/gofrontend/import.cc
+++ b/gcc/go/gofrontend/import.cc
@@ -34,6 +34,130 @@
   search_path.push_back(std::string(path));
 }
 
+// Read an importcfg file.
+
+void
+Gogo::read_importcfg(const char* filename)
+{
+  std::string data;
+  if (!Gogo::read_file(filename, Linemap::unknown_location(), &data))
+    return;
+  const char* p = data.data();
+  const char* pend = p + data.length();
+  int lineno = 0;
+  const char *pnext = NULL;
+  for (; p < pend; p = pnext)
+    {
+      // Line numbers start at 1.
+      lineno++;
+
+      // Find end of line.
+      const char* pnl = static_cast<const char*>(memchr(p, '\n', pend - p));
+      if (pnl != NULL)
+	pnext = pnl + 1;
+      else
+	{
+	  pnl = pend;
+	  pnext = pnl;
+	}
+
+      // Trim leading spaces.
+      while (p < pnl)
+	{
+	  unsigned int rune;
+	  int rune_len = Lex::fetch_char(p, &rune);
+	  if (rune_len == 0)
+	    {
+	      go_error_at(Linemap::unknown_location(),
+			  "%s:%d: invalid character in importcfg file",
+			  filename, lineno);
+	      return;
+	    }
+	  if (!Lex::is_unicode_space(rune))
+	    break;
+	  p += rune_len;
+	}
+
+      // Trim trailing spaces.
+      while (pnl > p)
+	{
+	  size_t start = pnl - p - 1;
+	  unsigned int rune = (unsigned char)p[start];
+	  int rune_len = 1;
+	  if (rune > 0x7f)
+	    {
+	      for (start--; start > 0; start--)
+		{
+		  unsigned char c = p[start];
+		  if ((c & 0xc0) != 0x80)
+		    break;
+		}
+	      rune_len = Lex::fetch_char(p + start, &rune);
+	      if (static_cast<size_t>(rune_len) != (pnl - p) - start)
+		{
+		  go_error_at(Linemap::unknown_location(),
+			      "%s:%d: invalid character in importcfg file",
+			      filename, lineno);
+		  return;
+		}
+	    }
+	  if (!Lex::is_unicode_space(rune))
+	    break;
+	  pnl -= rune_len;
+	}
+
+      // Skip empty lines and comment lines.
+      if (p == pnl || *p == '#')
+	continue;
+
+      size_t verb_len;
+      const char* psp = static_cast<const char*>(memchr(p, ' ', pnl - p));
+      if (psp == NULL)
+	verb_len = pnl - p;
+      else
+	verb_len = psp - p;
+
+      bool importmap = false;
+      bool packagefile = false;
+      if (strncmp(p, "importmap", verb_len) == 0)
+	importmap = true;
+      else if (strncmp(p, "packagefile", verb_len) == 0)
+	packagefile = true;
+      else
+	{
+	  go_error_at(Linemap::unknown_location(),
+		      "%s:%d: unknown directive in importcfg file",
+		      filename, lineno);
+	  return;
+	}
+
+      const char* peq;
+      if (psp == NULL)
+	peq = NULL;
+      else
+	{
+	  psp++;
+	  peq = static_cast<const char*>(memchr(psp, '=', pnl - psp));
+	}
+      if (peq == NULL || peq + 1 == pnl)
+	{
+	  go_error_at(Linemap::unknown_location(),
+		      "%s:%d: invalid syntax in importcfg file",
+		      filename, lineno);
+	  return;
+	}
+
+      std::string first(psp, peq - psp);
+      std::string second(peq + 1, pnl - (peq + 1));
+      if (importmap)
+	this->import_map_[first] = second;
+      else if (packagefile)
+	this->package_file_[first] = second;
+      else
+	go_unreachable();
+    }
+}
+
 // Find import data.  This searches the file system for FILENAME and
 // returns a pointer to a Stream object to read the data that it
 // exports.  If the file is not found, it returns NULL.
diff --git a/gcc/go/lang.opt b/gcc/go/lang.opt
index 4ca989c..0d658fc 100644
--- a/gcc/go/lang.opt
+++ b/gcc/go/lang.opt
@@ -61,6 +61,10 @@
 Go Joined RejectNegative
 -fgo-embedcfg=<file>	List embedded files via go:embed.
 
+fgo-importcfg=
+Go Joined RejectNegative
+-fgo-importcfg=<file>	Provide file that tells where to find imports.
+
 fgo-optimize-
 Go Joined
 -fgo-optimize-<type>	Turn on optimization passes in the frontend.
diff --git a/gcc/internal-fn.cc b/gcc/internal-fn.cc
index c911ae7..9017176 100644
--- a/gcc/internal-fn.cc
+++ b/gcc/internal-fn.cc
@@ -2949,7 +2949,7 @@
  * OPTAB.  */
 
 static void
-expand_partial_store_optab_fn (internal_fn, gcall *stmt, convert_optab optab)
+expand_partial_store_optab_fn (internal_fn ifn, gcall *stmt, convert_optab optab)
 {
   class expand_operand ops[5];
   tree type, lhs, rhs, maskt, biast;
@@ -2957,7 +2957,7 @@
   insn_code icode;
 
   maskt = gimple_call_arg (stmt, 2);
-  rhs = gimple_call_arg (stmt, 3);
+  rhs = gimple_call_arg (stmt, internal_fn_stored_value_index (ifn));
   type = TREE_TYPE (rhs);
   lhs = expand_call_mem_ref (type, stmt, 0);
 
@@ -2991,7 +2991,7 @@
       maskt = gimple_call_arg (stmt, 3);
       mask = expand_normal (maskt);
       create_input_operand (&ops[3], mask, TYPE_MODE (TREE_TYPE (maskt)));
-      biast = gimple_call_arg (stmt, 4);
+      biast = gimple_call_arg (stmt, 5);
       bias = expand_normal (biast);
       create_input_operand (&ops[4], bias, QImode);
       icode = convert_optab_handler (optab, TYPE_MODE (type), GET_MODE (mask));
@@ -4435,6 +4435,7 @@
     case IFN_GATHER_LOAD:
     case IFN_MASK_GATHER_LOAD:
     case IFN_LEN_LOAD:
+    case IFN_LEN_MASK_LOAD:
       return true;
 
     default:
@@ -4455,6 +4456,7 @@
     case IFN_SCATTER_STORE:
     case IFN_MASK_SCATTER_STORE:
     case IFN_LEN_STORE:
+    case IFN_LEN_MASK_STORE:
       return true;
 
     default:
@@ -4498,6 +4500,10 @@
     case IFN_MASK_SCATTER_STORE:
       return 4;
 
+    case IFN_LEN_MASK_LOAD:
+    case IFN_LEN_MASK_STORE:
+      return 3;
+
     default:
       return (conditional_internal_fn_code (fn) != ERROR_MARK
 	      || get_unconditional_internal_fn (fn) != IFN_LAST ? 0 : -1);
@@ -4519,6 +4525,9 @@
     case IFN_LEN_STORE:
       return 3;
 
+    case IFN_LEN_MASK_STORE:
+      return 4;
+
     default:
       return -1;
     }
@@ -4583,13 +4592,33 @@
 {
   optab optab = direct_internal_fn_optab (ifn);
   insn_code icode = direct_optab_handler (optab, mode);
+  int bias_opno = 3;
+
+  if (icode == CODE_FOR_nothing)
+    {
+      machine_mode mask_mode;
+      if (!targetm.vectorize.get_mask_mode (mode).exists (&mask_mode))
+	return VECT_PARTIAL_BIAS_UNSUPPORTED;
+      if (ifn == IFN_LEN_LOAD)
+	{
+	  /* Try LEN_MASK_LOAD.  */
+	  optab = direct_internal_fn_optab (IFN_LEN_MASK_LOAD);
+	}
+      else
+	{
+	  /* Try LEN_MASK_STORE.  */
+	  optab = direct_internal_fn_optab (IFN_LEN_MASK_STORE);
+	}
+      icode = convert_optab_handler (optab, mode, mask_mode);
+      bias_opno = 4;
+    }
 
   if (icode != CODE_FOR_nothing)
     {
       /* For now we only support biases of 0 or -1.  Try both of them.  */
-      if (insn_operand_matches (icode, 3, GEN_INT (0)))
+      if (insn_operand_matches (icode, bias_opno, GEN_INT (0)))
 	return 0;
-      if (insn_operand_matches (icode, 3, GEN_INT (-1)))
+      if (insn_operand_matches (icode, bias_opno, GEN_INT (-1)))
 	return -1;
     }
 
diff --git a/gcc/match.pd b/gcc/match.pd
index 2dd2382..33ccda3 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -6034,10 +6034,10 @@
 	     type1 = double_type_node;
 	 }
       tree newtype
-        = (TYPE_PRECISION (TREE_TYPE (@00)) > TYPE_PRECISION (type1)
+        = (element_precision (TREE_TYPE (@00)) > element_precision (type1)
 	   ? TREE_TYPE (@00) : type1);
      }
-     (if (TYPE_PRECISION (TREE_TYPE (@0)) > TYPE_PRECISION (newtype))
+     (if (element_precision (TREE_TYPE (@0)) > element_precision (newtype))
       (cmp (convert:newtype @00) (convert:newtype @10))))))))
 
 
@@ -7428,9 +7428,9 @@
 	      && newtype == type
 	      && types_match (newtype, type))
 	    (op (convert:newtype @1) (convert:newtype @2))
-	    (with { if (TYPE_PRECISION (ty1) > TYPE_PRECISION (newtype))
+	    (with { if (element_precision (ty1) > element_precision (newtype))
 		      newtype = ty1;
-		    if (TYPE_PRECISION (ty2) > TYPE_PRECISION (newtype))
+		    if (element_precision (ty2) > element_precision (newtype))
 		      newtype = ty2; }
 	       /* Sometimes this transformation is safe (cannot
 		  change results through affecting double rounding
@@ -7453,9 +7453,9 @@
 		  exponent range for the product or ratio of two
 		  values representable in the TYPE to be within the
 		  range of normal values of ITYPE.  */
-	      (if (TYPE_PRECISION (newtype) < TYPE_PRECISION (itype)
+	      (if (element_precision (newtype) < element_precision (itype)
 		   && (flag_unsafe_math_optimizations
-		       || (TYPE_PRECISION (newtype) == TYPE_PRECISION (type)
+		       || (element_precision (newtype) == element_precision (type)
 			   && real_can_shorten_arithmetic (TYPE_MODE (itype),
 							   TYPE_MODE (type))
 			   && !excess_precision_type (newtype)))
@@ -8672,7 +8672,14 @@
 
 (simplify
  (vec_perm vec_same_elem_p@0 @0 @1)
- @0)
+ (if (types_match (type, TREE_TYPE (@0)))
+  @0
+  (with
+   {
+     tree elem = uniform_vector_p (@0);
+   }
+   (if (elem)
+    { build_vector_from_val (type, elem); }))))
 
 /* Push VEC_PERM earlier if that may help FMA perception (PR101895).  */
 (simplify
diff --git a/gcc/optabs-tree.cc b/gcc/optabs-tree.cc
index 77bf745..e6ae159 100644
--- a/gcc/optabs-tree.cc
+++ b/gcc/optabs-tree.cc
@@ -543,19 +543,50 @@
 	  && optab_handler (ot, TYPE_MODE (type)) != CODE_FOR_nothing);
 }
 
-/* Return true if target supports vector masked load/store for mode.  */
+/* Return true if the target has support for masked load/store.
+   We can support masked load/store by either mask{load,store}
+   or len_mask{load,store}.
+   This helper function checks whether target supports masked
+   load/store and return corresponding IFN in the last argument
+   (IFN_MASK_{LOAD,STORE} or IFN_LEN_MASK_{LOAD,STORE}).  */
+
+static bool
+target_supports_mask_load_store_p (machine_mode mode, machine_mode mask_mode,
+				   bool is_load, internal_fn *ifn)
+{
+  optab op = is_load ? maskload_optab : maskstore_optab;
+  optab len_op = is_load ? len_maskload_optab : len_maskstore_optab;
+  if (convert_optab_handler (op, mode, mask_mode) != CODE_FOR_nothing)
+    {
+      if (ifn)
+	*ifn = is_load ? IFN_MASK_LOAD : IFN_MASK_STORE;
+      return true;
+    }
+  else if (convert_optab_handler (len_op, mode, mask_mode) != CODE_FOR_nothing)
+    {
+      if (ifn)
+	*ifn = is_load ? IFN_LEN_MASK_LOAD : IFN_LEN_MASK_STORE;
+      return true;
+    }
+  return false;
+}
+
+/* Return true if target supports vector masked load/store for mode.
+   An additional output in the last argument which is the IFN pointer.
+   We set IFN as MASK_{LOAD,STORE} or LEN_MASK_{LOAD,STORE} according
+   which optab is supported in the target.  */
 
 bool
 can_vec_mask_load_store_p (machine_mode mode,
 			   machine_mode mask_mode,
-			   bool is_load)
+			   bool is_load,
+			   internal_fn *ifn)
 {
-  optab op = is_load ? maskload_optab : maskstore_optab;
   machine_mode vmode;
 
   /* If mode is vector mode, check it directly.  */
   if (VECTOR_MODE_P (mode))
-    return convert_optab_handler (op, mode, mask_mode) != CODE_FOR_nothing;
+    return target_supports_mask_load_store_p (mode, mask_mode, is_load, ifn);
 
   /* Otherwise, return true if there is some vector mode with
      the mask load/store supported.  */
@@ -569,7 +600,7 @@
   vmode = targetm.vectorize.preferred_simd_mode (smode);
   if (VECTOR_MODE_P (vmode)
       && targetm.vectorize.get_mask_mode (vmode).exists (&mask_mode)
-      && convert_optab_handler (op, vmode, mask_mode) != CODE_FOR_nothing)
+      && target_supports_mask_load_store_p (vmode, mask_mode, is_load, ifn))
     return true;
 
   auto_vector_modes vector_modes;
@@ -577,33 +608,66 @@
   for (machine_mode base_mode : vector_modes)
     if (related_vector_mode (base_mode, smode).exists (&vmode)
 	&& targetm.vectorize.get_mask_mode (vmode).exists (&mask_mode)
-	&& convert_optab_handler (op, vmode, mask_mode) != CODE_FOR_nothing)
+	&& target_supports_mask_load_store_p (vmode, mask_mode, is_load, ifn))
       return true;
   return false;
 }
 
+/* Return true if the target has support for len load/store.
+   We can support len load/store by either len_{load,store}
+   or len_mask{load,store}.
+   This helper function checks whether target supports len
+   load/store and return corresponding IFN in the last argument
+   (IFN_LEN_{LOAD,STORE} or IFN_LEN_MASK_{LOAD,STORE}).  */
+
+static bool
+target_supports_len_load_store_p (machine_mode mode, bool is_load,
+				  internal_fn *ifn)
+{
+  optab op = is_load ? len_load_optab : len_store_optab;
+  optab masked_op = is_load ? len_maskload_optab : len_maskstore_optab;
+
+  if (direct_optab_handler (op, mode))
+    {
+      if (ifn)
+	*ifn = is_load ? IFN_LEN_LOAD : IFN_LEN_STORE;
+      return true;
+    }
+  machine_mode mask_mode;
+  if (targetm.vectorize.get_mask_mode (mode).exists (&mask_mode)
+      && convert_optab_handler (masked_op, mode, mask_mode) != CODE_FOR_nothing)
+    {
+      if (ifn)
+	*ifn = is_load ? IFN_LEN_MASK_LOAD : IFN_LEN_MASK_STORE;
+      return true;
+    }
+  return false;
+}
+
 /* If target supports vector load/store with length for vector mode MODE,
    return the corresponding vector mode, otherwise return opt_machine_mode ().
    There are two flavors for vector load/store with length, one is to measure
    length with bytes, the other is to measure length with lanes.
    As len_{load,store} optabs point out, for the flavor with bytes, we use
-   VnQI to wrap the other supportable same size vector modes.  */
+   VnQI to wrap the other supportable same size vector modes.
+   An additional output in the last argument which is the IFN pointer.
+   We set IFN as LEN_{LOAD,STORE} or LEN_MASK_{LOAD,STORE} according
+   which optab is supported in the target.  */
 
 opt_machine_mode
-get_len_load_store_mode (machine_mode mode, bool is_load)
+get_len_load_store_mode (machine_mode mode, bool is_load, internal_fn *ifn)
 {
-  optab op = is_load ? len_load_optab : len_store_optab;
   gcc_assert (VECTOR_MODE_P (mode));
 
   /* Check if length in lanes supported for this mode directly.  */
-  if (direct_optab_handler (op, mode))
+  if (target_supports_len_load_store_p (mode, is_load, ifn))
     return mode;
 
   /* Check if length in bytes supported for same vector size VnQI.  */
   machine_mode vmode;
   poly_uint64 nunits = GET_MODE_SIZE (mode);
   if (related_vector_mode (mode, QImode, nunits).exists (&vmode)
-      && direct_optab_handler (op, vmode))
+      && target_supports_len_load_store_p (vmode, is_load, ifn))
     return vmode;
 
   return opt_machine_mode ();
diff --git a/gcc/optabs-tree.h b/gcc/optabs-tree.h
index a3f79b6..e421fc2 100644
--- a/gcc/optabs-tree.h
+++ b/gcc/optabs-tree.h
@@ -47,7 +47,9 @@
 void init_tree_optimization_optabs (tree);
 bool target_supports_op_p (tree, enum tree_code,
 			   enum optab_subtype = optab_default);
-bool can_vec_mask_load_store_p (machine_mode, machine_mode, bool);
-opt_machine_mode get_len_load_store_mode (machine_mode, bool);
+bool can_vec_mask_load_store_p (machine_mode, machine_mode, bool,
+				internal_fn * = nullptr);
+opt_machine_mode get_len_load_store_mode (machine_mode, bool,
+					  internal_fn * = nullptr);
 
 #endif
diff --git a/gcc/opts-common.cc b/gcc/opts-common.cc
index 23ddcaa..f0c5f48 100644
--- a/gcc/opts-common.cc
+++ b/gcc/opts-common.cc
@@ -1068,6 +1068,7 @@
 	    "-fdiagnostics-color=never",
 	    "-fdiagnostics-urls=never",
 	    "-fdiagnostics-path-format=separate-events",
+	    "-fdiagnostics-text-art-charset=none"
 	  };
 	  const int num_expanded = ARRAY_SIZE (expanded_args);
 	  opt_array_len += num_expanded - 1;
diff --git a/gcc/opts.cc b/gcc/opts.cc
index 86b94d6..3087bda 100644
--- a/gcc/opts.cc
+++ b/gcc/opts.cc
@@ -35,6 +35,7 @@
 #include "version.h"
 #include "selftest.h"
 #include "file-prefix-map.h"
+#include "diagnostic-text-art.h"
 
 /* In this file all option sets are explicit.  */
 #undef OPTION_SET_P
@@ -2887,6 +2888,11 @@
 	  break;
 	}
 
+    case OPT_fdiagnostics_text_art_charset_:
+      diagnostics_text_art_charset_init (dc,
+					 (enum diagnostic_text_art_charset)value);
+      break;
+
     case OPT_fdiagnostics_parseable_fixits:
       dc->extra_output_kind = (value
 			       ? EXTRA_DIAGNOSTIC_OUTPUT_fixits_v1
diff --git a/gcc/pretty-print.cc b/gcc/pretty-print.cc
index 7d29471..3d789a2 100644
--- a/gcc/pretty-print.cc
+++ b/gcc/pretty-print.cc
@@ -1828,6 +1828,35 @@
   pp_maybe_wrap_text (pp, str, str + strlen (str));
 }
 
+/* Append code point C to the output area of PRETTY-PRINTER, encoding it
+   as UTF-8.  */
+
+void
+pp_unicode_character (pretty_printer *pp, unsigned c)
+{
+  static const uchar masks[6] =  { 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+  static const uchar limits[6] = { 0x80, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE };
+  size_t nbytes;
+  uchar buf[6], *p = &buf[6];
+
+  nbytes = 1;
+  if (c < 0x80)
+    *--p = c;
+  else
+    {
+      do
+	{
+	  *--p = ((c & 0x3F) | 0x80);
+	  c >>= 6;
+	  nbytes++;
+	}
+      while (c >= 0x3F || (c & limits[nbytes-1]));
+      *--p = (c | masks[nbytes-1]);
+    }
+
+  pp_append_r (pp, (const char *)p, nbytes);
+}
+
 /* Append the leading N characters of STRING to the output area of
    PRETTY-PRINTER, quoting in hexadecimal non-printable characters.
    Setting N = -1 is as if N were set to strlen (STRING).  The STRING
diff --git a/gcc/pretty-print.h b/gcc/pretty-print.h
index 0230a28..369be6e 100644
--- a/gcc/pretty-print.h
+++ b/gcc/pretty-print.h
@@ -401,6 +401,7 @@
 extern void pp_newline (pretty_printer *);
 extern void pp_character (pretty_printer *, int);
 extern void pp_string (pretty_printer *, const char *);
+extern void pp_unicode_character (pretty_printer *, unsigned);
 
 extern void pp_write_text_to_stream (pretty_printer *);
 extern void pp_write_text_as_dot_label_to_stream (pretty_printer *, bool);
diff --git a/gcc/rust/ChangeLog b/gcc/rust/ChangeLog
index 25b8ddf..4854d71 100644
--- a/gcc/rust/ChangeLog
+++ b/gcc/rust/ChangeLog
@@ -1,3 +1,8 @@
+2023-06-22  Paul E. Murphy  <murphyp@linux.ibm.com>
+
+	* rust-object-export.cc [TARGET_AIX]: Rename and update usage to
+	TARGET_AIX_OS.
+
 2023-04-06  Owen Avery  <powerboat9.gamer@gmail.com>
 
 	* parse/rust-parse-impl.h
diff --git a/gcc/rust/rust-object-export.cc b/gcc/rust/rust-object-export.cc
index 1143c76..f9a395f 100644
--- a/gcc/rust/rust-object-export.cc
+++ b/gcc/rust/rust-object-export.cc
@@ -46,8 +46,8 @@
 #define RUST_EXPORT_SECTION_NAME ".rust_export"
 #endif
 
-#ifndef TARGET_AIX
-#define TARGET_AIX 0
+#ifndef TARGET_AIX_OS
+#define TARGET_AIX_OS 0
 #endif
 
 /* Return whether or not GCC has reported any errors.  */
@@ -91,7 +91,7 @@
     {
       gcc_assert (targetm_common.have_named_sections);
       sec = get_section (RUST_EXPORT_SECTION_NAME,
-			 TARGET_AIX ? SECTION_EXCLUDE : SECTION_DEBUG, NULL);
+			 TARGET_AIX_OS ? SECTION_EXCLUDE : SECTION_DEBUG, NULL);
     }
 
   switch_to_section (sec);
diff --git a/gcc/selftest-run-tests.cc b/gcc/selftest-run-tests.cc
index 915f212..e2fc8f8 100644
--- a/gcc/selftest-run-tests.cc
+++ b/gcc/selftest-run-tests.cc
@@ -28,6 +28,7 @@
 #include "stringpool.h"
 #include "attribs.h"
 #include "analyzer/analyzer-selftests.h"
+#include "text-art/selftests.h"
 
 /* This function needed to be split out from selftest.cc as it references
    tests from the whole source tree, and so is within
@@ -118,6 +119,8 @@
   /* Run any lang-specific selftests.  */
   lang_hooks.run_lang_selftests ();
 
+  text_art_tests ();
+
   /* Run the analyzer selftests (if enabled).  */
   ana::selftest::run_analyzer_selftests ();
 
diff --git a/gcc/simplify-rtx.cc b/gcc/simplify-rtx.cc
index 1b58144..99cbdd4 100644
--- a/gcc/simplify-rtx.cc
+++ b/gcc/simplify-rtx.cc
@@ -7758,6 +7758,38 @@
 	return CONST0_RTX (outermode);
     }
 
+  /* Optimize SUBREGS of scalar integral ASHIFT by a valid constant.  */
+  if (GET_CODE (op) == ASHIFT
+      && SCALAR_INT_MODE_P (innermode)
+      && CONST_INT_P (XEXP (op, 1))
+      && INTVAL (XEXP (op, 1)) > 0
+      && known_gt (GET_MODE_BITSIZE (innermode), INTVAL (XEXP (op, 1))))
+    {
+      HOST_WIDE_INT val = INTVAL (XEXP (op, 1));
+      /* A lowpart SUBREG of a ASHIFT by a constant may fold to zero.  */
+      if (known_eq (subreg_lowpart_offset (outermode, innermode), byte)
+	  && known_le (GET_MODE_BITSIZE (outermode), val))
+        return CONST0_RTX (outermode);
+      /* Optimize the highpart SUBREG of a suitable ASHIFT (ZERO_EXTEND).  */
+      if (GET_CODE (XEXP (op, 0)) == ZERO_EXTEND
+	  && GET_MODE (XEXP (XEXP (op, 0), 0)) == outermode
+	  && known_eq (GET_MODE_BITSIZE (outermode), val)
+	  && known_eq (GET_MODE_BITSIZE (innermode), 2 * val)
+	  && known_eq (subreg_highpart_offset (outermode, innermode), byte))
+	return XEXP (XEXP (op, 0), 0);
+    }
+
+  /* Attempt to simplify WORD_MODE SUBREGs of bitwise expressions.  */
+  if (outermode == word_mode
+      && (GET_CODE (op) == IOR || GET_CODE (op) == XOR || GET_CODE (op) == AND)
+      && SCALAR_INT_MODE_P (innermode))
+    {
+      rtx op0 = simplify_subreg (outermode, XEXP (op, 0), innermode, byte);
+      rtx op1 = simplify_subreg (outermode, XEXP (op, 1), innermode, byte);
+      if (op0 && op1)
+	return simplify_gen_binary (GET_CODE (op), outermode, op0, op1);
+    }
+
   scalar_int_mode int_outermode, int_innermode;
   if (is_a <scalar_int_mode> (outermode, &int_outermode)
       && is_a <scalar_int_mode> (innermode, &int_innermode)
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 3e3f0ad..470cff2 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,261 @@
+2023-06-25  Juzhe-Zhong  <juzhe.zhong@rivai.ai>
+
+	* gcc.target/riscv/rvv/autovec/partial/select_vl-1.c: Add dump checks.
+	* gcc.target/riscv/rvv/autovec/partial/select_vl-2.c: New test.
+
+2023-06-25  Li Xu  <xuli1@eswincomputing.com>
+
+	* gcc.target/riscv/rvv/base/vlmul_ext-2.c: New test.
+
+2023-06-25  Juzhe-Zhong  <juzhe.zhong@rivai.ai>
+
+	* gcc.target/riscv/rvv/autovec/partial/single_rgroup-2.c: New test.
+	* gcc.target/riscv/rvv/autovec/partial/single_rgroup-2.h: New test.
+	* gcc.target/riscv/rvv/autovec/partial/single_rgroup-3.c: New test.
+	* gcc.target/riscv/rvv/autovec/partial/single_rgroup-3.h: New test.
+	* gcc.target/riscv/rvv/autovec/partial/single_rgroup_run-2.c: New test.
+	* gcc.target/riscv/rvv/autovec/partial/single_rgroup_run-3.c: New test.
+
+2023-06-25  Pan Li  <pan2.li@intel.com>
+
+	* gcc.target/riscv/rvv/base/abi-10.c: Revert.
+	* gcc.target/riscv/rvv/base/abi-11.c: Ditto.
+	* gcc.target/riscv/rvv/base/abi-12.c: Ditto.
+	* gcc.target/riscv/rvv/base/abi-15.c: Ditto.
+	* gcc.target/riscv/rvv/base/abi-8.c: Ditto.
+	* gcc.target/riscv/rvv/base/abi-9.c: Ditto.
+	* gcc.target/riscv/rvv/base/abi-17.c: Ditto.
+	* gcc.target/riscv/rvv/base/abi-18.c: Ditto.
+
+2023-06-25  Pan Li  <pan2.li@intel.com>
+
+	* gcc.target/riscv/rvv/base/tuple-28.c: Removed.
+	* gcc.target/riscv/rvv/base/tuple-29.c: Removed.
+	* gcc.target/riscv/rvv/base/tuple-30.c: Removed.
+	* gcc.target/riscv/rvv/base/tuple-31.c: Removed.
+	* gcc.target/riscv/rvv/base/tuple-32.c: Removed.
+
+2023-06-25  liuhongt  <hongtao.liu@intel.com>
+
+	* gcc.target/i386/pr110309.c: New test.
+
+2023-06-25  yulong  <shiyulong@iscas.ac.cn>
+
+	* gcc.target/riscv/rvv/base/abi-10.c: Add float16 tuple type case.
+	* gcc.target/riscv/rvv/base/abi-11.c: Ditto.
+	* gcc.target/riscv/rvv/base/abi-12.c: Ditto.
+	* gcc.target/riscv/rvv/base/abi-15.c: Ditto.
+	* gcc.target/riscv/rvv/base/abi-8.c: Ditto.
+	* gcc.target/riscv/rvv/base/abi-9.c: Ditto.
+	* gcc.target/riscv/rvv/base/abi-17.c: New test.
+	* gcc.target/riscv/rvv/base/abi-18.c: New test.
+
+2023-06-24  Juzhe-Zhong  <juzhe.zhong@rivai.ai>
+
+	* gcc.target/riscv/rvv/autovec/ternop/ternop-1.c: Adjust tests.
+	* gcc.target/riscv/rvv/autovec/ternop/ternop-2.c: Ditto.
+	* gcc.target/riscv/rvv/autovec/ternop/ternop-3.c: Ditto.
+	* gcc.target/riscv/rvv/autovec/ternop/ternop-4.c: Ditto.
+	* gcc.target/riscv/rvv/autovec/ternop/ternop-5.c: Ditto.
+	* gcc.target/riscv/rvv/autovec/ternop/ternop-6.c: Ditto.
+	* gcc.target/riscv/rvv/autovec/ternop/ternop_run-1.c: Ditto.
+	* gcc.target/riscv/rvv/autovec/ternop/ternop_run-2.c: Ditto.
+	* gcc.target/riscv/rvv/autovec/ternop/ternop_run-3.c: Ditto.
+	* gcc.target/riscv/rvv/autovec/ternop/ternop_run-4.c: Ditto.
+	* gcc.target/riscv/rvv/autovec/ternop/ternop_run-5.c: Ditto.
+	* gcc.target/riscv/rvv/autovec/ternop/ternop_run-6.c: Ditto.
+	* gcc.target/riscv/rvv/autovec/ternop/ternop-10.c: New test.
+	* gcc.target/riscv/rvv/autovec/ternop/ternop-11.c: New test.
+	* gcc.target/riscv/rvv/autovec/ternop/ternop-12.c: New test.
+	* gcc.target/riscv/rvv/autovec/ternop/ternop-7.c: New test.
+	* gcc.target/riscv/rvv/autovec/ternop/ternop-8.c: New test.
+	* gcc.target/riscv/rvv/autovec/ternop/ternop-9.c: New test.
+	* gcc.target/riscv/rvv/autovec/ternop/ternop_run-10.c: New test.
+	* gcc.target/riscv/rvv/autovec/ternop/ternop_run-11.c: New test.
+	* gcc.target/riscv/rvv/autovec/ternop/ternop_run-12.c: New test.
+	* gcc.target/riscv/rvv/autovec/ternop/ternop_run-7.c: New test.
+	* gcc.target/riscv/rvv/autovec/ternop/ternop_run-8.c: New test.
+	* gcc.target/riscv/rvv/autovec/ternop/ternop_run-9.c: New test.
+	* gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-1.c: New test.
+	* gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-10.c: New test.
+	* gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-11.c: New test.
+	* gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-12.c: New test.
+	* gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-2.c: New test.
+	* gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-3.c: New test.
+	* gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-4.c: New test.
+	* gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-5.c: New test.
+	* gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-6.c: New test.
+	* gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-7.c: New test.
+	* gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-8.c: New test.
+	* gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-9.c: New test.
+
+2023-06-24  David Malcolm  <dmalcolm@redhat.com>
+
+	* gcc.dg/plugin/diagnostic_plugin_test_text_art.c: Add
+	#define INCLUDE_VECTOR.
+
+2023-06-23  David Malcolm  <dmalcolm@redhat.com>
+
+	PR c++/110164
+	* g++.dg/diagnostic/missing-header-pr110164.C: New test.
+
+2023-06-23  Marek Polacek  <polacek@redhat.com>
+
+	* lib/target-supports.exp (check_effective_target_c++23): Return
+	1 also if check_effective_target_c++26.
+	(check_effective_target_c++23_down): New.
+	(check_effective_target_c++26_only): New.
+	(check_effective_target_c++26): New.
+	* g++.dg/cpp23/cplusplus.C: Adjust expected value.
+	* g++.dg/cpp26/cplusplus.C: New test.
+
+2023-06-23  Harald Anlauf  <anlauf@gmx.de>
+
+	PR fortran/110360
+	* gfortran.dg/value_9.f90: New test.
+
+2023-06-23   Michael Meissner  <meissner@linux.ibm.com>
+	    Aaron Sawdey   <acsawdey@linux.ibm.com>
+
+	PR target/105325
+	* g++.target/powerpc/pr105325.C: New test.
+	* gcc.target/powerpc/fusion-p10-ldcmpi.c: Update insn counts.
+
+2023-06-23  Iain Sandoe  <iain@sandoe.co.uk>
+
+	* objc-obj-c++-shared/GNUStep/Foundation/NSObjCRuntime.h: Make
+	this header use pragma system_header.
+
+2023-06-23  Prathamesh Kulkarni  <prathamesh.kulkarni@linaro.org>
+
+	PR tree-optimization/110280
+	* gcc.target/aarch64/sve/pr110280.c: New test.
+
+2023-06-22  Richard Biener  <rguenther@suse.de>
+
+	PR tree-optimization/110332
+	* g++.dg/torture/pr110332.C: New testcase.
+	* gcc.dg/torture/pr110332-1.c: Likewise.
+	* gcc.dg/torture/pr110332-2.c: Likewise.
+
+2023-06-22  Roger Sayle  <roger@nextmovesoftware.com>
+	    Uros Bizjak  <ubizjak@gmail.com>
+
+	* gcc.target/i386/avx-vptest-4.c: New test case.
+	* gcc.target/i386/avx-vptest-5.c: Likewise.
+	* gcc.target/i386/avx-vptest-6.c: Likewise.
+	* gcc.target/i386/pr109973-1.c: Update test case.
+	* gcc.target/i386/pr109973-2.c: Likewise.
+	* gcc.target/i386/sse4_1-ptest-4.c: New test case.
+	* gcc.target/i386/sse4_1-ptest-5.c: Likewise.
+	* gcc.target/i386/sse4_1-ptest-6.c: Likewise.
+
+2023-06-22  David Malcolm  <dmalcolm@redhat.com>
+
+	PR analyzer/106626
+	* gcc.dg/analyzer/data-model-1.c (test_16): Update for
+	out-of-bounds working.
+	* gcc.dg/analyzer/out-of-bounds-diagram-1-ascii.c: New test.
+	* gcc.dg/analyzer/out-of-bounds-diagram-1-debug.c: New test.
+	* gcc.dg/analyzer/out-of-bounds-diagram-1-emoji.c: New test.
+	* gcc.dg/analyzer/out-of-bounds-diagram-1-json.c: New test.
+	* gcc.dg/analyzer/out-of-bounds-diagram-1-sarif.c: New test.
+	* gcc.dg/analyzer/out-of-bounds-diagram-1-unicode.c: New test.
+	* gcc.dg/analyzer/out-of-bounds-diagram-10.c: New test.
+	* gcc.dg/analyzer/out-of-bounds-diagram-11.c: New test.
+	* gcc.dg/analyzer/out-of-bounds-diagram-12.c: New test.
+	* gcc.dg/analyzer/out-of-bounds-diagram-13.c: New test.
+	* gcc.dg/analyzer/out-of-bounds-diagram-14.c: New test.
+	* gcc.dg/analyzer/out-of-bounds-diagram-15.c: New test.
+	* gcc.dg/analyzer/out-of-bounds-diagram-2.c: New test.
+	* gcc.dg/analyzer/out-of-bounds-diagram-3.c: New test.
+	* gcc.dg/analyzer/out-of-bounds-diagram-4.c: New test.
+	* gcc.dg/analyzer/out-of-bounds-diagram-5-ascii.c: New test.
+	* gcc.dg/analyzer/out-of-bounds-diagram-5-unicode.c: New test.
+	* gcc.dg/analyzer/out-of-bounds-diagram-6.c: New test.
+	* gcc.dg/analyzer/out-of-bounds-diagram-7.c: New test.
+	* gcc.dg/analyzer/out-of-bounds-diagram-8.c: New test.
+	* gcc.dg/analyzer/out-of-bounds-diagram-9.c: New test.
+	* gcc.dg/analyzer/pattern-test-2.c: Update expected results.
+	* gcc.dg/analyzer/pr101962.c: Update expected results.
+	* gcc.dg/plugin/analyzer_gil_plugin.c:  Add logger param to
+	pending_diagnostic::emit implementations.
+
+2023-06-22  David Malcolm  <dmalcolm@redhat.com>
+
+	* gcc.dg/plugin/diagnostic-test-text-art-ascii-bw.c: New test.
+	* gcc.dg/plugin/diagnostic-test-text-art-ascii-color.c: New test.
+	* gcc.dg/plugin/diagnostic-test-text-art-none.c: New test.
+	* gcc.dg/plugin/diagnostic-test-text-art-unicode-bw.c: New test.
+	* gcc.dg/plugin/diagnostic-test-text-art-unicode-color.c: New test.
+	* gcc.dg/plugin/diagnostic_plugin_test_text_art.c: New test plugin.
+	* gcc.dg/plugin/plugin.exp (plugin_test_list): Add them.
+
+2023-06-22  David Malcolm  <dmalcolm@redhat.com>
+
+	* c-c++-common/Wlogical-not-parentheses-2.c: Split up the
+	multiline directive.
+	* gcc.dg/analyzer/malloc-macro-inline-events.c: Remove redundant
+	dg-regexp directives.
+	* gcc.dg/missing-header-fixit-5.c: Split up the multiline
+	directives.
+	* lib/gcc-dg.exp (gcc-dg-prune): Move call to
+	handle-multiline-outputs from prune_gcc_output to here.
+	* lib/multiline.exp (dg-end-multiline-output): Move call to
+	maybe-handle-nn-line-numbers from prune_gcc_output to here.
+	* lib/prune.exp (prune_gcc_output): Move calls to
+	maybe-handle-nn-line-numbers and handle-multiline-outputs from
+	here to the above.
+
+2023-06-21  Paul Thomas  <pault@gcc.gnu.org>
+
+	PR fortran/87477
+	PR fortran/107900
+	PR fortran/110224
+	PR fortran/88688
+	PR fortran/94380
+	PR fortran/95398
+	* gfortran.dg/pr107900.f90 : New test
+	* gfortran.dg/pr110224.f90 : New test
+	* gfortran.dg/pr88688.f90 : New test
+	* gfortran.dg/pr94380.f90 : New test
+	* gfortran.dg/pr95398.f90 : Set -std=f2008, bump the line
+	numbers in the error tests by two and change the text in two.
+
+2023-06-21  Paul Thomas  <pault@gcc.gnu.org>
+
+	PR fortran/108961
+	* gfortran.dg/pr108961.f90: New test.
+
+2023-06-21  Uros Bizjak  <ubizjak@gmail.com>
+
+	PR target/110018
+	* gcc.target/i386/pr110018-1.c: Use explicit signed types.
+	* gcc.target/i386/pr110018-2.c: New test.
+
+2023-06-21  Kyrylo Tkachov  <kyrylo.tkachov@arm.com>
+
+	* gcc.target/aarch64/sve/gather_earlyclobber.c: New test.
+	* gcc.target/aarch64/sve2/gather_earlyclobber.c: New test.
+
+2023-06-21  Richard Biener  <rguenther@suse.de>
+
+	* gcc.target/i386/vect-partial-vectors-1.c: New testcase.
+	* gcc.target/i386/vect-partial-vectors-2.c: Likewise.
+
+2023-06-21  Jan Beulich  <jbeulich@suse.com>
+
+	* gcc.target/i386/avx512f-copysign.c: New test.
+
+2023-06-21  Jan Beulich  <jbeulich@suse.com>
+
+	* gcc.target/i386/avx512f-dupv2di.c: Add
+	-mprefer-vector-width=512.
+
+2023-06-21  liuhongt  <hongtao.liu@intel.com>
+
+	* gcc.target/i386/pr110018-1.c: New test.
+
 2023-06-20  Lewis Hyatt  <lhyatt@gmail.com>
 
 	PR c++/66290
diff --git a/gcc/testsuite/c-c++-common/Wlogical-not-parentheses-2.c b/gcc/testsuite/c-c++-common/Wlogical-not-parentheses-2.c
index ba8dce8..2d93820 100644
--- a/gcc/testsuite/c-c++-common/Wlogical-not-parentheses-2.c
+++ b/gcc/testsuite/c-c++-common/Wlogical-not-parentheses-2.c
@@ -12,6 +12,8 @@
 /* { dg-begin-multiline-output "" }
    r += !aaa == bbb;
              ^~
+   { dg-end-multiline-output "" } */
+/* { dg-begin-multiline-output "" }
    r += !aaa == bbb;
         ^~~~
         (   )
diff --git a/gcc/testsuite/g++.dg/cpp23/cplusplus.C b/gcc/testsuite/g++.dg/cpp23/cplusplus.C
index 29a941b..ff331c2 100644
--- a/gcc/testsuite/g++.dg/cpp23/cplusplus.C
+++ b/gcc/testsuite/g++.dg/cpp23/cplusplus.C
@@ -1,4 +1,4 @@
 // { dg-do compile }
 // { dg-options "-std=c++23" }
 
-static_assert(__cplusplus > 202002L);
+static_assert(__cplusplus == 202302L);
diff --git a/gcc/testsuite/g++.dg/cpp26/cplusplus.C b/gcc/testsuite/g++.dg/cpp26/cplusplus.C
new file mode 100644
index 0000000..08c502e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/cplusplus.C
@@ -0,0 +1,3 @@
+// { dg-do compile { target c++26_only } }
+
+static_assert(__cplusplus > 202302L);
diff --git a/gcc/testsuite/g++.dg/diagnostic/missing-header-pr110164.C b/gcc/testsuite/g++.dg/diagnostic/missing-header-pr110164.C
new file mode 100644
index 0000000..1598007
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/missing-header-pr110164.C
@@ -0,0 +1,10 @@
+// { dg-require-effective-target c++11 }
+
+#include <map>
+
+std::array<int, 10> a1; /* { dg-error "incomplete type" } */
+/* { dg-message "'std::array' is defined in header '<array>'; this is probably fixable by adding '#include <array>'" "hint" { target *-*-* } .-1 } */
+
+std::array<int, 10> a2 {5}; /* { dg-error "incomplete type" } */
+/* { dg-message "'std::array' is defined in header '<array>'; this is probably fixable by adding '#include <array>'" "hint" { target *-*-* } .-1 } */
+
diff --git a/gcc/testsuite/g++.dg/torture/pr110332.C b/gcc/testsuite/g++.dg/torture/pr110332.C
new file mode 100644
index 0000000..31dc93e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/pr110332.C
@@ -0,0 +1,16 @@
+// { dg-do compile }
+
+struct SlotIndex { int lie; };
+SlotIndex si7, si8;
+
+unsigned u9, u6;
+bool b3, b4;
+unsigned &value() {
+  return b4 ? u6 : u9;
+}
+void transferValues() {
+  unsigned RegIdx;
+  SlotIndex End;
+  RegIdx = value();
+  End = b3 ? si7 : si8;
+}
diff --git a/gcc/testsuite/g++.target/powerpc/pr105325.C b/gcc/testsuite/g++.target/powerpc/pr105325.C
new file mode 100644
index 0000000..18a2e52
--- /dev/null
+++ b/gcc/testsuite/g++.target/powerpc/pr105325.C
@@ -0,0 +1,28 @@
+/* { dg-do assemble } */
+/* { dg-require-effective-target lp64 } */
+/* { dg-require-effective-target power10_ok } */
+/* { dg-require-effective-target powerpc_prefixed_addr } */
+/* { dg-options "-O2 -mdejagnu-cpu=power10 -fstack-protector" } */
+
+/* PR target/105324.  Test that power10 fusion does not generate an LWA/CMPDI
+   with a large offset that the assembler rejects.  Instead it should a
+   PLWZ/CMPWI combination.
+
+   Originally, the code was dying because the fusion load + compare -1/0/1
+   patterns did not handle the possibility that the load might be prefixed.
+   The -fstack-protector option is needed to show the bug.  */
+
+struct Ath__array1D {
+  int _current;
+  int getCnt() { return _current; }
+};
+struct extMeasure {
+  int _mapTable[10000];
+  Ath__array1D _metRCTable;
+};
+void measureRC() {
+  extMeasure m;
+  for (; m._metRCTable.getCnt();)
+    for (;;)
+      ;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-1.c b/gcc/testsuite/gcc.dg/analyzer/data-model-1.c
index 86d1ccf..3c4a45f 100644
--- a/gcc/testsuite/gcc.dg/analyzer/data-model-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-1.c
@@ -240,8 +240,8 @@
   __analyzer_eval (strlen (msg) == 11); /* { dg-warning "TRUE" } */
 
   /* Out-of-bounds.  */
-  __analyzer_eval (msg[100] == 'e'); /* { dg-warning "UNKNOWN" } */
-  // TODO: some kind of warning for the out-of-bounds access
+  __analyzer_eval (msg[100] == 'e'); /* { dg-warning "UNKNOWN" "eval result" } */
+  /* { dg-warning "buffer over-read" "out-of-bounds" { target *-*-* } .-1 } */
 }
 
 static const char *__attribute__((noinline))
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-macro-inline-events.c b/gcc/testsuite/gcc.dg/analyzer/malloc-macro-inline-events.c
index f08aee6..9134bb4 100644
--- a/gcc/testsuite/gcc.dg/analyzer/malloc-macro-inline-events.c
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-macro-inline-events.c
@@ -12,11 +12,6 @@
   WRAPPED_FREE (ptr); /* { dg-message "in expansion of macro 'WRAPPED_FREE'" } */
   WRAPPED_FREE (ptr); /* { dg-message "in expansion of macro 'WRAPPED_FREE'" } */
 
-  /* Erase the spans indicating the header file
-     (to avoid embedding path assumptions).  */
-  /* { dg-regexp "\[^|\]+/malloc-macro.h:\[0-9\]+:\[0-9\]+:" } */
-  /* { dg-regexp "\[^|\]+/malloc-macro.h:\[0-9\]+:\[0-9\]+:" } */
-
   /* { dg-begin-multiline-output "" }
    NN | #define WRAPPED_FREE(PTR) free(PTR)
       |                           ^~~~~~~~~
diff --git a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-1-ascii.c b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-1-ascii.c
new file mode 100644
index 0000000..5e6eadc
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-1-ascii.c
@@ -0,0 +1,55 @@
+/* { dg-additional-options "-fdiagnostics-show-path-depths" } */
+/* { dg-additional-options "-fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+/* { dg-additional-options "-fdiagnostics-text-art-charset=ascii" } */
+
+#include <stdint.h>
+
+int32_t arr[10];
+
+void int_arr_write_element_after_end_off_by_one(int32_t x)
+{
+  arr[10] = x;  /* { dg-line line } */
+}
+/* { dg-warning "buffer overflow" "warning" { target *-*-* } line } */
+/* { dg-message "valid subscripts for 'arr' are '\\\[0\\\]' to '\\\[9\\\]'" "valid subscript note" { target *-*-* } line } */
+
+
+/* { dg-begin-multiline-output "" }
+   arr[10] = x;
+   ~~~~~~~~^~~
+  event 1 (depth 0)
+    |
+    | int32_t arr[10];
+    |         ^~~
+    |         |
+    |         (1) capacity: 40 bytes
+    |
+    +--> 'int_arr_write_element_after_end_off_by_one': event 2 (depth 1)
+           |
+           |   arr[10] = x;
+           |   ~~~~~~~~^~~
+           |           |
+           |           (2) out-of-bounds write from byte 40 till byte 43 but 'arr' ends at byte 40
+           |
+   { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+
+                                        +--------------------------------+
+                                        |write from 'x' (type: 'int32_t')|
+                                        +--------------------------------+
+                                                        |
+                                                        |
+                                                        v
+  +--------+-----------------+---------++--------------------------------+
+  |  [0]   |       ...       |   [9]   ||                                |
+  +--------+-----------------+---------+|       after valid range        |
+  |    'arr' (type: 'int32_t[10]')     ||                                |
+  +------------------------------------++--------------------------------+
+  |~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~||~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~|
+                    |                                   |
+          +---------+--------+                +---------+---------+
+          |capacity: 40 bytes|                |overflow of 4 bytes|
+          +------------------+                +-------------------+
+
+   { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-1-debug.c b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-1-debug.c
new file mode 100644
index 0000000..4c4d9d1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-1-debug.c
@@ -0,0 +1,40 @@
+/* Test of -fanalyzer-debug-text-art.  */
+
+/* { dg-additional-options "-fdiagnostics-text-art-charset=ascii -fanalyzer-debug-text-art" } */
+
+#include <stdint.h>
+
+int32_t arr[10];
+
+void int_arr_write_element_after_end_off_by_one(int32_t x)
+{
+  arr[10] = x;  /* { dg-line line } */
+}
+/* { dg-warning "buffer overflow" "warning" { target *-*-* } line } */
+/* { dg-message "valid subscripts for 'arr' are '\\\[0\\\]' to '\\\[9\\\]'" "valid subscript note" { target *-*-* } line } */
+
+/* { dg-begin-multiline-output "" }
+
+  +---------+-----------+-----------+---+--------------------------------+
+  |   tc0   |    tc1    |    tc2    |tc3|              tc4               |
+  +---------+-----------+-----------+---+--------------------------------+
+  |bytes 0-3|bytes 4-35 |bytes 36-39|   |          bytes 40-43           |
+  +---------+-----------+-----------+   +--------------------------------+
+                                        +--------------------------------+
+                                        |write from 'x' (type: 'int32_t')|
+                                        +--------------------------------+
+                                                        |
+                                                        |
+                                                        v
+  +---------+-----------+-----------+   +--------------------------------+
+  |   [0]   |    ...    |    [9]    |   |                                |
+  +---------+-----------+-----------+   |       after valid range        |
+  |   'arr' (type: 'int32_t[10]')   |   |                                |
+  +---------------------------------+   +--------------------------------+
+  |~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~|   |~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~|
+                   |                                    |
+         +---------+--------+                 +---------+---------+
+         |capacity: 40 bytes|                 |overflow of 4 bytes|
+         +------------------+                 +-------------------+
+
+   { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-1-emoji.c b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-1-emoji.c
new file mode 100644
index 0000000..1c61252
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-1-emoji.c
@@ -0,0 +1,55 @@
+/* { dg-additional-options "-fdiagnostics-show-path-depths" } */
+/* { dg-additional-options "-fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+/* { dg-additional-options "-fdiagnostics-text-art-charset=emoji" } */
+
+#include <stdint.h>
+
+int32_t arr[10];
+
+void int_arr_write_element_after_end_off_by_one(int32_t x)
+{
+  arr[10] = x;  /* { dg-line line } */
+}
+/* { dg-warning "buffer overflow" "warning" { target *-*-* } line } */
+/* { dg-message "valid subscripts for 'arr' are '\\\[0\\\]' to '\\\[9\\\]'" "valid subscript note" { target *-*-* } line } */
+
+
+/* { dg-begin-multiline-output "" }
+   arr[10] = x;
+   ~~~~~~~~^~~
+  event 1 (depth 0)
+    |
+    | int32_t arr[10];
+    |         ^~~
+    |         |
+    |         (1) capacity: 40 bytes
+    |
+    +--> 'int_arr_write_element_after_end_off_by_one': event 2 (depth 1)
+           |
+           |   arr[10] = x;
+           |   ~~~~~~~~^~~
+           |           |
+           |           (2) out-of-bounds write from byte 40 till byte 43 but 'arr' ends at byte 40
+           |
+   { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+
+                                        ┌────────────────────────────────┐
+                                        │write from 'x' (type: 'int32_t')│
+                                        └────────────────────────────────┘
+                                                        │
+                                                        │
+                                                        v
+  ┌────────┬─────────────────┬─────────┐┌────────────────────────────────┐
+  │  [0]   │       ...       │   [9]   ││                                │
+  ├────────┴─────────────────┴─────────┤│       after valid range        │
+  │    'arr' (type: 'int32_t[10]')     ││                                │
+  └────────────────────────────────────┘└────────────────────────────────┘
+  ├─────────────────┬──────────────────┤├───────────────┬────────────────┤
+                    │                                   │
+          ╭─────────┴────────╮              ╭───────────┴──────────╮
+          │capacity: 40 bytes│              │⚠️  overflow of 4 bytes│
+          ╰──────────────────╯              ╰──────────────────────╯
+
+   { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-1-json.c b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-1-json.c
new file mode 100644
index 0000000..0a2cc34
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-1-json.c
@@ -0,0 +1,13 @@
+/* { dg-additional-options "-fdiagnostics-format=json-file" } */
+
+/* The custom JSON format doesn't support text art, so this is just a simple
+   smoketext.  */
+
+#include <stdint.h>
+
+int32_t arr[10];
+
+void int_arr_write_element_after_end_off_by_one(int32_t x)
+{
+  arr[10] = x;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-1-sarif.c b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-1-sarif.c
new file mode 100644
index 0000000..051a1ce
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-1-sarif.c
@@ -0,0 +1,24 @@
+/* We require -fdiagnostics-text-art-charset= to get any text art here
+   because of the test suite using -fdiagnostics-plain-output.  */
+
+/* { dg-additional-options "-fdiagnostics-format=sarif-file -fdiagnostics-text-art-charset=ascii" } */
+
+#include <stdint.h>
+
+int32_t arr[10];
+
+void int_arr_write_element_after_end_off_by_one(int32_t x)
+{
+  arr[10] = x;
+}
+
+/* Verify that some JSON was written to a file with the expected name.
+
+   { dg-final { verify-sarif-file } }
+
+   Expect the "alt-text" to be captured.
+     { dg-final { scan-sarif-file "\"text\": \"Diagram visualizing the predicted out-of-bounds access\"," } }
+
+   Expect the diagram to have 4 leading spaces (to indicate a code block),
+   and that at least part of the diagram was written out.
+     { dg-final { scan-sarif-file "\"markdown\": \"    .*capacity: 40 bytes.*\"" } } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-1-unicode.c b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-1-unicode.c
new file mode 100644
index 0000000..71f66ff
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-1-unicode.c
@@ -0,0 +1,55 @@
+/* { dg-additional-options "-fdiagnostics-show-path-depths" } */
+/* { dg-additional-options "-fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+/* { dg-additional-options "-fdiagnostics-text-art-charset=unicode" } */
+
+#include <stdint.h>
+
+int32_t arr[10];
+
+void int_arr_write_element_after_end_off_by_one(int32_t x)
+{
+  arr[10] = x;  /* { dg-line line } */
+}
+/* { dg-warning "buffer overflow" "warning" { target *-*-* } line } */
+/* { dg-message "valid subscripts for 'arr' are '\\\[0\\\]' to '\\\[9\\\]'" "valid subscript note" { target *-*-* } line } */
+
+
+/* { dg-begin-multiline-output "" }
+   arr[10] = x;
+   ~~~~~~~~^~~
+  event 1 (depth 0)
+    |
+    | int32_t arr[10];
+    |         ^~~
+    |         |
+    |         (1) capacity: 40 bytes
+    |
+    +--> 'int_arr_write_element_after_end_off_by_one': event 2 (depth 1)
+           |
+           |   arr[10] = x;
+           |   ~~~~~~~~^~~
+           |           |
+           |           (2) out-of-bounds write from byte 40 till byte 43 but 'arr' ends at byte 40
+           |
+   { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+
+                                        ┌────────────────────────────────┐
+                                        │write from 'x' (type: 'int32_t')│
+                                        └────────────────────────────────┘
+                                                        │
+                                                        │
+                                                        v
+  ┌────────┬─────────────────┬─────────┐┌────────────────────────────────┐
+  │  [0]   │       ...       │   [9]   ││                                │
+  ├────────┴─────────────────┴─────────┤│       after valid range        │
+  │    'arr' (type: 'int32_t[10]')     ││                                │
+  └────────────────────────────────────┘└────────────────────────────────┘
+  ├─────────────────┬──────────────────┤├───────────────┬────────────────┤
+                    │                                   │
+          ╭─────────┴────────╮                ╭─────────┴─────────╮
+          │capacity: 40 bytes│                │overflow of 4 bytes│
+          ╰──────────────────╯                ╰───────────────────╯
+
+   { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-10.c b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-10.c
new file mode 100644
index 0000000..4a7b8e3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-10.c
@@ -0,0 +1,29 @@
+/* { dg-additional-options "-fdiagnostics-text-art-charset=unicode" } */
+
+#include <stdint.h>
+#include <stdlib.h>
+
+int32_t int_vla_write_element_symbolic_before_start (int32_t x, size_t n)
+{
+  int32_t arr[n]; /* { dg-message "\\(1\\) capacity: 'n \\* 4' bytes" } */
+  arr[-2] = 42;  /* { dg-warning "stack-based buffer underwrite" } */
+}
+
+/* { dg-begin-multiline-output "" }
+
+  ┌───────────────────┐
+  │write of '(int) 42'│
+  └───────────────────┘
+            │
+            │
+            v
+  ┌───────────────────┐                 ┌────────────────────────────────┐
+  │before valid range │                 │buffer allocated on stack at (1)│
+  └───────────────────┘                 └────────────────────────────────┘
+  ├─────────┬─────────┤├───────┬───────┤├───────────────┬────────────────┤
+            │                  │                        │
+  ╭─────────┴───────────╮  ╭───┴───╮        ╭───────────┴───────────╮
+  │underwrite of 4 bytes│  │4 bytes│        │capacity: 'n * 4' bytes│
+  ╰─────────────────────╯  ╰───────╯        ╰───────────────────────╯
+
+   { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-11.c b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-11.c
new file mode 100644
index 0000000..f8eb158
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-11.c
@@ -0,0 +1,82 @@
+/* { dg-additional-options "-fdiagnostics-text-art-charset=unicode" } */
+/* { dg-require-effective-target alloca } */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+void test6 (size_t size)
+{
+  int32_t *buf = __builtin_alloca (4 * size);
+  memset (buf, 0, 4 * size);
+  int32_t last = *(buf + 4 * size); /* { dg-warning "stack-based buffer over-read" } */
+}
+
+/* (size * 16) - (size * 4) leads to a gap of (size * 12).  */
+
+/* { dg-begin-multiline-output "" }
+
+                                                       ┌─────────────────┐
+                                                       │ read of 4 bytes │
+                                                       └─────────────────┘
+                                                                ^
+                                                                │
+                                                                │
+  ┌────────────────────────────────┐                   ┌─────────────────┐
+  │buffer allocated on stack at (1)│                   │after valid range│
+  └────────────────────────────────┘                   └─────────────────┘
+  ├───────────────┬────────────────┤├────────┬────────┤├────────┬────────┤
+                  │                          │                  │
+                  │                          │       ╭──────────┴─────────╮
+                  │                          │       │over-read of 4 bytes│
+                  │                          │       ╰────────────────────╯
+      ╭───────────┴──────────╮      ╭────────┴────────╮
+      │size: 'size * 4' bytes│      │'size * 12' bytes│
+      ╰──────────────────────╯      ╰─────────────────╯
+
+   { dg-end-multiline-output "" } */
+
+void test7 (size_t size)
+{
+  int32_t *buf = __builtin_alloca (4 * size + 3); /* { dg-warning "allocated buffer size is not a multiple of the pointee's size" } */
+  buf[size] = 42; /* { dg-warning "stack-based buffer overflow" } */
+}
+
+/* { dg-begin-multiline-output "" }
+
+                                 ┌───────────────────────────────────────┐
+                                 │          write of '(int) 42'          │
+                                 └───────────────────────────────────────┘
+                                           │                   │
+                                           │                   │
+                                           v                   v
+  ┌──────────────────────────────────────────────────┐┌──────────────────┐
+  │         buffer allocated on stack at (1)         ││after valid range │
+  └──────────────────────────────────────────────────┘└──────────────────┘
+  ├────────────────────────┬─────────────────────────┤├────────┬─────────┤
+                           │                                   │
+           ╭───────────────┴──────────────╮          ╭─────────┴────────╮
+           │capacity: 'size * 4 + 3' bytes│          │overflow of 1 byte│
+           ╰──────────────────────────────╯          ╰──────────────────╯
+
+   { dg-end-multiline-output "" } */
+
+
+/* We're currently not able to generate a diagram for this case;
+   make sure we handle this case gracefully.  */
+
+char *test99 (const char *x, const char *y)
+{
+  size_t len_x = __builtin_strlen (x);
+  size_t len_y = __builtin_strlen (y);
+  /* BUG (root cause): forgot to add 1 for terminator.  */
+  size_t sz = len_x + len_y;
+  char *result = __builtin_malloc (sz);
+  if (!result)
+    return NULL;
+  __builtin_memcpy (result, x, len_x);
+  __builtin_memcpy (result + len_x, y, len_y);
+  /* BUG (symptom): off-by-one out-of-bounds write to heap.  */
+  result[len_x + len_y] = '\0'; /* { dg-warning "heap-based buffer overflow" } */
+  return result;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-12.c b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-12.c
new file mode 100644
index 0000000..3573750
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-12.c
@@ -0,0 +1,53 @@
+/* { dg-additional-options "-fdiagnostics-text-art-charset=unicode" } */
+/* { dg-require-effective-target alloca } */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+void test8 (size_t size, size_t offset)
+{
+  char src[size];
+  char dst[size];
+  memcpy (dst, src, size + offset); /* { dg-line test8 } */
+  /* { dg-warning "over-read" "warning" { target *-*-* } test8 } */
+  /* { dg-warning "overflow" "warning" { target *-*-* } test8 } */
+}
+
+/* { dg-begin-multiline-output "" }
+
+  ┌──────────────────────────────────────────────────────────────────────┐
+  │                    read of 'size + offset' bytes                     │
+  └──────────────────────────────────────────────────────────────────────┘
+                   ^                                   ^
+                   │                                   │
+                   │                                   │
+  ┌──────────────────────────────────┐┌──────────────────────────────────┐
+  │ buffer allocated on stack at (1) ││        after valid range         │
+  └──────────────────────────────────┘└──────────────────────────────────┘
+  ├────────────────┬─────────────────┤├────────────────┬─────────────────┤
+                   │                                   │
+         ╭─────────┴────────╮            ╭─────────────┴─────────────╮
+         │size: 'size' bytes│            │over-read of 'offset' bytes│
+         ╰──────────────────╯            ╰───────────────────────────╯
+
+   { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+
+  ┌──────────────────────────────────────────────────────────────────────┐
+  │                    write of 'size + offset' bytes                    │
+  └──────────────────────────────────────────────────────────────────────┘
+                   │                                   │
+                   │                                   │
+                   v                                   v
+  ┌──────────────────────────────────┐┌──────────────────────────────────┐
+  │ buffer allocated on stack at (1) ││        after valid range         │
+  └──────────────────────────────────┘└──────────────────────────────────┘
+  ├────────────────┬─────────────────┤├────────────────┬─────────────────┤
+                   │                                   │
+       ╭───────────┴──────────╮          ╭─────────────┴────────────╮
+       │capacity: 'size' bytes│          │overflow of 'offset' bytes│
+       ╰──────────────────────╯          ╰──────────────────────────╯
+
+   { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-13.c b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-13.c
new file mode 100644
index 0000000..dcd1263
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-13.c
@@ -0,0 +1,43 @@
+/* { dg-additional-options "-fdiagnostics-text-art-charset=unicode" } */
+
+#include <string.h>
+
+void
+test_non_ascii ()
+{
+  char buf[9];
+  strcpy (buf, "Liberté\n"); /* { dg-warning "stack-based buffer overflow" } */
+  /* { dg-warning "'__builtin_memcpy' writing 10 bytes into a region of size 9 overflows the destination" "" { target *-*-* } .-1 } */
+}
+
+/* Example of non-ASCII UTF-8 that's short enough to fully quote, whilst
+   containing control characters.  */
+
+/* { dg-begin-multiline-output "" }
+
+  ┌──────┬──────┬──────┬──────┬──────┬──────┬────┬────┬──────┐┌─────────────────┐
+  │ [0]  │ [1]  │ [2]  │ [3]  │ [4]  │ [5]  │[6] │[7] │ [8]  ││       [9]       │
+  ├──────┼──────┼──────┼──────┼──────┼──────┼────┼────┼──────┤├─────────────────┤
+  │ 0x4c │ 0x69 │ 0x62 │ 0x65 │ 0x72 │ 0x74 │0xc3│0xa9│ 0x0a ││      0x00       │
+  ├──────┼──────┼──────┼──────┼──────┼──────┼────┴────┼──────┤├─────────────────┤
+  │U+004c│U+0069│U+0062│U+0065│U+0072│U+0074│ U+00e9  │U+000a││     U+0000      │
+  ├──────┼──────┼──────┼──────┼──────┼──────┼─────────┼──────┤├─────────────────┤
+  │  L   │  i   │  b   │  e   │  r   │  t   │    é    │      ││       NUL       │
+  ├──────┴──────┴──────┴──────┴──────┴──────┴─────────┴──────┴┴─────────────────┤
+  │                      string literal (type: 'char[10]')                      │
+  └─────────────────────────────────────────────────────────────────────────────┘
+     │      │      │      │      │      │     │    │     │             │
+     │      │      │      │      │      │     │    │     │             │
+     v      v      v      v      v      v     v    v     v             v
+  ┌──────┬────────────────────────────────────────────┬──────┐┌─────────────────┐
+  │ [0]  │                    ...                     │ [8]  ││                 │
+  ├──────┴────────────────────────────────────────────┴──────┤│after valid range│
+  │                 'buf' (type: 'char[9]')                  ││                 │
+  └──────────────────────────────────────────────────────────┘└─────────────────┘
+  ├────────────────────────────┬─────────────────────────────┤├────────┬────────┤
+                               │                                       │
+                      ╭────────┴────────╮                    ╭─────────┴────────╮
+                      │capacity: 9 bytes│                    │overflow of 1 byte│
+                      ╰─────────────────╯                    ╰──────────────────╯
+
+   { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-14.c b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-14.c
new file mode 100644
index 0000000..3cedf06
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-14.c
@@ -0,0 +1,110 @@
+/* { dg-additional-options "-fdiagnostics-text-art-charset=unicode" } */
+
+#include <stdint.h>
+
+extern int32_t arr_0[0]; /* { dg-message "capacity: 0 bytes" } */
+extern int32_t arr_1[1]; /* { dg-message "capacity: 4 bytes" } */
+extern int32_t arr_2[2]; /* { dg-message "capacity: 8 bytes" } */
+extern int32_t arr_3[3]; /* { dg-message "capacity: 12 bytes" } */
+extern int32_t arr_4[4]; /* { dg-message "capacity: 16 bytes" } */
+
+void test_immediately_after (int x)
+{
+  arr_0[0] = x; /* { dg-warning "buffer overflow" } */
+  arr_1[1] = x; /* { dg-warning "buffer overflow" } */
+  arr_2[2] = x; /* { dg-warning "buffer overflow" } */
+  arr_3[3] = x; /* { dg-warning "buffer overflow" } */
+  arr_4[4] = x; /* { dg-warning "buffer overflow" } */
+}
+
+/* Expect no diagram for the arr_0 case: there's no valid region 
+to write to.  */
+
+/* The arr_1 case.  */
+/* { dg-begin-multiline-output "" }
+
+                                      ┌──────────────────────────────────┐
+                                      │   write from 'x' (type: 'int')   │
+                                      └──────────────────────────────────┘
+                                                       │
+                                                       │
+                                                       v
+  ┌──────────────────────────────────┐┌──────────────────────────────────┐
+  │               [0]                ││                                  │
+  ├──────────────────────────────────┤│        after valid range         │
+  │   'arr_1' (type: 'int32_t[1]')   ││                                  │
+  └──────────────────────────────────┘└──────────────────────────────────┘
+  ├────────────────┬─────────────────┤├────────────────┬─────────────────┤
+                   │                                   │
+          ╭────────┴────────╮                ╭─────────┴─────────╮
+          │capacity: 4 bytes│                │overflow of 4 bytes│
+          ╰─────────────────╯                ╰───────────────────╯
+
+   { dg-end-multiline-output "" } */
+
+/* The arr_2 case.  */
+/* { dg-begin-multiline-output "" }
+
+                                            ┌────────────────────────────┐
+                                            │write from 'x' (type: 'int')│
+                                            └────────────────────────────┘
+                                                          │
+                                                          │
+                                                          v
+  ┌────────────────────┬───────────────────┐┌────────────────────────────┐
+  │        [0]         │        [1]        ││                            │
+  ├────────────────────┴───────────────────┤│     after valid range      │
+  │      'arr_2' (type: 'int32_t[2]')      ││                            │
+  └────────────────────────────────────────┘└────────────────────────────┘
+  ├───────────────────┬────────────────────┤├─────────────┬──────────────┤
+                      │                                   │
+             ╭────────┴────────╮                ╭─────────┴─────────╮
+             │capacity: 8 bytes│                │overflow of 4 bytes│
+             ╰─────────────────╯                ╰───────────────────╯
+
+   { dg-end-multiline-output "" } */
+
+/* The arr_3 case.  */
+// Perhaps we should show [1] rather than ellipsize here.
+/* { dg-begin-multiline-output "" }
+
+                                            ┌────────────────────────────┐
+                                            │write from 'x' (type: 'int')│
+                                            └────────────────────────────┘
+                                                          │
+                                                          │
+                                                          v
+  ┌─────────────┬─────────────┬────────────┐┌────────────────────────────┐
+  │     [0]     │     ...     │    [2]     ││                            │
+  ├─────────────┴─────────────┴────────────┤│     after valid range      │
+  │      'arr_3' (type: 'int32_t[3]')      ││                            │
+  └────────────────────────────────────────┘└────────────────────────────┘
+  ├───────────────────┬────────────────────┤├─────────────┬──────────────┤
+                      │                                   │
+            ╭─────────┴────────╮                ╭─────────┴─────────╮
+            │capacity: 12 bytes│                │overflow of 4 bytes│
+            ╰──────────────────╯                ╰───────────────────╯
+
+   { dg-end-multiline-output "" } */
+
+/* The arr_4 case.  */
+/* { dg-begin-multiline-output "" }
+
+                                            ┌────────────────────────────┐
+                                            │write from 'x' (type: 'int')│
+                                            └────────────────────────────┘
+                                                          │
+                                                          │
+                                                          v
+  ┌──────────┬──────────────────┬──────────┐┌────────────────────────────┐
+  │   [0]    │       ...        │   [3]    ││                            │
+  ├──────────┴──────────────────┴──────────┤│     after valid range      │
+  │      'arr_4' (type: 'int32_t[4]')      ││                            │
+  └────────────────────────────────────────┘└────────────────────────────┘
+  ├───────────────────┬────────────────────┤├─────────────┬──────────────┤
+                      │                                   │
+            ╭─────────┴────────╮                ╭─────────┴─────────╮
+            │capacity: 16 bytes│                │overflow of 4 bytes│
+            ╰──────────────────╯                ╰───────────────────╯
+
+   { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-15.c b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-15.c
new file mode 100644
index 0000000..e2a6381
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-15.c
@@ -0,0 +1,42 @@
+/* Regression test for ICE with short values of
+   --param=analyzer-text-art-string-ellipsis-threshold=.  */
+/* { dg-additional-options "-fdiagnostics-text-art-charset=unicode --param=analyzer-text-art-string-ellipsis-threshold=0" } */
+
+#include <string.h>
+
+void
+test_non_ascii ()
+{
+  char buf[9];
+  strcpy (buf, "Liberté\n"); /* { dg-warning "stack-based buffer overflow" } */
+  /* { dg-warning "'__builtin_memcpy' writing 10 bytes into a region of size 9 overflows the destination" "" { target *-*-* } .-1 } */
+}
+
+/* { dg-begin-multiline-output "" }
+
+  ┌──────┬──────┬──────┬──────┬──────┬──────┬────┬────┬──────┐┌─────────────────┐
+  │ [0]  │ [1]  │ [2]  │ [3]  │ [4]  │ [5]  │[6] │[7] │ [8]  ││       [9]       │
+  ├──────┼──────┼──────┼──────┼──────┼──────┼────┼────┼──────┤├─────────────────┤
+  │ 0x4c │ 0x69 │ 0x62 │ 0x65 │ 0x72 │ 0x74 │0xc3│0xa9│ 0x0a ││      0x00       │
+  ├──────┼──────┼──────┼──────┼──────┼──────┼────┴────┼──────┤├─────────────────┤
+  │U+004c│U+0069│U+0062│U+0065│U+0072│U+0074│ U+00e9  │U+000a││     U+0000      │
+  ├──────┼──────┼──────┼──────┼──────┼──────┼─────────┼──────┤├─────────────────┤
+  │  L   │  i   │  b   │  e   │  r   │  t   │    é    │      ││       NUL       │
+  ├──────┴──────┴──────┴──────┴──────┴──────┴─────────┴──────┴┴─────────────────┤
+  │                      string literal (type: 'char[10]')                      │
+  └─────────────────────────────────────────────────────────────────────────────┘
+     │      │      │      │      │      │     │    │     │             │
+     │      │      │      │      │      │     │    │     │             │
+     v      v      v      v      v      v     v    v     v             v
+  ┌──────┬────────────────────────────────────────────┬──────┐┌─────────────────┐
+  │ [0]  │                    ...                     │ [8]  ││                 │
+  ├──────┴────────────────────────────────────────────┴──────┤│after valid range│
+  │                 'buf' (type: 'char[9]')                  ││                 │
+  └──────────────────────────────────────────────────────────┘└─────────────────┘
+  ├────────────────────────────┬─────────────────────────────┤├────────┬────────┤
+                               │                                       │
+                      ╭────────┴────────╮                    ╭─────────┴────────╮
+                      │capacity: 9 bytes│                    │overflow of 1 byte│
+                      ╰─────────────────╯                    ╰──────────────────╯
+
+   { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-2.c b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-2.c
new file mode 100644
index 0000000..535dab1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-2.c
@@ -0,0 +1,30 @@
+/* { dg-additional-options "-fdiagnostics-text-art-charset=unicode" } */
+
+#include <stdint.h>
+#include <stdlib.h>
+
+void int_vla_write_element_after_end_off_by_one(int32_t x, size_t n)
+{
+  int32_t arr[n]; /* { dg-message "\\(1\\) capacity: 'n \\* 4' bytes" } */
+
+  arr[n] = x;  /* { dg-warning "stack-based buffer overflow" } */
+}
+
+/* { dg-begin-multiline-output "" }
+
+                                      ┌──────────────────────────────────┐
+                                      │ write from 'x' (type: 'int32_t') │
+                                      └──────────────────────────────────┘
+                                                       │
+                                                       │
+                                                       v
+  ┌──────────────────────────────────┐┌──────────────────────────────────┐
+  │ buffer allocated on stack at (1) ││        after valid range         │
+  └──────────────────────────────────┘└──────────────────────────────────┘
+  ├────────────────┬─────────────────┤├────────────────┬─────────────────┤
+                   │                                   │
+       ╭───────────┴───────────╮             ╭─────────┴─────────╮
+       │capacity: 'n * 4' bytes│             │overflow of 4 bytes│
+       ╰───────────────────────╯             ╰───────────────────╯
+
+   { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-3.c b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-3.c
new file mode 100644
index 0000000..064f3fa
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-3.c
@@ -0,0 +1,45 @@
+/* The multiline output assumes sizeof(size_t) == 8.
+   { dg-require-effective-target lp64 } */
+
+/* { dg-additional-options "-fdiagnostics-text-art-charset=unicode" } */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+
+struct str {
+  size_t len;
+  char data[];
+};
+
+struct str *
+make_str_badly (const char *src)
+{
+  size_t len = strlen(src);
+  struct str *str = malloc(sizeof(str) + len); /* { dg-message "\\(1\\) capacity: 'len \\+ 8' bytes" } */
+  if (!str)
+    return NULL;
+  str->len = len;
+  memcpy(str->data, src, len);
+  str->data[len] = '\0'; /* { dg-warning "heap-based buffer overflow" } */
+  return str;
+}
+
+/* { dg-begin-multiline-output "" }
+
+                                      ┌──────────────────────────────────┐
+                                      │       write of '(char) 0'        │
+                                      └──────────────────────────────────┘
+                                                       │
+                                                       │
+                                                       v
+  ┌──────────────────────────────────┐┌──────────────────────────────────┐
+  │ buffer allocated on heap at (1)  ││        after valid range         │
+  └──────────────────────────────────┘└──────────────────────────────────┘
+  ├────────────────┬─────────────────┤├────────────────┬─────────────────┤
+                   │                                   │
+      ╭────────────┴────────────╮            ╭─────────┴────────╮
+      │capacity: 'len + 8' bytes│            │overflow of 1 byte│
+      ╰─────────────────────────╯            ╰──────────────────╯
+
+   { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-4.c b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-4.c
new file mode 100644
index 0000000..ec8e4ab
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-4.c
@@ -0,0 +1,45 @@
+/* { dg-additional-options "-fdiagnostics-text-art-charset=unicode" } */
+
+#include <string.h>
+
+#define LOREM_IPSUM \
+  "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod" \
+  " tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim" \
+  " veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea" \
+  " commodo consequat. Duis aute irure dolor in reprehenderit in voluptate" \
+  " velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint" \
+  " occaecat cupidatat non proident, sunt in culpa qui officia deserunt" \
+  " mollit anim id est laborum."
+
+void
+test_long_string ()
+{
+  char buf[100];
+  strcpy (buf, LOREM_IPSUM); /* { dg-warning "stack-based buffer overflow" } */
+  /* { dg-warning "'__builtin_memcpy' writing 446 bytes into a region of size 100 overflows the destination" "" { target *-*-* } .-1 } */
+}
+
+/* { dg-begin-multiline-output "" }
+
+  ┌───┬───┬───┬───┬───┬───┬──────────┬─────┬─────┬─────┬─────┬─────┬─────┐
+  │[0]│[1]│[2]│[3]│[4]│[5]│          │[440]│[441]│[442]│[443]│[444]│[445]│
+  ├───┼───┼───┼───┼───┼───┤   ...    ├─────┼─────┼─────┼─────┼─────┼─────┤
+  │'L'│'o'│'r'│'e'│'m'│' '│          │ 'o' │ 'r' │ 'u' │ 'm' │ '.' │ NUL │
+  ├───┴───┴───┴───┴───┴───┴──────────┴─────┴─────┴─────┴─────┴─────┴─────┤
+  │                  string literal (type: 'char[446]')                  │
+  └──────────────────────────────────────────────────────────────────────┘
+    │   │   │   │   │   │  │  │    │    │     │     │     │     │     │
+    │   │   │   │   │   │  │  │    │    │     │     │     │     │     │
+    v   v   v   v   v   v  v  v    v    v     v     v     v     v     v
+  ┌───┬─────────────────────┬────┐┌──────────────────────────────────────┐
+  │[0]│         ...         │[99]││                                      │
+  ├───┴─────────────────────┴────┤│          after valid range           │
+  │  'buf' (type: 'char[100]')   ││                                      │
+  └──────────────────────────────┘└──────────────────────────────────────┘
+  ├──────────────┬───────────────┤├──────────────────┬───────────────────┤
+                 │                                   │
+       ╭─────────┴─────────╮              ╭──────────┴──────────╮
+       │capacity: 100 bytes│              │overflow of 346 bytes│
+       ╰───────────────────╯              ╰─────────────────────╯
+
+   { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-5-ascii.c b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-5-ascii.c
new file mode 100644
index 0000000..e82bce9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-5-ascii.c
@@ -0,0 +1,40 @@
+/* { dg-additional-options "-fdiagnostics-text-art-charset=ascii" } */
+
+#include <string.h>
+
+void
+test_non_ascii ()
+{
+  char buf[5];
+  strcpy (buf, "文字化け"); /* { dg-warning "stack-based buffer overflow" } */
+  /* { dg-warning "'__builtin_memcpy' writing 13 bytes into a region of size 5 overflows the destination" "" { target *-*-* } .-1 } */
+}
+
+/* Without unicode support, we shouldn't show the printable unicode chars.  */
+
+/* { dg-begin-multiline-output "" }
+
+  +-----+-----+-----+----+----++----+----+----+----+----+----+----+------+
+  | [0] | [1] | [2] |[3] |[4] ||[5] |[6] |[7] |[8] |[9] |[10]|[11]| [12] |
+  +-----+-----+-----+----+----++----+----+----+----+----+----+----+------+
+  |0xe6 |0x96 |0x87 |0xe5|0xad||0x97|0xe5|0x8c|0x96|0xe3|0x81|0x91| 0x00 |
+  +-----+-----+-----+----+----++----+----+----+----+----+----+----+------+
+  |     U+6587      |    U+5b57     |    U+5316    |    U+3051    |U+0000|
+  +-----------------+---------------+--------------+--------------+------+
+  |                  string literal (type: 'char[13]')                   |
+  +----------------------------------------------------------------------+
+     |     |     |    |    |     |    |    |    |    |    |    |     |
+     |     |     |    |    |     |    |    |    |    |    |    |     |
+     v     v     v    v    v     v    v    v    v    v    v    v     v
+  +-----+----------------+----++-----------------------------------------+
+  | [0] |      ...       |[4] ||                                         |
+  +-----+----------------+----+|            after valid range            |
+  |  'buf' (type: 'char[5]')  ||                                         |
+  +---------------------------++-----------------------------------------+
+  |~~~~~~~~~~~~~+~~~~~~~~~~~~~||~~~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~~|
+                |                                   |
+       +--------+--------+                +---------+---------+
+       |capacity: 5 bytes|                |overflow of 8 bytes|
+       +-----------------+                +-------------------+
+
+   { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-5-unicode.c b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-5-unicode.c
new file mode 100644
index 0000000..48fa12f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-5-unicode.c
@@ -0,0 +1,42 @@
+/* { dg-additional-options "-fdiagnostics-text-art-charset=unicode" } */
+
+#include <string.h>
+
+void
+test_non_ascii ()
+{
+  char buf[5];
+  strcpy (buf, "文字化け"); /* { dg-warning "stack-based buffer overflow" } */
+  /* { dg-warning "'__builtin_memcpy' writing 13 bytes into a region of size 5 overflows the destination" "" { target *-*-* } .-1 } */
+}
+
+/* With unicode support, we should show the printable unicode chars.  */
+
+/* { dg-begin-multiline-output "" }
+
+  ┌─────┬─────┬─────┬────┬────┐┌────┬────┬────┬────┬────┬────┬────┬──────┐
+  │ [0] │ [1] │ [2] │[3] │[4] ││[5] │[6] │[7] │[8] │[9] │[10]│[11]│ [12] │
+  ├─────┼─────┼─────┼────┼────┤├────┼────┼────┼────┼────┼────┼────┼──────┤
+  │0xe6 │0x96 │0x87 │0xe5│0xad││0x97│0xe5│0x8c│0x96│0xe3│0x81│0x91│ 0x00 │
+  ├─────┴─────┴─────┼────┴────┴┴────┼────┴────┴────┼────┴────┴────┼──────┤
+  │     U+6587      │    U+5b57     │    U+5316    │    U+3051    │U+0000│
+  ├─────────────────┼───────────────┼──────────────┼──────────────┼──────┤
+  │       文        │      字       │      化      │      け      │ NUL  │
+  ├─────────────────┴───────────────┴──────────────┴──────────────┴──────┤
+  │                  string literal (type: 'char[13]')                   │
+  └──────────────────────────────────────────────────────────────────────┘
+     │     │     │    │    │     │    │    │    │    │    │    │     │
+     │     │     │    │    │     │    │    │    │    │    │    │     │
+     v     v     v    v    v     v    v    v    v    v    v    v     v
+  ┌─────┬────────────────┬────┐┌─────────────────────────────────────────┐
+  │ [0] │      ...       │[4] ││                                         │
+  ├─────┴────────────────┴────┤│            after valid range            │
+  │  'buf' (type: 'char[5]')  ││                                         │
+  └───────────────────────────┘└─────────────────────────────────────────┘
+  ├─────────────┬─────────────┤├────────────────────┬────────────────────┤
+                │                                   │
+       ╭────────┴────────╮                ╭─────────┴─────────╮
+       │capacity: 5 bytes│                │overflow of 8 bytes│
+       ╰─────────────────╯                ╰───────────────────╯
+
+   { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-6.c b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-6.c
new file mode 100644
index 0000000..25bf9d5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-6.c
@@ -0,0 +1,125 @@
+/* { dg-require-effective-target lp64 }
+   Misbehaves with -m32 due to optimization turning the pointer arithmetic into:
+      _2 = &buf + 4294967246;
+      memcpy (_2, _1, 4096);
+*/
+
+/* { dg-additional-options "-fdiagnostics-text-art-charset=unicode" } */
+
+#include <string.h>
+
+#define LOREM_IPSUM \
+  "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod" \
+  " tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim" \
+  " veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea" \
+  " commodo consequat. Duis aute irure dolor in reprehenderit in voluptate" \
+  " velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint" \
+  " occaecat cupidatat non proident, sunt in culpa qui officia deserunt" \
+  " mollit anim id est laborum."
+
+/* This memcpy reads from both before and after the bounds of the
+   string literal, and writes to both before and after the bounds of "buf".  */
+
+void
+test_bad_memcpy ()
+{
+  char buf[100];
+  memcpy (buf - 50, LOREM_IPSUM - 100, 4096); /* { dg-warning "stack-based buffer overflow" } */
+  /* { dg-warning "stack-based buffer underwrite" "" { target *-*-* } .-1 } */
+  /* { dg-warning "buffer under-read" "" { target *-*-* } .-2 } */
+  /* { dg-warning "buffer over-read" "" { target *-*-* } .-3 } */
+  /* { dg-warning "'memcpy' writing 4096 bytes into a region of size 0 overflows the destination" "" { target *-*-* } .-4 } */
+}
+
+/* { dg-begin-multiline-output "" }
+
+  ┌─────────────────────────────────────────────────────────────────────────┐
+  │                           read of 4096 bytes                            │
+  └─────────────────────────────────────────────────────────────────────────┘
+           ^               ^          ^           ^                ^
+           │               │          │           │                │
+           │               │          │           │                │
+  ┌──────────────────┐┌──────────┬──────────┬────────────┐┌─────────────────┐
+  │                  ││   [0]    │   ...    │   [445]    ││                 │
+  │before valid range│├──────────┴──────────┴────────────┤│after valid range│
+  │                  ││string literal (type: 'char[446]')││                 │
+  └──────────────────┘└──────────────────────────────────┘└─────────────────┘
+  ├────────┬─────────┤├────────────────┬─────────────────┤├────────┬────────┤
+           │                           │                           │
+  ╭────────┴──────────────╮    ╭───────┴───────╮       ╭───────────┴───────────╮
+  │under-read of 100 bytes│    │size: 446 bytes│       │over-read of 3550 bytes│
+  ╰───────────────────────╯    ╰───────────────╯       ╰───────────────────────╯
+
+   { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+
+  ┌──────────────────────────────────────────────────────────────────────┐
+  │                         write of 4096 bytes                          │
+  └──────────────────────────────────────────────────────────────────────┘
+           │              │       │        │                 │
+           │              │       │        │                 │
+           v              v       v        v                 v
+  ┌──────────────────┐┌───────┬───────┬─────────┐┌───────────────────────┐
+  │                  ││  [0]  │  ...  │  [99]   ││                       │
+  │before valid range│├───────┴───────┴─────────┤│   after valid range   │
+  │                  ││'buf' (type: 'char[100]')││                       │
+  └──────────────────┘└─────────────────────────┘└───────────────────────┘
+  ├────────┬─────────┤├────────────┬────────────┤├───────────┬───────────┤
+           │                       │                         │
+           │             ╭─────────┴─────────╮   ╭───────────┴──────────╮
+           │             │capacity: 100 bytes│   │overflow of 3946 bytes│
+           │             ╰───────────────────╯   ╰──────────────────────╯
+  ╭────────┴─────────────╮
+  │underwrite of 50 bytes│
+  ╰──────────────────────╯
+
+   { dg-end-multiline-output "" } */
+
+/* The read and write diagrams are each emitted twice: once for the "before"
+   and once for the "after" diagnostic.  */
+
+/* { dg-begin-multiline-output "" }
+
+  ┌─────────────────────────────────────────────────────────────────────────┐
+  │                           read of 4096 bytes                            │
+  └─────────────────────────────────────────────────────────────────────────┘
+           ^               ^          ^           ^                ^
+           │               │          │           │                │
+           │               │          │           │                │
+  ┌──────────────────┐┌──────────┬──────────┬────────────┐┌─────────────────┐
+  │                  ││   [0]    │   ...    │   [445]    ││                 │
+  │before valid range│├──────────┴──────────┴────────────┤│after valid range│
+  │                  ││string literal (type: 'char[446]')││                 │
+  └──────────────────┘└──────────────────────────────────┘└─────────────────┘
+  ├────────┬─────────┤├────────────────┬─────────────────┤├────────┬────────┤
+           │                           │                           │
+  ╭────────┴──────────────╮    ╭───────┴───────╮       ╭───────────┴───────────╮
+  │under-read of 100 bytes│    │size: 446 bytes│       │over-read of 3550 bytes│
+  ╰───────────────────────╯    ╰───────────────╯       ╰───────────────────────╯
+
+   { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+
+  ┌──────────────────────────────────────────────────────────────────────┐
+  │                         write of 4096 bytes                          │
+  └──────────────────────────────────────────────────────────────────────┘
+           │              │       │        │                 │
+           │              │       │        │                 │
+           v              v       v        v                 v
+  ┌──────────────────┐┌───────┬───────┬─────────┐┌───────────────────────┐
+  │                  ││  [0]  │  ...  │  [99]   ││                       │
+  │before valid range│├───────┴───────┴─────────┤│   after valid range   │
+  │                  ││'buf' (type: 'char[100]')││                       │
+  └──────────────────┘└─────────────────────────┘└───────────────────────┘
+  ├────────┬─────────┤├────────────┬────────────┤├───────────┬───────────┤
+           │                       │                         │
+           │             ╭─────────┴─────────╮   ╭───────────┴──────────╮
+           │             │capacity: 100 bytes│   │overflow of 3946 bytes│
+           │             ╰───────────────────╯   ╰──────────────────────╯
+  ╭────────┴─────────────╮
+  │underwrite of 50 bytes│
+  ╰──────────────────────╯
+
+   { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-7.c b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-7.c
new file mode 100644
index 0000000..25a9acc
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-7.c
@@ -0,0 +1,36 @@
+/* { dg-additional-options "-fdiagnostics-text-art-charset=unicode" } */
+
+#include <string.h>
+
+void
+test_string_with_control_chars ()
+{
+  char buf[8];
+  strcpy (buf, "\tone\n\ttwo\n"); /* { dg-warning "stack-based buffer overflow" } */
+  /* { dg-warning "'__builtin_memcpy' writing 11 bytes into a region of size 8 overflows the destination" "" { target *-*-* } .-1 } */
+}
+
+/* { dg-begin-multiline-output "" }
+
+  ┌──────┬──────┬──────┬─────┬─────┬─────┬─────┬─────┐┌─────┬─────┬──────┐
+  │ [0]  │ [1]  │ [2]  │ [3] │ [4] │ [5] │ [6] │ [7] ││ [8] │ [9] │ [10] │
+  ├──────┼──────┼──────┼─────┼─────┼─────┼─────┼─────┤├─────┼─────┼──────┤
+  │ 0x09 │ 'o'  │ 'n'  │ 'e' │0x0a │0x09 │ 't' │ 'w' ││ 'o' │0x0a │ NUL  │
+  ├──────┴──────┴──────┴─────┴─────┴─────┴─────┴─────┴┴─────┴─────┴──────┤
+  │                  string literal (type: 'char[11]')                   │
+  └──────────────────────────────────────────────────────────────────────┘
+     │      │      │      │     │     │     │     │      │     │     │
+     │      │      │      │     │     │     │     │      │     │     │
+     v      v      v      v     v     v     v     v      v     v     v
+  ┌──────┬─────────────────────────────────────┬─────┐┌──────────────────┐
+  │ [0]  │                 ...                 │ [7] ││                  │
+  ├──────┴─────────────────────────────────────┴─────┤│after valid range │
+  │             'buf' (type: 'char[8]')              ││                  │
+  └──────────────────────────────────────────────────┘└──────────────────┘
+  ├────────────────────────┬─────────────────────────┤├────────┬─────────┤
+                           │                                   │
+                  ╭────────┴────────╮                ╭─────────┴─────────╮
+                  │capacity: 8 bytes│                │overflow of 3 bytes│
+                  ╰─────────────────╯                ╰───────────────────╯
+
+   { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-8.c b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-8.c
new file mode 100644
index 0000000..24d8735
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-8.c
@@ -0,0 +1,34 @@
+/* { dg-additional-options "-fdiagnostics-text-art-charset=unicode" } */
+
+#include <stdlib.h>
+#include <stdint.h>
+
+/* Gap of 4, then an overflow of 4.  */
+
+void test2 (size_t size)
+{
+  int32_t *buf = __builtin_malloc (size * sizeof(int32_t)); /* { dg-message "\\(1\\) capacity: 'size \\* 4' bytes" } */
+  if (!buf) return;
+
+  buf[size + 1] = 42; /* { dg-warning "heap-based buffer overflow" } */
+  __builtin_free (buf);
+}
+
+/* { dg-begin-multiline-output "" }
+
+                                                     ┌───────────────────┐
+                                                     │write of '(int) 42'│
+                                                     └───────────────────┘
+                                                               │
+                                                               │
+                                                               v
+  ┌───────────────────────────────┐                  ┌───────────────────┐
+  │buffer allocated on heap at (1)│                  │ after valid range │
+  └───────────────────────────────┘                  └───────────────────┘
+  ├───────────────┬───────────────┤├───────┬────────┤├─────────┬─────────┤
+                  │                        │                   │
+    ╭─────────────┴────────────╮       ╭───┴───╮     ╭─────────┴─────────╮
+    │capacity: 'size * 4' bytes│       │4 bytes│     │overflow of 4 bytes│
+    ╰──────────────────────────╯       ╰───────╯     ╰───────────────────╯
+
+   { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-9.c b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-9.c
new file mode 100644
index 0000000..bb9ad66
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-9.c
@@ -0,0 +1,42 @@
+/* { dg-additional-options "-fdiagnostics-text-art-charset=unicode" } */
+
+#include <stdint.h>
+
+struct st
+{
+  char buf[16];
+  int32_t x;
+  int32_t y;
+};
+
+struct st arr[10];
+
+int32_t struct_arr_read_x_element_before_start_far(void)
+{
+  return arr[-100].x; /* { dg-warning "buffer under-read" "warning" } */
+  /* { dg-message "out-of-bounds read from byte -2384 till byte -2381 but 'arr' starts at byte 0" "final event" { target *-*-* } .-1 } */
+  /* { dg-message "valid subscripts for 'arr' are '\\\[0\\\]' to '\\\[9\\\]'" "valid subscript note" { target *-*-* } .-2 } */
+}
+
+// TODO: show index of accessed element
+// TODO: show field of accessed element
+/* { dg-begin-multiline-output "" }
+
+  ┌───────────────────────────┐
+  │read of 'int32_t' (4 bytes)│
+  └───────────────────────────┘
+                ^
+                │
+                │
+  ┌───────────────────────────┐            ┌─────────┬─────────┬─────────┐
+  │                           │            │   [0]   │   ...   │   [9]   │
+  │    before valid range     │            ├─────────┴─────────┴─────────┤
+  │                           │            │'arr' (type: 'struct st[10]')│
+  └───────────────────────────┘            └─────────────────────────────┘
+  ├─────────────┬─────────────┤├────┬─────┤├──────────────┬──────────────┤
+                │                   │                     │
+     ╭──────────┴──────────╮  ╭─────┴────╮        ╭───────┴───────╮
+     │under-read of 4 bytes│  │2380 bytes│        │size: 240 bytes│
+     ╰─────────────────────╯  ╰──────────╯        ╰───────────────╯
+
+   { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/pattern-test-2.c b/gcc/testsuite/gcc.dg/analyzer/pattern-test-2.c
index 7c8d1b3..5b8ff7b 100644
--- a/gcc/testsuite/gcc.dg/analyzer/pattern-test-2.c
+++ b/gcc/testsuite/gcc.dg/analyzer/pattern-test-2.c
@@ -26,7 +26,7 @@
   foo(p);
 
   /* { dg-warning "pattern match on 'p != 0'" "p != 0" { target *-*-* } cond_2 } */
-  /* { dg-warning "pattern match on 'tmp1 | tmp2 != 0'" "tmp1 | tmp2 != 0" { target *-*-* } cond_2 } */
+  /* { dg-warning "pattern match on 'p == 0 | q == 0 != 0'" "tmp1 | tmp2 != 0" { target *-*-* } cond_2 } */
   /* { dg-warning "pattern match on 'q != 0'" "q != 0" { target *-*-* } cond_2 } */
 }
 
@@ -42,6 +42,6 @@
   foo(p);
 
   /* { dg-warning "pattern match on 'p == 0'" "p == 0" { target *-*-* } cond_3 } */
-  /* { dg-warning "pattern match on 'tmp1 & tmp2 == 0'" "tmp1 & tmp2 == 0" { target *-*-* } cond_3 } */
+  /* { dg-warning "pattern match on 'p == 0 & q == 0 == 0'" "tmp1 & tmp2 == 0" { target *-*-* } cond_3 } */
   /* { dg-warning "pattern match on 'q == 0'" "q == 0" { target *-*-* } cond_3 } */
 }
diff --git a/gcc/testsuite/gcc.dg/analyzer/pr101962.c b/gcc/testsuite/gcc.dg/analyzer/pr101962.c
index b878aad..5eb7cf0 100644
--- a/gcc/testsuite/gcc.dg/analyzer/pr101962.c
+++ b/gcc/testsuite/gcc.dg/analyzer/pr101962.c
@@ -16,7 +16,7 @@
 int
 test_1 (void)
 {
-  int stack; /* { dg-message "region created on stack here" } */
+  int stack;
   int *a = &stack;
   a = maybe_inc_int_ptr (a);
   a = maybe_inc_int_ptr (a);
diff --git a/gcc/testsuite/gcc.dg/missing-header-fixit-5.c b/gcc/testsuite/gcc.dg/missing-header-fixit-5.c
index 916033c..bf44feb 100644
--- a/gcc/testsuite/gcc.dg/missing-header-fixit-5.c
+++ b/gcc/testsuite/gcc.dg/missing-header-fixit-5.c
@@ -12,14 +12,18 @@
   /* { dg-begin-multiline-output "" }
    11 |   if (isdigit (m[0]))
       |       ^~~~~~~
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
   +++ |+#include <ctype.h>
     1 | 
      { dg-end-multiline-output "" } */
     {
       return abs (i); /* { dg-warning "implicit declaration of function" } */
   /* { dg-begin-multiline-output "" }
-   19 |       return abs (i);
+   21 |       return abs (i);
       |              ^~~
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
   +++ |+#include <stdlib.h>
     1 | 
      { dg-end-multiline-output "" } */
@@ -27,8 +31,10 @@
   else
     putchar (m[0]); /* { dg-warning "implicit declaration of function" } */
   /* { dg-begin-multiline-output "" }
-   28 |     putchar (m[0]);
+   32 |     putchar (m[0]);
       |     ^~~~~~~
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
   +++ |+#include <stdio.h>
     1 | 
      { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/plugin/analyzer_gil_plugin.c b/gcc/testsuite/gcc.dg/plugin/analyzer_gil_plugin.c
index e494315..e0fc9cd 100644
--- a/gcc/testsuite/gcc.dg/plugin/analyzer_gil_plugin.c
+++ b/gcc/testsuite/gcc.dg/plugin/analyzer_gil_plugin.c
@@ -155,7 +155,7 @@
     return m_call == sub_other.m_call;
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *) final override
   {
     return warning_at (rich_loc, get_controlling_option (),
 		       "nested usage of %qs", "Py_BEGIN_ALLOW_THREADS");
@@ -194,7 +194,7 @@
 	    && m_arg_idx == sub_other.m_arg_idx);
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *) final override
   {
     auto_diagnostic_group d;
     if (m_callee_fndecl)
@@ -245,7 +245,7 @@
 			((const pyobject_usage_without_gil&)base_other).m_expr);
   }
 
-  bool emit (rich_location *rich_loc) final override
+  bool emit (rich_location *rich_loc, logger *) final override
   {
     auto_diagnostic_group d;
     return warning_at (rich_loc, get_controlling_option (),
diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-text-art-ascii-bw.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-text-art-ascii-bw.c
new file mode 100644
index 0000000..e4239aa
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-text-art-ascii-bw.c
@@ -0,0 +1,57 @@
+/* { dg-additional-options "-fdiagnostics-text-art-charset=ascii -fdiagnostics-color=never" } */
+
+int non_empty;
+
+/* { dg-begin-multiline-output "" }
+
+  A
+   B
+    C
+
+   { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+
+  ♜ ♞ ♝ ♛ ♚ ♝ ♞ ♜
+  ♟ ♟ ♟ ♟ ♟ ♟ ♟ ♟
+  
+  
+  
+  
+  ♙ ♙ ♙ ♙ ♙ ♙ ♙ ♙
+  ♖ ♘ ♗ ♕ ♔ ♗ ♘ ♖
+
+   { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+
+  +--+
+  |🙂|
+  +--+
+
+   { dg-end-multiline-output "" } */
+/* { dg-begin-multiline-output "" }
+
+  +-------+-----+---------------+---------------------+-----------------------+-----------------------+
+  |Offsets|Octet|       0       |          1          |           2           |           3           |
+  +-------+-----+-+-+-+-+-+-+-+-+-+-+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+  | Octet | Bit |0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|
+  +-------+-----+-+-+-+-+-+-+-+-+-+-+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+  |   0   |  0  |Version|  IHL  |     DSCP      | ECN |                 Total Length                  |
+  +-------+-----+-------+-------+---------------+-----+--------+--------------------------------------+
+  |   4   | 32  |           Identification            | Flags  |           Fragment Offset            |
+  +-------+-----+---------------+---------------------+--------+--------------------------------------+
+  |   8   | 64  | Time To Live  |      Protocol       |                Header Checksum                |
+  +-------+-----+---------------+---------------------+-----------------------------------------------+
+  |  12   | 96  |                                  Source IP Address                                  |
+  +-------+-----+-------------------------------------------------------------------------------------+
+  |  16   | 128 |                               Destination IP Address                                |
+  +-------+-----+-------------------------------------------------------------------------------------+
+  |  20   | 160 |                                                                                     |
+  +-------+-----+                                                                                     |
+  |  ...  | ... |                                       Options                                       |
+  +-------+-----+                                                                                     |
+  |  56   | 448 |                                                                                     |
+  +-------+-----+-------------------------------------------------------------------------------------+
+
+   { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-text-art-ascii-color.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-text-art-ascii-color.c
new file mode 100644
index 0000000..0650428
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-text-art-ascii-color.c
@@ -0,0 +1,58 @@
+/* { dg-additional-options "-fdiagnostics-text-art-charset=ascii -fdiagnostics-color=always" } */
+
+int non_empty;
+
+/* { dg-begin-multiline-output "" }
+
+  A
+   B
+    C
+
+   { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+
+  ♜ ♞ ♝ ♛ ♚ ♝ ♞ ♜ 
+  ♟ ♟ ♟ ♟ ♟ ♟ ♟ ♟ 
+                  
+                  
+                  
+                  
+  ♙ ♙ ♙ ♙ ♙ ♙ ♙ ♙ 
+  ♖ ♘ ♗ ♕ ♔ ♗ ♘ ♖ 
+
+   { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+
+  +--+
+  |🙂|
+  +--+
+
+   { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+
+  +-------+-----+---------------+---------------------+-----------------------+-----------------------+
+  |Offsets|Octet|       0       |          1          |           2           |           3           |
+  +-------+-----+-+-+-+-+-+-+-+-+-+-+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+  | Octet | Bit |0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|
+  +-------+-----+-+-+-+-+-+-+-+-+-+-+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+  |   0   |  0  |Version|  IHL  |     DSCP      | ECN |                 Total Length                  |
+  +-------+-----+-------+-------+---------------+-----+--------+--------------------------------------+
+  |   4   | 32  |           Identification            | Flags  |           Fragment Offset            |
+  +-------+-----+---------------+---------------------+--------+--------------------------------------+
+  |   8   | 64  | Time To Live  |      Protocol       |                Header Checksum                |
+  +-------+-----+---------------+---------------------+-----------------------------------------------+
+  |  12   | 96  |                                  Source IP Address                                  |
+  +-------+-----+-------------------------------------------------------------------------------------+
+  |  16   | 128 |                               Destination IP Address                                |
+  +-------+-----+-------------------------------------------------------------------------------------+
+  |  20   | 160 |                                                                                     |
+  +-------+-----+                                                                                     |
+  |  ...  | ... |                                       Options                                       |
+  +-------+-----+                                                                                     |
+  |  56   | 448 |                                                                                     |
+  +-------+-----+-------------------------------------------------------------------------------------+
+
+   { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-text-art-none.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-text-art-none.c
new file mode 100644
index 0000000..c8118b4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-text-art-none.c
@@ -0,0 +1,5 @@
+/* { dg-additional-options "-fdiagnostics-text-art-charset=none" } */
+
+int non_empty;
+
+/* We expect no output.  */
diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-text-art-unicode-bw.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-text-art-unicode-bw.c
new file mode 100644
index 0000000..c9f5b36
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-text-art-unicode-bw.c
@@ -0,0 +1,58 @@
+/* { dg-additional-options "-fdiagnostics-text-art-charset=unicode -fdiagnostics-color=never" } */
+
+int non_empty;
+
+/* { dg-begin-multiline-output "" }
+
+  A
+   B
+    C
+
+   { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+
+  ♜ ♞ ♝ ♛ ♚ ♝ ♞ ♜
+  ♟ ♟ ♟ ♟ ♟ ♟ ♟ ♟
+  
+  
+  
+  
+  ♙ ♙ ♙ ♙ ♙ ♙ ♙ ♙
+  ♖ ♘ ♗ ♕ ♔ ♗ ♘ ♖
+
+   { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+
+  ┌──┐
+  │🙂│
+  └──┘
+
+   { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+
+  ┌───────┬─────┬───────────────┬─────────────────────┬───────────────────────┬───────────────────────┐
+  │Offsets│Octet│       0       │          1          │           2           │           3           │
+  ├───────┼─────┼─┬─┬─┬─┬─┬─┬─┬─┼─┬─┬──┬──┬──┬──┬──┬──┼──┬──┬──┬──┬──┬──┬──┬──┼──┬──┬──┬──┬──┬──┬──┬──┤
+  │ Octet │ Bit │0│1│2│3│4│5│6│7│8│9│10│11│12│13│14│15│16│17│18│19│20│21│22│23│24│25│26│27│28│29│30│31│
+  ├───────┼─────┼─┴─┴─┴─┼─┴─┴─┴─┼─┴─┴──┴──┴──┴──┼──┴──┼──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┤
+  │   0   │  0  │Version│  IHL  │     DSCP      │ ECN │                 Total Length                  │
+  ├───────┼─────┼───────┴───────┴───────────────┴─────┼────────┬──────────────────────────────────────┤
+  │   4   │ 32  │           Identification            │ Flags  │           Fragment Offset            │
+  ├───────┼─────┼───────────────┬─────────────────────┼────────┴──────────────────────────────────────┤
+  │   8   │ 64  │ Time To Live  │      Protocol       │                Header Checksum                │
+  ├───────┼─────┼───────────────┴─────────────────────┴───────────────────────────────────────────────┤
+  │  12   │ 96  │                                  Source IP Address                                  │
+  ├───────┼─────┼─────────────────────────────────────────────────────────────────────────────────────┤
+  │  16   │ 128 │                               Destination IP Address                                │
+  ├───────┼─────┼─────────────────────────────────────────────────────────────────────────────────────┤
+  │  20   │ 160 │                                                                                     │
+  ├───────┼─────┤                                                                                     │
+  │  ...  │ ... │                                       Options                                       │
+  ├───────┼─────┤                                                                                     │
+  │  56   │ 448 │                                                                                     │
+  └───────┴─────┴─────────────────────────────────────────────────────────────────────────────────────┘
+
+   { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-text-art-unicode-color.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-text-art-unicode-color.c
new file mode 100644
index 0000000..f402836
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-text-art-unicode-color.c
@@ -0,0 +1,59 @@
+/* { dg-additional-options "-fdiagnostics-text-art-charset=unicode -fdiagnostics-color=always" } */
+
+int non_empty;
+
+
+/* { dg-begin-multiline-output "" }
+
+  A
+   B
+    C
+
+   { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+
+  ♜ ♞ ♝ ♛ ♚ ♝ ♞ ♜ 
+  ♟ ♟ ♟ ♟ ♟ ♟ ♟ ♟ 
+                  
+                  
+                  
+                  
+  ♙ ♙ ♙ ♙ ♙ ♙ ♙ ♙ 
+  ♖ ♘ ♗ ♕ ♔ ♗ ♘ ♖ 
+
+   { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+
+  ┌──┐
+  │🙂│
+  └──┘
+
+   { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+
+  ┌───────┬─────┬───────────────┬─────────────────────┬───────────────────────┬───────────────────────┐
+  │Offsets│Octet│       0       │          1          │           2           │           3           │
+  ├───────┼─────┼─┬─┬─┬─┬─┬─┬─┬─┼─┬─┬──┬──┬──┬──┬──┬──┼──┬──┬──┬──┬──┬──┬──┬──┼──┬──┬──┬──┬──┬──┬──┬──┤
+  │ Octet │ Bit │0│1│2│3│4│5│6│7│8│9│10│11│12│13│14│15│16│17│18│19│20│21│22│23│24│25│26│27│28│29│30│31│
+  ├───────┼─────┼─┴─┴─┴─┼─┴─┴─┴─┼─┴─┴──┴──┴──┴──┼──┴──┼──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┤
+  │   0   │  0  │Version│  IHL  │     DSCP      │ ECN │                 Total Length                  │
+  ├───────┼─────┼───────┴───────┴───────────────┴─────┼────────┬──────────────────────────────────────┤
+  │   4   │ 32  │           Identification            │ Flags  │           Fragment Offset            │
+  ├───────┼─────┼───────────────┬─────────────────────┼────────┴──────────────────────────────────────┤
+  │   8   │ 64  │ Time To Live  │      Protocol       │                Header Checksum                │
+  ├───────┼─────┼───────────────┴─────────────────────┴───────────────────────────────────────────────┤
+  │  12   │ 96  │                                  Source IP Address                                  │
+  ├───────┼─────┼─────────────────────────────────────────────────────────────────────────────────────┤
+  │  16   │ 128 │                               Destination IP Address                                │
+  ├───────┼─────┼─────────────────────────────────────────────────────────────────────────────────────┤
+  │  20   │ 160 │                                                                                     │
+  ├───────┼─────┤                                                                                     │
+  │  ...  │ ... │                                       Options                                       │
+  ├───────┼─────┤                                                                                     │
+  │  56   │ 448 │                                                                                     │
+  └───────┴─────┴─────────────────────────────────────────────────────────────────────────────────────┘
+
+   { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_text_art.c b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_text_art.c
new file mode 100644
index 0000000..58b219b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_text_art.c
@@ -0,0 +1,258 @@
+/* { dg-options "-O" } */
+
+/* This plugin exercises the text_art code.  */
+
+#define INCLUDE_VECTOR
+#include "gcc-plugin.h"
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "plugin-version.h"
+#include "diagnostic.h"
+#include "diagnostic-diagram.h"
+#include "text-art/canvas.h"
+#include "text-art/table.h"
+
+int plugin_is_GPL_compatible;
+
+using namespace text_art;
+
+/* Canvas tests.  */
+
+static void
+emit_canvas (const canvas &c, const char *alt_text)
+{
+  diagnostic_diagram diagram (c, alt_text);
+  diagnostic_emit_diagram (global_dc, diagram);
+}
+
+static void
+test_abc ()
+{
+  style_manager sm;
+  canvas c (canvas::size_t (3, 3), sm);
+  c.paint (canvas::coord_t (0, 0), styled_unichar ('A'));
+  c.paint (canvas::coord_t (1, 1), styled_unichar ('B'));
+  c.paint (canvas::coord_t (2, 2), styled_unichar ('C'));
+  emit_canvas (c, "test_abc");
+}
+
+/* Test of procedural art using 24-bit color: chess starting position.  */
+
+static void
+test_chessboard ()
+{
+  /* With the exception of NONE, these are in order of the chess symbols
+     in the Unicode Miscellaneous Symbols block.  */
+  enum class piece { KING, QUEEN, ROOK, BISHOP, KNIGHT, PAWN, NONE };
+  enum class color { BLACK, WHITE, NONE };
+
+  style_manager sm;
+
+  /* We assume double-column chars for the pieces, so allow two canvas
+     columns per square.  */
+  canvas canvas (canvas::size_t (16, 8), sm);
+
+  for (int x = 0; x < 8; x++)
+    for (int y = 0; y < 8; y++)
+      {
+	enum piece piece_kind;
+	enum color piece_color;
+	switch (y)
+	  {
+	  case 0:
+	  case 7:
+	    switch (x)
+	      {
+	      default:
+		gcc_unreachable ();
+	      case 0:
+		piece_kind = piece::ROOK;
+		break;
+	      case 1:
+		piece_kind = piece::KNIGHT;
+		break;
+	      case 2:
+		piece_kind = piece::BISHOP;
+		break;
+	      case 3:
+		piece_kind = piece::QUEEN;
+		break;
+	      case 4:
+		piece_kind = piece::KING;
+		break;
+	      case 5:
+		piece_kind = piece::BISHOP;
+		break;
+	      case 6:
+		piece_kind = piece::KNIGHT;
+		break;
+	      case 7:
+		piece_kind = piece::ROOK;
+	      break;
+	      }
+	    piece_color = (y == 0) ? color::BLACK : color::WHITE;
+	    break;
+	  case 1:
+	  case 6:
+	    piece_kind = piece::PAWN;
+	    piece_color = (y == 1) ? color::BLACK : color::WHITE;
+	    break;
+	  default:
+	    piece_kind = piece::NONE;
+	    piece_color = color::NONE;
+	    break;
+	  }
+
+	style s;
+	const bool white_square = (x + y) % 2 == 0;
+	if (white_square)
+	  s.m_bg_color = style::color (0xf0, 0xd9, 0xb5);
+	else
+	  s.m_bg_color = style::color (0xb5, 0x88, 0x63);
+	switch (piece_color)
+	  {
+	  default:
+	    gcc_unreachable ();
+	  case color::WHITE:
+	    s.m_fg_color = style::color (0xff, 0xff, 0xff);
+	    break;
+	  case color::BLACK:
+	    s.m_fg_color = style::color (0x00, 0x00, 0x00);
+	    break;
+	  case color::NONE:
+	    break;
+	  }
+	style::id_t style_id = sm.get_or_create_id (s);
+
+	cppchar_t ch;
+	if (piece_kind == piece::NONE)
+	  ch = ' ';
+	else
+	  {
+	    const cppchar_t WHITE_KING = 0x2654;
+	    const cppchar_t BLACK_KING = 0x265A;
+	    cppchar_t base ((piece_color == color::WHITE)
+			    ? WHITE_KING : BLACK_KING);
+	    ch = base + ((int)piece_kind - (int)piece::KING);
+	  }
+	canvas.paint (canvas::coord_t (x * 2, y),
+		      canvas::cell_t (ch, false, style_id));
+	canvas.paint (canvas::coord_t (x * 2 + 1, y),
+		      canvas::cell_t (' ', false, style_id));
+      }
+  emit_canvas (canvas, "test_chessboard");
+}
+
+/* Table tests.  */
+
+static void
+emit_table (const table &table, const style_manager &sm, const char *alt_text)
+{
+  const text_art::theme *theme = global_dc->m_diagrams.m_theme;
+  if (!theme)
+    return;
+  canvas c (table.to_canvas (*theme, sm));
+  emit_canvas (c, alt_text);
+}
+
+static void
+test_double_width_chars ()
+{
+  style_manager sm;
+  table table (table::size_t (1, 1));
+  table.set_cell (table::coord_t (0,0),
+		  styled_string ((cppchar_t)0x1f642));
+
+  emit_table (table, sm, "test_double_width_chars");
+}
+
+static void
+test_ipv4_header ()
+{
+  style_manager sm;
+  table table (table::size_t (34, 10));
+  table.set_cell (table::coord_t (0, 0), styled_string (sm, "Offsets"));
+  table.set_cell (table::coord_t (1, 0), styled_string (sm, "Octet"));
+  table.set_cell (table::coord_t (0, 1), styled_string (sm, "Octet"));
+  for (int octet = 0; octet < 4; octet++)
+    table.set_cell_span (table::rect_t (table::coord_t (2 + (octet * 8), 0),
+					table::size_t (8, 1)),
+			 styled_string::from_fmt (sm, nullptr, "%i", octet));
+  table.set_cell (table::coord_t (1, 1), styled_string (sm, "Bit"));
+  for (int bit = 0; bit < 32; bit++)
+    table.set_cell (table::coord_t (bit + 2, 1),
+		    styled_string::from_fmt (sm, nullptr, "%i", bit));
+  for (int word = 0; word < 6; word++)
+    {
+      table.set_cell (table::coord_t (0, word + 2),
+		      styled_string::from_fmt (sm, nullptr, "%i", word * 4));
+      table.set_cell (table::coord_t (1, word + 2),
+		      styled_string::from_fmt (sm, nullptr, "%i", word * 32));
+    }
+
+  table.set_cell (table::coord_t (0, 8), styled_string (sm, "..."));
+  table.set_cell (table::coord_t (1, 8), styled_string (sm, "..."));
+  table.set_cell (table::coord_t (0, 9), styled_string (sm, "56"));
+  table.set_cell (table::coord_t (1, 9), styled_string (sm, "448"));
+
+#define SET_BITS(FIRST, LAST, NAME)					\
+  do {									\
+    const int first = (FIRST);						\
+    const int last = (LAST);						\
+    const char *name = (NAME);						\
+    const int row = first / 32;						\
+    gcc_assert (last / 32 == row);					\
+    table::rect_t rect (table::coord_t ((first % 32) + 2, row + 2),	\
+			table::size_t (last + 1 - first , 1));		\
+    table.set_cell_span (rect, styled_string (sm, name));		\
+  } while (0)
+
+  SET_BITS (0, 3, "Version");
+  SET_BITS (4, 7, "IHL");
+  SET_BITS (8, 13, "DSCP");
+  SET_BITS (14, 15, "ECN");
+  SET_BITS (16, 31, "Total Length");
+
+  SET_BITS (32 +  0, 32 + 15, "Identification");
+  SET_BITS (32 + 16, 32 + 18, "Flags");
+  SET_BITS (32 + 19, 32 + 31, "Fragment Offset");
+
+  SET_BITS (64 +  0, 64 +  7, "Time To Live");
+  SET_BITS (64 +  8, 64 + 15, "Protocol");
+  SET_BITS (64 + 16, 64 + 31, "Header Checksum");
+
+  SET_BITS (96 +  0, 96 + 31, "Source IP Address");
+  SET_BITS (128 +  0, 128 + 31, "Destination IP Address");
+
+  table.set_cell_span(table::rect_t (table::coord_t (2, 7),
+				     table::size_t (32, 3)),
+		      styled_string (sm, "Options"));
+
+  emit_table (table, sm, "test_ipv4_header");
+}
+
+static void
+show_diagrams ()
+{
+  test_abc ();
+  test_chessboard ();
+  test_double_width_chars ();
+  test_ipv4_header ();
+}
+
+int
+plugin_init (struct plugin_name_args *plugin_info,
+	     struct plugin_gcc_version *version)
+{
+  const char *plugin_name = plugin_info->base_name;
+  int argc = plugin_info->argc;
+  struct plugin_argument *argv = plugin_info->argv;
+
+  if (!plugin_default_version_check (version, &gcc_version))
+    return 1;
+
+  show_diagrams ();
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/plugin/plugin.exp b/gcc/testsuite/gcc.dg/plugin/plugin.exp
index 4d6304c..60723a2 100644
--- a/gcc/testsuite/gcc.dg/plugin/plugin.exp
+++ b/gcc/testsuite/gcc.dg/plugin/plugin.exp
@@ -114,6 +114,12 @@
 	  diagnostic-path-format-inline-events-1.c \
 	  diagnostic-path-format-inline-events-2.c \
 	  diagnostic-path-format-inline-events-3.c } \
+    { diagnostic_plugin_test_text_art.c \
+	  diagnostic-test-text-art-none.c \
+	  diagnostic-test-text-art-ascii-bw.c \
+	  diagnostic-test-text-art-ascii-color.c \
+	  diagnostic-test-text-art-unicode-bw.c \
+	  diagnostic-test-text-art-unicode-color.c } \
     { location_overflow_plugin.c \
 	  location-overflow-test-1.c \
 	  location-overflow-test-2.c \
diff --git a/gcc/testsuite/gcc.dg/torture/pr110332-1.c b/gcc/testsuite/gcc.dg/torture/pr110332-1.c
new file mode 100644
index 0000000..438993e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/pr110332-1.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+
+struct s { int lie; };
+struct s si7, si8;
+unsigned u9, u6;
+_Bool b3, b4;
+unsigned transferValues(struct s *End) {
+  unsigned RegIdx;
+  unsigned *t = b4 ? &u6 : &u9;
+  RegIdx = *t;
+  *End = *(b3 ? &si7 : &si8);
+  return RegIdx;
+}
diff --git a/gcc/testsuite/gcc.dg/torture/pr110332-2.c b/gcc/testsuite/gcc.dg/torture/pr110332-2.c
new file mode 100644
index 0000000..18b656f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/pr110332-2.c
@@ -0,0 +1,10 @@
+/* { dg-do compile } */
+
+_Bool a;
+struct s { int t; } c, d;
+unsigned e, f;
+unsigned transferValues(struct s *End) {
+  unsigned RegIdx = *(a ? &e : &f);
+  *End = *(a ? &c : &d);
+  return RegIdx;
+}
diff --git a/gcc/testsuite/gcc.dg/vect/pr110381.c b/gcc/testsuite/gcc.dg/vect/pr110381.c
new file mode 100644
index 0000000..2313dbf
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/pr110381.c
@@ -0,0 +1,40 @@
+/* { dg-do run } */
+
+struct FOO {
+   double a;
+   double b;
+   double c;
+};
+
+double __attribute__((noipa))
+sum_8_foos(const struct FOO* foos)
+{
+  double sum = 0;
+
+  for (int i = 0; i < 8; ++i)
+    {
+      struct FOO foo = foos[i];
+
+      /* Need to use an in-order reduction here, preserving
+         the load permutation.  */
+      sum += foo.a;
+      sum += foo.c;
+      sum += foo.b;
+    }
+
+  return sum;
+}
+
+int main()
+{
+  struct FOO foos[8];
+
+  __builtin_memset (foos, 0, sizeof (foos));
+  foos[0].a = __DBL_MAX__;
+  foos[0].b = 5;
+  foos[0].c = -__DBL_MAX__;
+
+  if (sum_8_foos (foos) != 5)
+    __builtin_abort ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/pr110371.c b/gcc/testsuite/gcc.target/aarch64/pr110371.c
new file mode 100644
index 0000000..444e514e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/pr110371.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O3" } */
+
+typedef struct dest
+{
+  double m[3][3];
+} dest;
+
+typedef struct src
+{
+  int m[3][3];
+} src;
+
+void
+foo (dest *a, src* s)
+{
+  for (int i = 0; i != 3; i++)
+    for (int j = 0; j != 3; j++)
+      a->m[i][j] = s->m[i][j];
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/pr110280.c b/gcc/testsuite/gcc.target/aarch64/sve/pr110280.c
new file mode 100644
index 0000000..d3279f3
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve/pr110280.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -fdump-tree-optimized" } */
+
+#include "arm_sve.h"
+
+svuint32_t l()
+{
+  _Alignas(16) const unsigned int lanes[4] = {0, 0, 0, 0};
+  return svld1rq_u32(svptrue_b8(), lanes);
+}
+
+/* { dg-final { scan-tree-dump-not "VEC_PERM_EXPR" "optimized" } } */
diff --git a/gcc/testsuite/gcc.target/i386/ashldi3-1.c b/gcc/testsuite/gcc.target/i386/ashldi3-1.c
new file mode 100644
index 0000000..b61d63b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ashldi3-1.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2" } */
+
+long long foo(long long x, int y)
+{
+  long long t = (long long)y << 32;
+  return x ^ t;
+}
+
+long long bar(long long x, int y)
+{
+  long long t = (long long)y << 35;
+  return x ^ t;
+}
+
+/* { dg-final { scan-assembler-times "xorl" 2 } } */
diff --git a/gcc/testsuite/gcc.target/i386/ashlti3-2.c b/gcc/testsuite/gcc.target/i386/ashlti3-2.c
new file mode 100644
index 0000000..7e21ab9
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/ashlti3-2.c
@@ -0,0 +1,17 @@
+/* { dg-do compile { target int128 } } */
+/* { dg-options "-O2" } */
+
+__int128 foo(__int128 x, long long y)
+{
+  __int128 t = (__int128)y << 64;
+  return x ^ t;
+}
+
+__int128 bar(__int128 x, long long y)
+{
+  __int128 t = (__int128)y << 67;
+  return x ^ t;
+}
+
+/* { dg-final { scan-assembler-not "xorl" } } */
+/* { dg-final { scan-assembler-times "xorq" 2 } } */
diff --git a/gcc/testsuite/gcc.target/i386/avx-vptest-4.c b/gcc/testsuite/gcc.target/i386/avx-vptest-4.c
new file mode 100644
index 0000000..0a234e1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/avx-vptest-4.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mavx" } */
+
+typedef long long __m256i __attribute__ ((__vector_size__ (32)));
+
+int foo (__m256i x, __m256i y)
+{
+  __m256i a = x & ~y;
+  return __builtin_ia32_ptestz256 (a, a);
+}
+
+int bar (__m256i x, __m256i y)
+{
+  __m256i a = ~x & y;
+  return __builtin_ia32_ptestz256 (a, a);
+}
+
+/* { dg-final { scan-assembler-times "vptest\[ \\t\]+%" 2 } } */
+/* { dg-final { scan-assembler-times "setc" 2 } } */
+/* { dg-final { scan-assembler-not "vpandn" } } */
+/* { dg-final { scan-assembler-not "sete" } } */
diff --git a/gcc/testsuite/gcc.target/i386/avx-vptest-5.c b/gcc/testsuite/gcc.target/i386/avx-vptest-5.c
new file mode 100644
index 0000000..fd0e5e2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/avx-vptest-5.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mavx" } */
+
+typedef long long __m256i __attribute__ ((__vector_size__ (32)));
+
+int foo (__m256i x, __m256i y)
+{
+  __m256i a = x & ~y;
+  return !__builtin_ia32_ptestz256 (a, a);
+}
+
+int bar (__m256i x, __m256i y)
+{
+  __m256i a = ~x & y;
+  return !__builtin_ia32_ptestz256 (a, a);
+}
+
+/* { dg-final { scan-assembler-times "vptest\[ \\t\]+%" 2} } */
+/* { dg-final { scan-assembler-times "setnc" 2 } } */
+/* { dg-final { scan-assembler-not "vpandn" } } */
+/* { dg-final { scan-assembler-not "setne" } } */
diff --git a/gcc/testsuite/gcc.target/i386/avx-vptest-6.c b/gcc/testsuite/gcc.target/i386/avx-vptest-6.c
new file mode 100644
index 0000000..5821a92
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/avx-vptest-6.c
@@ -0,0 +1,40 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mavx" } */
+
+typedef long long __m256i __attribute__ ((__vector_size__ (32)));
+
+extern void ext (void);
+
+void foo (__m256i x, __m256i y)
+{
+  __m256i a = x & ~y;
+  if (__builtin_ia32_ptestz256 (a, a))
+    ext();
+}
+
+void bar (__m256i x, __m256i y)
+{
+  __m256i a = ~x & y;
+  if (__builtin_ia32_ptestz256 (a, a))
+    ext();
+}
+
+void foo2 (__m256i x, __m256i y)
+{
+  __m256i a = x & ~y;
+  if (__builtin_ia32_ptestz256 (a, a))
+    ext();
+}
+
+void bar2 (__m256i x, __m256i y)
+{
+  __m256i a = ~x & y;
+  if (__builtin_ia32_ptestz256 (a, a))
+    ext();
+}
+
+/* { dg-final { scan-assembler-times "ptest\[ \\t\]+%" 4 } } */
+/* { dg-final { scan-assembler-times "jn?c" 4 } } */
+/* { dg-final { scan-assembler-not "pandn" } } */
+/* { dg-final { scan-assembler-not "jne" } } */
+/* { dg-final { scan-assembler-not "je" } } */
diff --git a/gcc/testsuite/gcc.target/i386/mvc17.c b/gcc/testsuite/gcc.target/i386/mvc17.c
new file mode 100644
index 0000000..2c7cc2f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/mvc17.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O2" } */
+/* { dg-final { scan-assembler-times "rep mov" 1 } } */
+
+__attribute__((target_clones("default","arch=icelake-server")))
+void
+foo (char *a, char *b, int size)
+{
+  __builtin_memcpy (a, b, size & 0x7F);
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr109973-1.c b/gcc/testsuite/gcc.target/i386/pr109973-1.c
index a1b6136b..1d812dd 100644
--- a/gcc/testsuite/gcc.target/i386/pr109973-1.c
+++ b/gcc/testsuite/gcc.target/i386/pr109973-1.c
@@ -10,4 +10,4 @@
   return __builtin_ia32_ptestc256 (a, a);
 }
 
-/* { dg-final { scan-assembler "vpand" } } */
+/* { dg-final { scan-assembler "movl\[ \\t]*\\\$1, %eax" } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr109973-2.c b/gcc/testsuite/gcc.target/i386/pr109973-2.c
index 167f6ee..1068c3e 100644
--- a/gcc/testsuite/gcc.target/i386/pr109973-2.c
+++ b/gcc/testsuite/gcc.target/i386/pr109973-2.c
@@ -10,4 +10,4 @@
   return __builtin_ia32_ptestc128 (a, a);
 }
 
-/* { dg-final { scan-assembler "pand" } } */
+/* { dg-final { scan-assembler "movl\[ \\t]*\\\$1, %eax" } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr110018-1.c b/gcc/testsuite/gcc.target/i386/pr110018-1.c
index b6a3be7..24eeca6 100644
--- a/gcc/testsuite/gcc.target/i386/pr110018-1.c
+++ b/gcc/testsuite/gcc.target/i386/pr110018-1.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-mavx512fp16 -mavx512vl -O2 -mavx512dq" } */
+/* { dg-options "-mavx512fp16 -mavx512vl -O2 -mavx512dq -fno-trapping-math" } */
 /* { dg-final { scan-assembler-times {(?n)vcvttp[dsh]2[dqw]} 5 } } */
 /* { dg-final { scan-assembler-times {(?n)vcvt[dqw]*2p[dsh]} 5 } } */
 
diff --git a/gcc/testsuite/gcc.target/i386/pr110018-2.c b/gcc/testsuite/gcc.target/i386/pr110018-2.c
index a663e07..9a2d9e1 100644
--- a/gcc/testsuite/gcc.target/i386/pr110018-2.c
+++ b/gcc/testsuite/gcc.target/i386/pr110018-2.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-mavx512fp16 -mavx512vl -O2 -mavx512dq" } */
+/* { dg-options "-mavx512fp16 -mavx512vl -O2 -mavx512dq -fno-trapping-math" } */
 /* { dg-final { scan-assembler-times {(?n)vcvttp[dsh]2[dqw]} 5 } } */
 /* { dg-final { scan-assembler-times {(?n)vcvt[dqw]*2p[dsh]} 5 } } */
 
diff --git a/gcc/testsuite/gcc.target/i386/pr110309.c b/gcc/testsuite/gcc.target/i386/pr110309.c
new file mode 100644
index 0000000..f6e9e9c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr110309.c
@@ -0,0 +1,10 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 --param vect-partial-vector-usage=1 -march=znver4 -mprefer-vector-width=256" } */
+/* { dg-final { scan-assembler-not {(?n)vpblendd.*ymm} } } */
+
+
+void foo (int * __restrict a, int *b)
+{
+  for (int i = 0; i < 6; ++i)
+    a[i] = b[i] + 42;
+}
diff --git a/gcc/testsuite/gcc.target/i386/sse4_1-ptest-4.c b/gcc/testsuite/gcc.target/i386/sse4_1-ptest-4.c
new file mode 100644
index 0000000..e74ddb3
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/sse4_1-ptest-4.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -msse4.1" } */
+
+typedef long long __m128i __attribute__ ((__vector_size__ (16)));
+
+int foo (__m128i x, __m128i y)
+{
+  __m128i a = x & ~y;
+  return __builtin_ia32_ptestz128 (a, a);
+}
+
+int bar (__m128i x, __m128i y)
+{
+  __m128i a = ~x & y;
+  return __builtin_ia32_ptestz128 (a, a);
+}
+
+/* { dg-final { scan-assembler-times "ptest\[ \\t\]+%" 2 } } */
+/* { dg-final { scan-assembler-times "setc" 2 } } */
+/* { dg-final { scan-assembler-not "pandn" } } */
+/* { dg-final { scan-assembler-not "sete" } } */
+
diff --git a/gcc/testsuite/gcc.target/i386/sse4_1-ptest-5.c b/gcc/testsuite/gcc.target/i386/sse4_1-ptest-5.c
new file mode 100644
index 0000000..74b0a8c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/sse4_1-ptest-5.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -msse4.1" } */
+
+typedef long long __m128i __attribute__ ((__vector_size__ (16)));
+
+int foo (__m128i x, __m128i y)
+{
+  __m128i a = x & ~y;
+  return !__builtin_ia32_ptestz128 (a, a);
+}
+
+int bar (__m128i x, __m128i y)
+{
+  __m128i a = ~x & y;
+  return !__builtin_ia32_ptestz128 (a, a);
+}
+
+/* { dg-final { scan-assembler-times "ptest\[ \\t\]+%" 2 } } */
+/* { dg-final { scan-assembler-times "setnc" 2 } } */
+/* { dg-final { scan-assembler-not "pandn" } } */
+/* { dg-final { scan-assembler-not "setne" } } */
+
diff --git a/gcc/testsuite/gcc.target/i386/sse4_1-ptest-6.c b/gcc/testsuite/gcc.target/i386/sse4_1-ptest-6.c
new file mode 100644
index 0000000..d9114bb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/sse4_1-ptest-6.c
@@ -0,0 +1,40 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -msse4.1" } */
+
+typedef long long __m128i __attribute__ ((__vector_size__ (16)));
+
+extern void ext (void);
+
+void foo (__m128i x, __m128i y)
+{
+  __m128i a = x & ~y;
+  if (__builtin_ia32_ptestz128 (a, a))
+    ext();
+}
+
+void bar (__m128i x, __m128i y)
+{
+  __m128i a = ~x & y;
+  if (__builtin_ia32_ptestz128 (a, a))
+    ext();
+}
+
+void foo2 (__m128i x, __m128i y)
+{
+  __m128i a = x & ~y;
+  if (__builtin_ia32_ptestz128 (a, a))
+    ext();
+}
+
+void bar2 (__m128i x, __m128i y)
+{
+  __m128i a = ~x & y;
+  if (__builtin_ia32_ptestz128 (a, a))
+    ext();
+}
+
+/* { dg-final { scan-assembler-times "ptest\[ \\t\]+%" 4 } } */
+/* { dg-final { scan-assembler-times "jn?c" 4 } } */
+/* { dg-final { scan-assembler-not "pandn" } } */
+/* { dg-final { scan-assembler-not "jne" } } */
+/* { dg-final { scan-assembler-not "je" } } */
diff --git a/gcc/testsuite/gcc.target/powerpc/fusion-p10-ldcmpi.c b/gcc/testsuite/gcc.target/powerpc/fusion-p10-ldcmpi.c
index 526a026..165bd9a 100644
--- a/gcc/testsuite/gcc.target/powerpc/fusion-p10-ldcmpi.c
+++ b/gcc/testsuite/gcc.target/powerpc/fusion-p10-ldcmpi.c
@@ -54,15 +54,17 @@
 TEST(int8_t)
 
 /* { dg-final { scan-assembler-times "lbz_cmpldi_cr0_QI_clobber_CCUNS_zero"   4 { target lp64 } } } */
-/* { dg-final { scan-assembler-times "ld_cmpdi_cr0_DI_DI_CC_none"             4 { target lp64 } } } */
-/* { dg-final { scan-assembler-times "ld_cmpdi_cr0_DI_clobber_CC_none"        4 { target lp64 } } } */
-/* { dg-final { scan-assembler-times "ld_cmpldi_cr0_DI_DI_CCUNS_none"         1 { target lp64 } } } */
-/* { dg-final { scan-assembler-times "ld_cmpldi_cr0_DI_clobber_CCUNS_none"    1 { target lp64 } } } */
+/* { dg-final { scan-assembler-times "ld_cmpdi_cr0_DI_DI_CC_none"            24 { target lp64 } } } */
+/* { dg-final { scan-assembler-times "ld_cmpdi_cr0_DI_clobber_CC_none"        8 { target lp64 } } } */
+/* { dg-final { scan-assembler-times "ld_cmpldi_cr0_DI_DI_CCUNS_none"         2 { target lp64 } } } */
+/* { dg-final { scan-assembler-times "ld_cmpldi_cr0_DI_clobber_CCUNS_none"    2 { target lp64 } } } */
 /* { dg-final { scan-assembler-times "lha_cmpdi_cr0_HI_clobber_CC_sign"      16 { target lp64 } } } */
 /* { dg-final { scan-assembler-times "lhz_cmpldi_cr0_HI_clobber_CCUNS_zero"   4 { target lp64 } } } */
 /* { dg-final { scan-assembler-times "lwa_cmpdi_cr0_SI_EXTSI_CC_sign"         0 { target lp64 } } } */
-/* { dg-final { scan-assembler-times "lwa_cmpdi_cr0_SI_clobber_CC_none"       4 { target lp64 } } } */
+/* { dg-final { scan-assembler-times "lwz_cmpwi_cr0_SI_clobber_CC_none"       8 { target lp64 } } } */
+/* { dg-final { scan-assembler-times "lwz_cmpwi_cr0_SI_SI_CC_none"            8 { target lp64 } } } */
 /* { dg-final { scan-assembler-times "lwz_cmpldi_cr0_SI_EXTSI_CCUNS_zero"     0 { target lp64 } } } */
+/* { dg-final { scan-assembler-times "lwz_cmpldi_cr0_SI_SI_CCUNS_none"        2 { target lp64 } } } */
 /* { dg-final { scan-assembler-times "lwz_cmpldi_cr0_SI_clobber_CCUNS_none"   2 { target lp64 } } } */
 
 /* { dg-final { scan-assembler-times "lbz_cmpldi_cr0_QI_clobber_CCUNS_zero"   2 { target ilp32 } } } */
@@ -73,6 +75,8 @@
 /* { dg-final { scan-assembler-times "lha_cmpdi_cr0_HI_clobber_CC_sign"       8 { target ilp32 } } } */
 /* { dg-final { scan-assembler-times "lhz_cmpldi_cr0_HI_clobber_CCUNS_zero"   2 { target ilp32 } } } */
 /* { dg-final { scan-assembler-times "lwa_cmpdi_cr0_SI_EXTSI_CC_sign"         0 { target ilp32 } } } */
-/* { dg-final { scan-assembler-times "lwa_cmpdi_cr0_SI_clobber_CC_none"       9 { target ilp32 } } } */
+/* { dg-final { scan-assembler-times "lwz_cmpwi_cr0_SI_SI_CC_none"           36 { target ilp32 } } } */
+/* { dg-final { scan-assembler-times "lwz_cmpwi_cr0_SI_clobber_CC_none"      16 { target ilp32 } } } */
 /* { dg-final { scan-assembler-times "lwz_cmpldi_cr0_SI_EXTSI_CCUNS_zero"     0 { target ilp32 } } } */
 /* { dg-final { scan-assembler-times "lwz_cmpldi_cr0_SI_clobber_CCUNS_none"   6 { target ilp32 } } } */
+/* { dg-final { scan-assembler-times "lwz_cmpldi_cr0_SI_SI_CCUNS_none"        2 { target ilp32 } } } */
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/partial/gimple_fold-1.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/partial/gimple_fold-1.c
new file mode 100644
index 0000000..23407a2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/partial/gimple_fold-1.c
@@ -0,0 +1,43 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv32gcv -mabi=ilp32d --param riscv-autovec-preference=fixed-vlmax --param riscv-autovec-lmul=m8 -O3 -fdump-tree-optimized-details" } */
+
+#include <stdint-gcc.h>
+
+#define SZ 255
+
+#define DEF(TYPE) void fn_##TYPE (TYPE *__restrict a);
+
+#define RUN(TYPE)                                                              \
+  TYPE a##TYPE[SZ];                                                            \
+  for (int i = 0; i < SZ; i++)                                                 \
+    {                                                                          \
+      a##TYPE[i] = 127;                                                        \
+    }                                                                          \
+  fn_##TYPE (a##TYPE);
+
+#define RUN_ALL()                                                              \
+  RUN (int8_t)                                                                 \
+  RUN (int16_t)                                                                \
+  RUN (int32_t)                                                                \
+  RUN (int64_t)                                                                \
+  RUN (uint8_t)                                                                \
+  RUN (uint16_t)                                                               \
+  RUN (uint32_t)                                                               \
+  RUN (uint64_t)
+
+DEF (int8_t)
+DEF (int16_t)
+DEF (int32_t)
+DEF (int64_t)
+DEF (uint8_t)
+DEF (uint16_t)
+DEF (uint32_t)
+DEF (uint64_t)
+
+int
+main ()
+{
+  RUN_ALL ()
+}
+
+/* { dg-final { scan-tree-dump-times "\.LEN_MASK_STORE" 6 "optimized" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/partial/select_vl-1.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/partial/select_vl-1.c
index 74bbf40..e27090d 100644
--- a/gcc/testsuite/gcc.target/riscv/rvv/autovec/partial/select_vl-1.c
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/partial/select_vl-1.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-additional-options "-march=rv32gcv -mabi=ilp32d --param riscv-autovec-preference=scalable -fno-vect-cost-model -fno-tree-loop-distribute-patterns -fdump-tree-optimized-details" } */
+/* { dg-additional-options "-march=rv32gcv_zvfh -mabi=ilp32d --param riscv-autovec-preference=scalable -fno-vect-cost-model -fno-tree-loop-distribute-patterns -fdump-tree-optimized-details" } */
 
 #include <stdint-gcc.h>
 
@@ -20,7 +20,10 @@
   TEST_TYPE (uint32_t)                                                         \
   TEST_TYPE (int64_t)                                                          \
   TEST_TYPE (uint64_t)                                                         \
+  TEST_TYPE (_Float16)                                                         \
   TEST_TYPE (float)                                                            \
   TEST_TYPE (double)
 
 TEST_ALL ()
+
+/* { dg-final { scan-tree-dump-times "\.SELECT_VL" 11 "optimized" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/partial/select_vl-2.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/partial/select_vl-2.c
new file mode 100644
index 0000000..eac7cbc
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/partial/select_vl-2.c
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcv -mabi=lp64d --param riscv-autovec-preference=scalable -fno-schedule-insns --param riscv-autovec-lmul=m1 -O3 -ftree-vectorize" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+#include <stdint-gcc.h>
+
+/*
+** foo:
+**	vsetivli\t[a-x0-9]+,\s*8,\s*e(8?|16?|32?|64),\s*m(1?|2?|4?|8?|f2?|f4?|f8),\s*t[au],\s*m[au]
+**	vle32\.v\tv[0-9]+,0\([a-x0-9]+\)
+**	...
+**	vsetvli\t[a-x0-9]+,\s*[a-x0-9]+,\s*e(8?|16?|32?|64),\s*m(1?|2?|4?|8?|f2?|f4?|f8),\s*t[au],\s*m[au]
+**	add\t[a-x0-9]+,[a-x0-9]+,[a-x0-9]+
+**	vle32\.v\tv[0-9]+,0\([a-x0-9]+\)
+**	...
+*/
+void
+foo (int32_t *__restrict a,
+     int32_t *__restrict b,
+     int32_t *__restrict cond)
+{
+  for (int i = 0; i < 8; i++)
+    if (cond[i])
+      a[i] = b[i];
+}
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/partial/single_rgroup-2.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/partial/single_rgroup-2.c
new file mode 100644
index 0000000..24490dc
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/partial/single_rgroup-2.c
@@ -0,0 +1,8 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-march=rv32gcv_zvfhmin -mabi=ilp32d --param riscv-autovec-preference=fixed-vlmax -fdump-tree-vect-details" } */
+
+#include "single_rgroup-2.h"
+
+TEST_ALL (test_1)
+
+/* { dg-final { scan-tree-dump-times "vectorized 1 loops in function" 11 "vect" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/partial/single_rgroup-2.h b/gcc/testsuite/gcc.target/riscv/rvv/autovec/partial/single_rgroup-2.h
new file mode 100644
index 0000000..a94f3eb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/partial/single_rgroup-2.h
@@ -0,0 +1,44 @@
+#include <assert.h>
+#include <stdint-gcc.h>
+
+#define N 777
+
+#define test_1(TYPE)                                                           \
+  TYPE a_##TYPE[N] = {0};                                                      \
+  TYPE b_##TYPE[N] = {0};                                                      \
+  void __attribute__ ((noinline, noclone))                                     \
+  test_1_##TYPE (int *__restrict cond)                                         \
+  {                                                                            \
+    unsigned int i = 0;                                                        \
+    for (i = 0; i < 8; i++)                                                    \
+      if (cond[i])                                                             \
+	b_##TYPE[i] = a_##TYPE[i];                                             \
+  }
+
+#define run_1(TYPE)                                                            \
+  int cond_##TYPE[N] = {0};                                                    \
+  for (unsigned int i = 0; i < N; i++)                                         \
+    a_##TYPE[i] = i * 2 * 33 + 1 + 109;                                        \
+  for (unsigned int i = 0; i < N; i++)                                         \
+    cond_##TYPE[i] = i & 1;                                                    \
+  test_1_##TYPE (cond_##TYPE);                                                 \
+  for (unsigned int i = 0; i < N; i++)                                         \
+    {                                                                          \
+      if (cond_##TYPE[i] && i < 8)                                             \
+	assert (b_##TYPE[i] == a_##TYPE[i]);                                   \
+      else                                                                     \
+	assert (b_##TYPE[i] == 0);                                             \
+    }
+
+#define TEST_ALL(T)                                                            \
+  T (int8_t)                                                                   \
+  T (uint8_t)                                                                  \
+  T (int16_t)                                                                  \
+  T (uint16_t)                                                                 \
+  T (int32_t)                                                                  \
+  T (uint32_t)                                                                 \
+  T (int64_t)                                                                  \
+  T (uint64_t)                                                                 \
+  T (_Float16)                                                                 \
+  T (float)                                                                    \
+  T (double)
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/partial/single_rgroup-3.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/partial/single_rgroup-3.c
new file mode 100644
index 0000000..9cbae13
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/partial/single_rgroup-3.c
@@ -0,0 +1,8 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-march=rv32gcv_zvfhmin -mabi=ilp32d --param riscv-autovec-preference=scalable -fdump-tree-vect-details" } */
+
+#include "single_rgroup-3.h"
+
+TEST_ALL (test_1)
+
+/* { dg-final { scan-tree-dump-times "vectorized 1 loops in function" 11 "vect" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/partial/single_rgroup-3.h b/gcc/testsuite/gcc.target/riscv/rvv/autovec/partial/single_rgroup-3.h
new file mode 100644
index 0000000..e60e0b1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/partial/single_rgroup-3.h
@@ -0,0 +1,149 @@
+#include <assert.h>
+#include <stdint-gcc.h>
+
+#define N 777
+
+int cond[N] = {0};
+#define test_1(TYPE)                                                           \
+  TYPE a_##TYPE[N];                                                            \
+  TYPE b_##TYPE[N];                                                            \
+  void __attribute__ ((noinline, noclone)) test_1_##TYPE (unsigned int n)      \
+  {                                                                            \
+    unsigned int i = 0;                                                        \
+    for (i = 0; i < n; i++)                                                    \
+      if (cond[i])                                                             \
+	b_##TYPE[i] = a_##TYPE[i];                                             \
+  }
+
+#define run_1(TYPE)                                                            \
+  for (unsigned int i = 0; i < N; i++)                                         \
+    a_##TYPE[i] = i * 2 * 33 + 1 + 109;                                        \
+  test_1_##TYPE (5);                                                           \
+  for (unsigned int i = 0; i < N; i++)                                         \
+    {                                                                          \
+      if (cond[i] && i < 5)                                                    \
+	assert (b_##TYPE[i] == a_##TYPE[i]);                                   \
+      else                                                                     \
+	assert (b_##TYPE[i] == 0);                                             \
+    }
+
+#define run_2(TYPE)                                                            \
+  for (unsigned int i = 0; i < N; i++)                                         \
+    a_##TYPE[i] = i * 2 * 57 + 1 + 999;                                        \
+  test_1_##TYPE (17);                                                          \
+  for (unsigned int i = 0; i < N; i++)                                         \
+    {                                                                          \
+      if (cond[i] && i < 17)                                                   \
+	assert (b_##TYPE[i] == a_##TYPE[i]);                                   \
+      else                                                                     \
+	assert (b_##TYPE[i] == 0);                                             \
+    }
+
+#define run_3(TYPE)                                                            \
+  for (unsigned int i = 0; i < N; i++)                                         \
+    a_##TYPE[i] = i * 2 * 77 + 1 + 3;                                          \
+  test_1_##TYPE (32);                                                          \
+  for (unsigned int i = 0; i < N; i++)                                         \
+    {                                                                          \
+      if (cond[i] && i < 32)                                                   \
+	assert (b_##TYPE[i] == a_##TYPE[i]);                                   \
+      else                                                                     \
+	assert (b_##TYPE[i] == 0);                                             \
+    }
+
+#define run_4(TYPE)                                                            \
+  for (unsigned int i = 0; i < N; i++)                                         \
+    a_##TYPE[i] = i * 2 * 45 + 1 + 11;                                         \
+  test_1_##TYPE (128);                                                         \
+  for (unsigned int i = 0; i < N; i++)                                         \
+    {                                                                          \
+      if (cond[i] && i < 128)                                                  \
+	assert (b_##TYPE[i] == a_##TYPE[i]);                                   \
+      else                                                                     \
+	assert (b_##TYPE[i] == 0);                                             \
+    }
+
+#define run_5(TYPE)                                                            \
+  for (unsigned int i = 0; i < N; i++)                                         \
+    a_##TYPE[i] = i * 2 * 199 + 1 + 79;                                        \
+  test_1_##TYPE (177);                                                         \
+  for (unsigned int i = 0; i < N; i++)                                         \
+    {                                                                          \
+      if (cond[i] && i < 177)                                                  \
+	assert (b_##TYPE[i] == a_##TYPE[i]);                                   \
+      else                                                                     \
+	assert (b_##TYPE[i] == 0);                                             \
+    }
+
+#define run_6(TYPE)                                                            \
+  for (unsigned int i = 0; i < N; i++)                                         \
+    a_##TYPE[i] = i * 2 * 377 + 1 + 73;                                        \
+  test_1_##TYPE (255);                                                         \
+  for (unsigned int i = 0; i < N; i++)                                         \
+    {                                                                          \
+      if (cond[i] && i < 255)                                                  \
+	assert (b_##TYPE[i] == a_##TYPE[i]);                                   \
+      else                                                                     \
+	assert (b_##TYPE[i] == 0);                                             \
+    }
+
+#define run_7(TYPE)                                                            \
+  for (unsigned int i = 0; i < N; i++)                                         \
+    a_##TYPE[i] = i * 2 * 98 + 1 + 66;                                         \
+  test_1_##TYPE (333);                                                         \
+  for (unsigned int i = 0; i < N; i++)                                         \
+    {                                                                          \
+      if (cond[i] && i < 333)                                                  \
+	assert (b_##TYPE[i] == a_##TYPE[i]);                                   \
+      else                                                                     \
+	assert (b_##TYPE[i] == 0);                                             \
+    }
+
+#define run_8(TYPE)                                                            \
+  for (unsigned int i = 0; i < N; i++)                                         \
+    a_##TYPE[i] = i * 2 * 7 + 1 * 7;                                           \
+  test_1_##TYPE (512);                                                         \
+  for (unsigned int i = 0; i < N; i++)                                         \
+    {                                                                          \
+      if (cond[i] && i < 512)                                                  \
+	assert (b_##TYPE[i] == a_##TYPE[i]);                                   \
+      else                                                                     \
+	assert (b_##TYPE[i] == 0);                                             \
+    }
+
+#define run_9(TYPE)                                                            \
+  for (unsigned int i = 0; i < N; i++)                                         \
+    a_##TYPE[i] = i * 2 + 1 + 88;                                              \
+  test_1_##TYPE (637);                                                         \
+  for (unsigned int i = 0; i < N; i++)                                         \
+    {                                                                          \
+      if (cond[i] && i < 637)                                                  \
+	assert (b_##TYPE[i] == a_##TYPE[i]);                                   \
+      else                                                                     \
+	assert (b_##TYPE[i] == 0);                                             \
+    }
+
+#define run_10(TYPE)                                                           \
+  for (unsigned int i = 0; i < N; i++)                                         \
+    a_##TYPE[i] = i * 2 * 331 + 1 + 547;                                       \
+  test_1_##TYPE (777);                                                         \
+  for (unsigned int i = 0; i < N; i++)                                         \
+    {                                                                          \
+      if (cond[i] && i < 777)                                                  \
+	assert (b_##TYPE[i] == a_##TYPE[i]);                                   \
+      else                                                                     \
+	assert (b_##TYPE[i] == 0);                                             \
+    }
+
+#define TEST_ALL(T)                                                            \
+  T (int8_t)                                                                   \
+  T (uint8_t)                                                                  \
+  T (int16_t)                                                                  \
+  T (uint16_t)                                                                 \
+  T (int32_t)                                                                  \
+  T (uint32_t)                                                                 \
+  T (int64_t)                                                                  \
+  T (uint64_t)                                                                 \
+  T (_Float16)                                                                 \
+  T (float)                                                                    \
+  T (double)
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/partial/single_rgroup_run-2.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/partial/single_rgroup_run-2.c
new file mode 100644
index 0000000..8767efe
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/partial/single_rgroup_run-2.c
@@ -0,0 +1,10 @@
+/* { dg-do run { target { riscv_vector } } } */
+/* { dg-additional-options "--param riscv-autovec-preference=fixed-vlmax" } */
+
+#include "single_rgroup-2.c"
+
+int main (void)
+{
+  TEST_ALL (run_1)
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/partial/single_rgroup_run-3.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/partial/single_rgroup_run-3.c
new file mode 100644
index 0000000..9ff6e92
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/partial/single_rgroup_run-3.c
@@ -0,0 +1,22 @@
+/* { dg-do run { target { riscv_vector } } } */
+/* { dg-additional-options "--param riscv-autovec-preference=scalable" } */
+
+#include "single_rgroup-3.c"
+
+int
+main (void)
+{
+  for (int i = 0; i < N; i++)
+    cond[i] = i & 1;
+  TEST_ALL (run_1)
+  TEST_ALL (run_2)
+  TEST_ALL (run_3)
+  TEST_ALL (run_4)
+  TEST_ALL (run_5)
+  TEST_ALL (run_6)
+  TEST_ALL (run_7)
+  TEST_ALL (run_8)
+  TEST_ALL (run_9)
+  TEST_ALL (run_10)
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-1.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-1.c
index 1996ca6..4420001 100644
--- a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-1.c
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-1.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-additional-options "-march=rv32gcv -mabi=ilp32d --param=riscv-autovec-preference=scalable" } */
+/* { dg-additional-options "-march=rv32gcv_zvfh -mabi=ilp32d --param=riscv-autovec-preference=scalable -ffast-math" } */
 
 #include <stdint-gcc.h>
 
@@ -20,9 +20,13 @@
   TEST_TYPE (int32_t)                                                          \
   TEST_TYPE (uint32_t)                                                         \
   TEST_TYPE (int64_t)                                                          \
-  TEST_TYPE (uint64_t)
+  TEST_TYPE (uint64_t)                                                         \
+  TEST_TYPE (_Float16)                                                         \
+  TEST_TYPE (float)                                                            \
+  TEST_TYPE (double)
 
 TEST_ALL ()
 
 /* { dg-final { scan-assembler-times {\tvmadd\.vv} 8 } } */
+/* { dg-final { scan-assembler-times {\tvfmadd\.vv} 3 } } */
 /* { dg-final { scan-assembler-not {\tvmv} } } */
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-10.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-10.c
new file mode 100644
index 0000000..fc66def
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-10.c
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-march=rv32gcv_zvfh -mabi=ilp32d --param=riscv-autovec-preference=scalable -ffast-math" } */
+
+#include <stdint-gcc.h>
+
+#define TEST_TYPE(TYPE)                                                        \
+  __attribute__ ((noipa)) void ternop_##TYPE (TYPE *__restrict dst,            \
+					      TYPE *__restrict a,              \
+					      TYPE *__restrict b, int n)       \
+  {                                                                            \
+    for (int i = 0; i < n; i++)                                                \
+      dst[i] = -(a[i] * b[i]) - dst[i];                                        \
+  }
+
+#define TEST_ALL()                                                             \
+  TEST_TYPE (_Float16)                                                         \
+  TEST_TYPE (float)                                                            \
+  TEST_TYPE (double)
+
+TEST_ALL ()
+
+/* { dg-final { scan-assembler-times {\tvfnmadd\.vv} 3 } } */
+/* { dg-final { scan-assembler-not {\tvmv} } } */
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-11.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-11.c
new file mode 100644
index 0000000..23c542f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-11.c
@@ -0,0 +1,29 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-march=rv32gcv_zvfh -mabi=ilp32d --param=riscv-autovec-preference=scalable -fno-schedule-insns -ffast-math" } */
+
+#include <stdint-gcc.h>
+
+#define TEST_TYPE(TYPE)                                                        \
+  __attribute__ ((noipa)) void ternop_##TYPE (TYPE *__restrict dest1,          \
+					      TYPE *__restrict dest2,          \
+					      TYPE *__restrict dest3,          \
+					      TYPE *__restrict src1,           \
+					      TYPE *__restrict src2, int n)    \
+  {                                                                            \
+    for (int i = 0; i < n; ++i)                                                \
+      {                                                                        \
+	dest1[i] = -(src1[i] * src2[i]) - dest1[i];                            \
+	dest2[i] = src1[i] * dest1[i] - dest2[i];                              \
+	dest3[i] = src2[i] * dest2[i] - dest3[i];                              \
+      }                                                                        \
+  }
+
+#define TEST_ALL()                                                             \
+  TEST_TYPE (_Float16)                                                         \
+  TEST_TYPE (float)                                                            \
+  TEST_TYPE (double)
+
+TEST_ALL ()
+
+/* { dg-final { scan-assembler-times {\tvfnmacc\.vv} 3 } } */
+/* { dg-final { scan-assembler-not {\tvmv} } } */
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-12.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-12.c
new file mode 100644
index 0000000..8ec261b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-12.c
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-march=rv32gcv_zvfh -mabi=ilp32d --param=riscv-autovec-preference=scalable -ffast-math" } */
+
+#include <stdint-gcc.h>
+
+#define TEST_TYPE(TYPE)                                                        \
+  __attribute__ ((noipa)) void ternop_##TYPE (TYPE *__restrict dest1,          \
+					      TYPE *__restrict dest2,          \
+					      TYPE *__restrict dest3,          \
+					      TYPE *__restrict src1,           \
+					      TYPE *__restrict src2, int n)    \
+  {                                                                            \
+    for (int i = 0; i < n; ++i)                                                \
+      {                                                                        \
+	dest1[i] = -(src1[i] * src2[i]) - dest2[i];                            \
+	dest2[i] = src1[i] * dest1[i] - dest2[i];                              \
+	dest3[i] = src2[i] * dest2[i] - dest3[i];                              \
+      }                                                                        \
+  }
+
+#define TEST_ALL()                                                             \
+  TEST_TYPE (_Float16)                                                         \
+  TEST_TYPE (float)                                                            \
+  TEST_TYPE (double)
+
+TEST_ALL ()
+
+/* { dg-final { scan-assembler-times {\tvmv} 3 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-2.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-2.c
index e52e07d..ad2673a 100644
--- a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-2.c
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-2.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-additional-options "-march=rv32gcv -mabi=ilp32d --param=riscv-autovec-preference=scalable -fno-schedule-insns" } */
+/* { dg-additional-options "-march=rv32gcv_zvfh -mabi=ilp32d --param=riscv-autovec-preference=scalable -fno-schedule-insns -ffast-math" } */
 
 #include <stdint-gcc.h>
 
@@ -26,9 +26,13 @@
   TEST_TYPE (int32_t)                                                          \
   TEST_TYPE (uint32_t)                                                         \
   TEST_TYPE (int64_t)                                                          \
-  TEST_TYPE (uint64_t)
+  TEST_TYPE (uint64_t)                                                         \
+  TEST_TYPE (_Float16)                                                         \
+  TEST_TYPE (float)                                                            \
+  TEST_TYPE (double)
 
 TEST_ALL ()
 
 /* { dg-final { scan-assembler-times {\tvmacc\.vv} 8 } } */
+/* { dg-final { scan-assembler-times {\tvfmacc\.vv} 3 } } */
 /* { dg-final { scan-assembler-not {\tvmv} } } */
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-3.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-3.c
index 127e701..cd97f4d 100644
--- a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-3.c
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-3.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-additional-options "-march=rv32gcv -mabi=ilp32d --param=riscv-autovec-preference=scalable" } */
+/* { dg-additional-options "-march=rv32gcv_zvfh -mabi=ilp32d --param=riscv-autovec-preference=scalable -ffast-math" } */
 
 #include <stdint-gcc.h>
 
@@ -26,8 +26,11 @@
   TEST_TYPE (int32_t)                                                          \
   TEST_TYPE (uint32_t)                                                         \
   TEST_TYPE (int64_t)                                                          \
-  TEST_TYPE (uint64_t)
+  TEST_TYPE (uint64_t)                                                         \
+  TEST_TYPE (_Float16)                                                         \
+  TEST_TYPE (float)                                                            \
+  TEST_TYPE (double)
 
 TEST_ALL ()
 
-/* { dg-final { scan-assembler-times {\tvmv} 8 } } */
+/* { dg-final { scan-assembler-times {\tvmv} 11 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-4.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-4.c
index 1b8b934..a225ea0 100644
--- a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-4.c
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-4.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-additional-options "-march=rv32gcv -mabi=ilp32d --param=riscv-autovec-preference=scalable" } */
+/* { dg-additional-options "-march=rv32gcv_zvfh -mabi=ilp32d --param=riscv-autovec-preference=scalable -ffast-math" } */
 
 #include <stdint-gcc.h>
 
@@ -20,9 +20,13 @@
   TEST_TYPE (int32_t)                                                          \
   TEST_TYPE (uint32_t)                                                         \
   TEST_TYPE (int64_t)                                                          \
-  TEST_TYPE (uint64_t)
+  TEST_TYPE (uint64_t)                                                         \
+  TEST_TYPE (_Float16)                                                         \
+  TEST_TYPE (float)                                                            \
+  TEST_TYPE (double)
 
 TEST_ALL ()
 
 /* { dg-final { scan-assembler-times {\tvnmsub\.vv} 8 } } */
+/* { dg-final { scan-assembler-times {\tvfnmsub\.vv} 3 } } */
 /* { dg-final { scan-assembler-not {\tvmv} } } */
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-5.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-5.c
index 49c85efb..12dfa0a 100644
--- a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-5.c
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-5.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-additional-options "-march=rv32gcv -mabi=ilp32d --param=riscv-autovec-preference=scalable -fno-schedule-insns" } */
+/* { dg-additional-options "-march=rv32gcv_zvfh -mabi=ilp32d --param=riscv-autovec-preference=scalable -fno-schedule-insns -ffast-math" } */
 
 #include <stdint-gcc.h>
 
@@ -26,9 +26,13 @@
   TEST_TYPE (int32_t)                                                          \
   TEST_TYPE (uint32_t)                                                         \
   TEST_TYPE (int64_t)                                                          \
-  TEST_TYPE (uint64_t)
+  TEST_TYPE (uint64_t)                                                         \
+  TEST_TYPE (_Float16)                                                         \
+  TEST_TYPE (float)                                                            \
+  TEST_TYPE (double)
 
 TEST_ALL ()
 
 /* { dg-final { scan-assembler-times {\tvnmsac\.vv} 8 } } */
+/* { dg-final { scan-assembler-times {\tvfnmsac\.vv} 3 } } */
 /* { dg-final { scan-assembler-not {\tvmv} } } */
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-6.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-6.c
index f38f303..b83590f 100644
--- a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-6.c
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-6.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-additional-options "-march=rv32gcv -mabi=ilp32d --param=riscv-autovec-preference=scalable" } */
+/* { dg-additional-options "-march=rv32gcv_zvfh -mabi=ilp32d --param=riscv-autovec-preference=scalable -ffast-math" } */
 
 #include <stdint-gcc.h>
 
@@ -26,8 +26,11 @@
   TEST_TYPE (int32_t)                                                          \
   TEST_TYPE (uint32_t)                                                         \
   TEST_TYPE (int64_t)                                                          \
-  TEST_TYPE (uint64_t)
+  TEST_TYPE (uint64_t)                                                         \
+  TEST_TYPE (_Float16)                                                         \
+  TEST_TYPE (float)                                                            \
+  TEST_TYPE (double)
 
 TEST_ALL ()
 
-/* { dg-final { scan-assembler-times {\tvmv} 8 } } */
+/* { dg-final { scan-assembler-times {\tvmv} 11 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-7.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-7.c
new file mode 100644
index 0000000..0f80da4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-7.c
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-march=rv32gcv_zvfh -mabi=ilp32d --param=riscv-autovec-preference=scalable -ffast-math" } */
+
+#include <stdint-gcc.h>
+
+#define TEST_TYPE(TYPE)                                                        \
+  __attribute__ ((noipa)) void ternop_##TYPE (TYPE *__restrict dst,            \
+					      TYPE *__restrict a,              \
+					      TYPE *__restrict b, int n)       \
+  {                                                                            \
+    for (int i = 0; i < n; i++)                                                \
+      dst[i] = a[i] * b[i] - dst[i];                                           \
+  }
+
+#define TEST_ALL()                                                             \
+  TEST_TYPE (_Float16)                                                         \
+  TEST_TYPE (float)                                                            \
+  TEST_TYPE (double)
+
+TEST_ALL ()
+
+/* { dg-final { scan-assembler-times {\tvfmsub\.vv} 3 } } */
+/* { dg-final { scan-assembler-not {\tvmv} } } */
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-8.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-8.c
new file mode 100644
index 0000000..ae65298
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-8.c
@@ -0,0 +1,29 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-march=rv32gcv_zvfh -mabi=ilp32d --param=riscv-autovec-preference=scalable -fno-schedule-insns -ffast-math" } */
+
+#include <stdint-gcc.h>
+
+#define TEST_TYPE(TYPE)                                                        \
+  __attribute__ ((noipa)) void ternop_##TYPE (TYPE *__restrict dest1,          \
+					      TYPE *__restrict dest2,          \
+					      TYPE *__restrict dest3,          \
+					      TYPE *__restrict src1,           \
+					      TYPE *__restrict src2, int n)    \
+  {                                                                            \
+    for (int i = 0; i < n; ++i)                                                \
+      {                                                                        \
+	dest1[i] = src1[i] * src2[i] - dest1[i];                               \
+	dest2[i] = src1[i] * dest1[i] - dest2[i];                              \
+	dest3[i] = src2[i] * dest2[i] - dest3[i];                              \
+      }                                                                        \
+  }
+
+#define TEST_ALL()                                                             \
+  TEST_TYPE (_Float16)                                                         \
+  TEST_TYPE (float)                                                            \
+  TEST_TYPE (double)
+
+TEST_ALL ()
+
+/* { dg-final { scan-assembler-times {\tvfmsac\.vv} 3 } } */
+/* { dg-final { scan-assembler-not {\tvmv} } } */
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-9.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-9.c
new file mode 100644
index 0000000..299bd2d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop-9.c
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-march=rv32gcv_zvfh -mabi=ilp32d --param=riscv-autovec-preference=scalable -ffast-math" } */
+
+#include <stdint-gcc.h>
+
+#define TEST_TYPE(TYPE)                                                        \
+  __attribute__ ((noipa)) void ternop_##TYPE (TYPE *__restrict dest1,          \
+					      TYPE *__restrict dest2,          \
+					      TYPE *__restrict dest3,          \
+					      TYPE *__restrict src1,           \
+					      TYPE *__restrict src2, int n)    \
+  {                                                                            \
+    for (int i = 0; i < n; ++i)                                                \
+      {                                                                        \
+	dest1[i] = src1[i] * src2[i] - dest2[i];                               \
+	dest2[i] = src1[i] * dest1[i] - dest2[i];                              \
+	dest3[i] = src2[i] * dest2[i] - dest3[i];                              \
+      }                                                                        \
+  }
+
+#define TEST_ALL()                                                             \
+  TEST_TYPE (_Float16)                                                         \
+  TEST_TYPE (float)                                                            \
+  TEST_TYPE (double)
+
+TEST_ALL ()
+
+/* { dg-final { scan-assembler-times {\tvmv} 3 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-1.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-1.c
index 1f69b69..e0ec9ed 100644
--- a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-1.c
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-1.c
@@ -1,5 +1,5 @@
 /* { dg-do run { target { riscv_vector } } } */
-/* { dg-additional-options "--param=riscv-autovec-preference=scalable" } */
+/* { dg-additional-options "--param=riscv-autovec-preference=scalable -ffast-math" } */
 
 #include "ternop-1.c"
 
@@ -80,5 +80,15 @@
   TEST_LOOP (int64_t, 795)
   TEST_LOOP (uint64_t, 795)
 
+  TEST_LOOP (float, 7)
+  TEST_LOOP (double, 7)
+  TEST_LOOP (float, 16)
+  TEST_LOOP (double, 16)
+  TEST_LOOP (float, 77)
+  TEST_LOOP (double, 77)
+  TEST_LOOP (float, 128)
+  TEST_LOOP (double, 128)
+  TEST_LOOP (float, 795)
+  TEST_LOOP (double, 795)
   return 0;
 }
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-10.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-10.c
new file mode 100644
index 0000000..854827f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-10.c
@@ -0,0 +1,40 @@
+/* { dg-do run { target { riscv_vector } } } */
+/* { dg-additional-options "--param=riscv-autovec-preference=scalable -ffast-math" } */
+
+#include "ternop-10.c"
+
+#define TEST_LOOP(TYPE, NUM)                                                   \
+  {                                                                            \
+    TYPE array1_##NUM[NUM] = {};                                               \
+    TYPE array2_##NUM[NUM] = {};                                               \
+    TYPE array3_##NUM[NUM] = {};                                               \
+    TYPE array4_##NUM[NUM] = {};                                               \
+    for (int i = 0; i < NUM; ++i)                                              \
+      {                                                                        \
+ array1_##NUM[i] = (i & 1) + 5;                                         \
+ array2_##NUM[i] = i - NUM / 3;                                         \
+ array3_##NUM[i] = NUM - NUM / 3 - i;                                   \
+ array4_##NUM[i] = NUM - NUM / 3 - i;                                   \
+ asm volatile("" ::: "memory");                                         \
+      }                                                                        \
+    ternop_##TYPE (array3_##NUM, array1_##NUM, array2_##NUM, NUM);             \
+    for (int i = 0; i < NUM; i++)                                              \
+      if (array3_##NUM[i]                                                      \
+   != (TYPE) (-(array1_##NUM[i] * array2_##NUM[i]) - array4_##NUM[i]))  \
+ __builtin_abort ();                                                    \
+  }
+
+int __attribute__ ((optimize (0))) main ()
+{
+  TEST_LOOP (float, 7)
+  TEST_LOOP (double, 7)
+  TEST_LOOP (float, 16)
+  TEST_LOOP (double, 16)
+  TEST_LOOP (float, 77)
+  TEST_LOOP (double, 77)
+  TEST_LOOP (float, 128)
+  TEST_LOOP (double, 128)
+  TEST_LOOP (float, 795)
+  TEST_LOOP (double, 795)
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-11.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-11.c
new file mode 100644
index 0000000..b5a0845
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-11.c
@@ -0,0 +1,60 @@
+/* { dg-do run { target { riscv_vector } } } */
+/* { dg-additional-options "--param=riscv-autovec-preference=scalable -ffast-math" } */
+
+#include "ternop-11.c"
+
+#define TEST_LOOP(TYPE, NUM)                                                   \
+  {                                                                            \
+    TYPE array1_##NUM[NUM] = {};                                               \
+    TYPE array2_##NUM[NUM] = {};                                               \
+    TYPE array3_##NUM[NUM] = {};                                               \
+    TYPE array4_##NUM[NUM] = {};                                               \
+    TYPE array5_##NUM[NUM] = {};                                               \
+    TYPE array6_##NUM[NUM] = {};                                               \
+    TYPE array7_##NUM[NUM] = {};                                               \
+    TYPE array8_##NUM[NUM] = {};                                               \
+    for (int i = 0; i < NUM; ++i)                                              \
+      {                                                                        \
+ array1_##NUM[i] = (i & 1) + 5;                                         \
+ array2_##NUM[i] = i - NUM / 3;                                         \
+ array3_##NUM[i] = NUM - NUM / 3 - i;                                   \
+ array6_##NUM[i] = NUM - NUM / 3 - i;                                   \
+ array4_##NUM[i] = NUM - NUM / 2 + i;                                   \
+ array7_##NUM[i] = NUM - NUM / 2 + i;                                   \
+ array5_##NUM[i] = NUM + i * 7;                                         \
+ array8_##NUM[i] = NUM + i * 7;                                         \
+ asm volatile("" ::: "memory");                                         \
+      }                                                                        \
+    ternop_##TYPE (array3_##NUM, array4_##NUM, array5_##NUM, array1_##NUM,     \
+    array2_##NUM, NUM);                                         \
+    for (int i = 0; i < NUM; i++)                                              \
+      {                                                                        \
+ array6_##NUM[i]                                                        \
+   = (TYPE) (-(array1_##NUM[i] * array2_##NUM[i]) - array6_##NUM[i]);   \
+ if (array3_##NUM[i] != array6_##NUM[i])                                \
+   __builtin_abort ();                                                  \
+ array7_##NUM[i]                                                        \
+   = (TYPE) (array1_##NUM[i] * array6_##NUM[i] - array7_##NUM[i]);      \
+ if (array4_##NUM[i] != array7_##NUM[i])                                \
+   __builtin_abort ();                                                  \
+ array8_##NUM[i]                                                        \
+   = (TYPE) (array2_##NUM[i] * array7_##NUM[i] - array8_##NUM[i]);      \
+ if (array5_##NUM[i] != array8_##NUM[i])                                \
+   __builtin_abort ();                                                  \
+      }                                                                        \
+  }
+
+int __attribute__ ((optimize (0))) main ()
+{
+  TEST_LOOP (float, 7)
+  TEST_LOOP (double, 7)
+  TEST_LOOP (float, 16)
+  TEST_LOOP (double, 16)
+  TEST_LOOP (float, 77)
+  TEST_LOOP (double, 77)
+  TEST_LOOP (float, 128)
+  TEST_LOOP (double, 128)
+  TEST_LOOP (float, 795)
+  TEST_LOOP (double, 795)
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-12.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-12.c
new file mode 100644
index 0000000..c7c4b4b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-12.c
@@ -0,0 +1,60 @@
+/* { dg-do run { target { riscv_vector } } } */
+/* { dg-additional-options "--param=riscv-autovec-preference=scalable -ffast-math" } */
+
+#include "ternop-12.c"
+
+#define TEST_LOOP(TYPE, NUM)                                                   \
+  {                                                                            \
+    TYPE array1_##NUM[NUM] = {};                                               \
+    TYPE array2_##NUM[NUM] = {};                                               \
+    TYPE array3_##NUM[NUM] = {};                                               \
+    TYPE array4_##NUM[NUM] = {};                                               \
+    TYPE array5_##NUM[NUM] = {};                                               \
+    TYPE array6_##NUM[NUM] = {};                                               \
+    TYPE array7_##NUM[NUM] = {};                                               \
+    TYPE array8_##NUM[NUM] = {};                                               \
+    for (int i = 0; i < NUM; ++i)                                              \
+      {                                                                        \
+ array1_##NUM[i] = (i & 1) + 5;                                         \
+ array2_##NUM[i] = i - NUM / 3;                                         \
+ array3_##NUM[i] = NUM - NUM / 3 - i;                                   \
+ array6_##NUM[i] = NUM - NUM / 3 - i;                                   \
+ array4_##NUM[i] = NUM - NUM / 2 + i;                                   \
+ array7_##NUM[i] = NUM - NUM / 2 + i;                                   \
+ array5_##NUM[i] = NUM + i * 7;                                         \
+ array8_##NUM[i] = NUM + i * 7;                                         \
+ asm volatile("" ::: "memory");                                         \
+      }                                                                        \
+    ternop_##TYPE (array3_##NUM, array4_##NUM, array5_##NUM, array1_##NUM,     \
+    array2_##NUM, NUM);                                         \
+    for (int i = 0; i < NUM; i++)                                              \
+      {                                                                        \
+ array6_##NUM[i]                                                        \
+   = (TYPE) (-(array1_##NUM[i] * array2_##NUM[i]) - array7_##NUM[i]);   \
+ if (array3_##NUM[i] != array6_##NUM[i])                                \
+   __builtin_abort ();                                                  \
+ array7_##NUM[i]                                                        \
+   = (TYPE) (array1_##NUM[i] * array6_##NUM[i] - array7_##NUM[i]);      \
+ if (array4_##NUM[i] != array7_##NUM[i])                                \
+   __builtin_abort ();                                                  \
+ array8_##NUM[i]                                                        \
+   = (TYPE) (array2_##NUM[i] * array7_##NUM[i] - array8_##NUM[i]);      \
+ if (array5_##NUM[i] != array8_##NUM[i])                                \
+   __builtin_abort ();                                                  \
+      }                                                                        \
+  }
+
+int __attribute__ ((optimize (0))) main ()
+{
+  TEST_LOOP (float, 7)
+  TEST_LOOP (double, 7)
+  TEST_LOOP (float, 16)
+  TEST_LOOP (double, 16)
+  TEST_LOOP (float, 77)
+  TEST_LOOP (double, 77)
+  TEST_LOOP (float, 128)
+  TEST_LOOP (double, 128)
+  TEST_LOOP (float, 795)
+  TEST_LOOP (double, 795)
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-2.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-2.c
index 103b98a..ee7c725 100644
--- a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-2.c
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-2.c
@@ -1,5 +1,5 @@
 /* { dg-do run { target { riscv_vector } } } */
-/* { dg-additional-options "--param=riscv-autovec-preference=scalable" } */
+/* { dg-additional-options "--param=riscv-autovec-preference=scalable -ffast-math" } */
 
 #include "ternop-2.c"
 
@@ -100,5 +100,15 @@
   TEST_LOOP (int64_t, 795)
   TEST_LOOP (uint64_t, 795)
 
+  TEST_LOOP (float, 7)
+  TEST_LOOP (double, 7)
+  TEST_LOOP (float, 16)
+  TEST_LOOP (double, 16)
+  TEST_LOOP (float, 77)
+  TEST_LOOP (double, 77)
+  TEST_LOOP (float, 128)
+  TEST_LOOP (double, 128)
+  TEST_LOOP (float, 795)
+  TEST_LOOP (double, 795)
   return 0;
 }
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-3.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-3.c
index eac5408..6c4f28e 100644
--- a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-3.c
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-3.c
@@ -1,5 +1,5 @@
 /* { dg-do run { target { riscv_vector } } } */
-/* { dg-additional-options "--param=riscv-autovec-preference=scalable" } */
+/* { dg-additional-options "--param=riscv-autovec-preference=scalable -ffast-math" } */
 
 #include "ternop-3.c"
 
@@ -100,5 +100,15 @@
   TEST_LOOP (int64_t, 795)
   TEST_LOOP (uint64_t, 795)
 
+  TEST_LOOP (float, 7)
+  TEST_LOOP (double, 7)
+  TEST_LOOP (float, 16)
+  TEST_LOOP (double, 16)
+  TEST_LOOP (float, 77)
+  TEST_LOOP (double, 77)
+  TEST_LOOP (float, 128)
+  TEST_LOOP (double, 128)
+  TEST_LOOP (float, 795)
+  TEST_LOOP (double, 795)
   return 0;
 }
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-4.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-4.c
index c6f1fe5..44a4771 100644
--- a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-4.c
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-4.c
@@ -1,5 +1,5 @@
 /* { dg-do run { target { riscv_vector } } } */
-/* { dg-additional-options "--param=riscv-autovec-preference=scalable" } */
+/* { dg-additional-options "--param=riscv-autovec-preference=scalable -ffast-math" } */
 
 #include "ternop-4.c"
 
@@ -80,5 +80,15 @@
   TEST_LOOP (int64_t, 795)
   TEST_LOOP (uint64_t, 795)
 
+  TEST_LOOP (float, 7)
+  TEST_LOOP (double, 7)
+  TEST_LOOP (float, 16)
+  TEST_LOOP (double, 16)
+  TEST_LOOP (float, 77)
+  TEST_LOOP (double, 77)
+  TEST_LOOP (float, 128)
+  TEST_LOOP (double, 128)
+  TEST_LOOP (float, 795)
+  TEST_LOOP (double, 795)
   return 0;
 }
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-5.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-5.c
index 81af4b6..efe2f36 100644
--- a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-5.c
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-5.c
@@ -1,5 +1,5 @@
 /* { dg-do run { target { riscv_vector } } } */
-/* { dg-additional-options "--param=riscv-autovec-preference=scalable" } */
+/* { dg-additional-options "--param=riscv-autovec-preference=scalable -ffast-math" } */
 
 #include "ternop-5.c"
 
@@ -100,5 +100,15 @@
   TEST_LOOP (int64_t, 795)
   TEST_LOOP (uint64_t, 795)
 
+  TEST_LOOP (float, 7)
+  TEST_LOOP (double, 7)
+  TEST_LOOP (float, 16)
+  TEST_LOOP (double, 16)
+  TEST_LOOP (float, 77)
+  TEST_LOOP (double, 77)
+  TEST_LOOP (float, 128)
+  TEST_LOOP (double, 128)
+  TEST_LOOP (float, 795)
+  TEST_LOOP (double, 795)
   return 0;
 }
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-6.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-6.c
index b5e579e..f1ce6a7 100644
--- a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-6.c
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-6.c
@@ -1,5 +1,5 @@
 /* { dg-do run { target { riscv_vector } } } */
-/* { dg-additional-options "--param=riscv-autovec-preference=scalable" } */
+/* { dg-additional-options "--param=riscv-autovec-preference=scalable -ffast-math" } */
 
 #include "ternop-6.c"
 
@@ -100,5 +100,15 @@
   TEST_LOOP (int64_t, 795)
   TEST_LOOP (uint64_t, 795)
 
+  TEST_LOOP (float, 7)
+  TEST_LOOP (double, 7)
+  TEST_LOOP (float, 16)
+  TEST_LOOP (double, 16)
+  TEST_LOOP (float, 77)
+  TEST_LOOP (double, 77)
+  TEST_LOOP (float, 128)
+  TEST_LOOP (double, 128)
+  TEST_LOOP (float, 795)
+  TEST_LOOP (double, 795)
   return 0;
 }
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-7.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-7.c
new file mode 100644
index 0000000..1809b23
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-7.c
@@ -0,0 +1,40 @@
+/* { dg-do run { target { riscv_vector } } } */
+/* { dg-additional-options "--param=riscv-autovec-preference=scalable -ffast-math" } */
+
+#include "ternop-7.c"
+
+#define TEST_LOOP(TYPE, NUM)                                                   \
+  {                                                                            \
+    TYPE array1_##NUM[NUM] = {};                                               \
+    TYPE array2_##NUM[NUM] = {};                                               \
+    TYPE array3_##NUM[NUM] = {};                                               \
+    TYPE array4_##NUM[NUM] = {};                                               \
+    for (int i = 0; i < NUM; ++i)                                              \
+      {                                                                        \
+	array1_##NUM[i] = (i & 1) + 5;                                         \
+	array2_##NUM[i] = i - NUM / 3;                                         \
+	array3_##NUM[i] = NUM - NUM / 3 - i;                                   \
+	array4_##NUM[i] = NUM - NUM / 3 - i;                                   \
+	asm volatile("" ::: "memory");                                         \
+      }                                                                        \
+    ternop_##TYPE (array3_##NUM, array1_##NUM, array2_##NUM, NUM);             \
+    for (int i = 0; i < NUM; i++)                                              \
+      if (array3_##NUM[i]                                                      \
+	  != (TYPE) (array1_##NUM[i] * array2_##NUM[i] - array4_##NUM[i]))     \
+	__builtin_abort ();                                                    \
+  }
+
+int __attribute__ ((optimize (0))) main ()
+{
+  TEST_LOOP (float, 7)
+  TEST_LOOP (double, 7)
+  TEST_LOOP (float, 16)
+  TEST_LOOP (double, 16)
+  TEST_LOOP (float, 77)
+  TEST_LOOP (double, 77)
+  TEST_LOOP (float, 128)
+  TEST_LOOP (double, 128)
+  TEST_LOOP (float, 795)
+  TEST_LOOP (double, 795)
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-8.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-8.c
new file mode 100644
index 0000000..f048652
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-8.c
@@ -0,0 +1,60 @@
+/* { dg-do run { target { riscv_vector } } } */
+/* { dg-additional-options "--param=riscv-autovec-preference=scalable -ffast-math" } */
+
+#include "ternop-8.c"
+
+#define TEST_LOOP(TYPE, NUM)                                                   \
+  {                                                                            \
+    TYPE array1_##NUM[NUM] = {};                                               \
+    TYPE array2_##NUM[NUM] = {};                                               \
+    TYPE array3_##NUM[NUM] = {};                                               \
+    TYPE array4_##NUM[NUM] = {};                                               \
+    TYPE array5_##NUM[NUM] = {};                                               \
+    TYPE array6_##NUM[NUM] = {};                                               \
+    TYPE array7_##NUM[NUM] = {};                                               \
+    TYPE array8_##NUM[NUM] = {};                                               \
+    for (int i = 0; i < NUM; ++i)                                              \
+      {                                                                        \
+	array1_##NUM[i] = (i & 1) + 5;                                         \
+	array2_##NUM[i] = i - NUM / 3;                                         \
+	array3_##NUM[i] = NUM - NUM / 3 - i;                                   \
+	array6_##NUM[i] = NUM - NUM / 3 - i;                                   \
+	array4_##NUM[i] = NUM - NUM / 2 + i;                                   \
+	array7_##NUM[i] = NUM - NUM / 2 + i;                                   \
+	array5_##NUM[i] = NUM + i * 7;                                         \
+	array8_##NUM[i] = NUM + i * 7;                                         \
+	asm volatile("" ::: "memory");                                         \
+      }                                                                        \
+    ternop_##TYPE (array3_##NUM, array4_##NUM, array5_##NUM, array1_##NUM,     \
+		   array2_##NUM, NUM);                                         \
+    for (int i = 0; i < NUM; i++)                                              \
+      {                                                                        \
+	array6_##NUM[i]                                                        \
+	  = (TYPE) (array1_##NUM[i] * array2_##NUM[i] - array6_##NUM[i]);      \
+	if (array3_##NUM[i] != array6_##NUM[i])                                \
+	  __builtin_abort ();                                                  \
+	array7_##NUM[i]                                                        \
+	  = (TYPE) (array1_##NUM[i] * array6_##NUM[i] - array7_##NUM[i]);      \
+	if (array4_##NUM[i] != array7_##NUM[i])                                \
+	  __builtin_abort ();                                                  \
+	array8_##NUM[i]                                                        \
+	  = (TYPE) (array2_##NUM[i] * array7_##NUM[i] - array8_##NUM[i]);      \
+	if (array5_##NUM[i] != array8_##NUM[i])                                \
+	  __builtin_abort ();                                                  \
+      }                                                                        \
+  }
+
+int __attribute__ ((optimize (0))) main ()
+{
+  TEST_LOOP (float, 7)
+  TEST_LOOP (double, 7)
+  TEST_LOOP (float, 16)
+  TEST_LOOP (double, 16)
+  TEST_LOOP (float, 77)
+  TEST_LOOP (double, 77)
+  TEST_LOOP (float, 128)
+  TEST_LOOP (double, 128)
+  TEST_LOOP (float, 795)
+  TEST_LOOP (double, 795)
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-9.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-9.c
new file mode 100644
index 0000000..dcf87f6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run-9.c
@@ -0,0 +1,60 @@
+/* { dg-do run { target { riscv_vector } } } */
+/* { dg-additional-options "--param=riscv-autovec-preference=scalable -ffast-math" } */
+
+#include "ternop-9.c"
+
+#define TEST_LOOP(TYPE, NUM)                                                   \
+  {                                                                            \
+    TYPE array1_##NUM[NUM] = {};                                               \
+    TYPE array2_##NUM[NUM] = {};                                               \
+    TYPE array3_##NUM[NUM] = {};                                               \
+    TYPE array4_##NUM[NUM] = {};                                               \
+    TYPE array5_##NUM[NUM] = {};                                               \
+    TYPE array6_##NUM[NUM] = {};                                               \
+    TYPE array7_##NUM[NUM] = {};                                               \
+    TYPE array8_##NUM[NUM] = {};                                               \
+    for (int i = 0; i < NUM; ++i)                                              \
+      {                                                                        \
+	array1_##NUM[i] = (i & 1) + 5;                                         \
+	array2_##NUM[i] = i - NUM / 3;                                         \
+	array3_##NUM[i] = NUM - NUM / 3 - i;                                   \
+	array6_##NUM[i] = NUM - NUM / 3 - i;                                   \
+	array4_##NUM[i] = NUM - NUM / 2 + i;                                   \
+	array7_##NUM[i] = NUM - NUM / 2 + i;                                   \
+	array5_##NUM[i] = NUM + i * 7;                                         \
+	array8_##NUM[i] = NUM + i * 7;                                         \
+	asm volatile("" ::: "memory");                                         \
+      }                                                                        \
+    ternop_##TYPE (array3_##NUM, array4_##NUM, array5_##NUM, array1_##NUM,     \
+		   array2_##NUM, NUM);                                         \
+    for (int i = 0; i < NUM; i++)                                              \
+      {                                                                        \
+	array6_##NUM[i]                                                        \
+	  = (TYPE) (array1_##NUM[i] * array2_##NUM[i] - array7_##NUM[i]);      \
+	if (array3_##NUM[i] != array6_##NUM[i])                                \
+	  __builtin_abort ();                                                  \
+	array7_##NUM[i]                                                        \
+	  = (TYPE) (array1_##NUM[i] * array6_##NUM[i] - array7_##NUM[i]);      \
+	if (array4_##NUM[i] != array7_##NUM[i])                                \
+	  __builtin_abort ();                                                  \
+	array8_##NUM[i]                                                        \
+	  = (TYPE) (array2_##NUM[i] * array7_##NUM[i] - array8_##NUM[i]);      \
+	if (array5_##NUM[i] != array8_##NUM[i])                                \
+	  __builtin_abort ();                                                  \
+      }                                                                        \
+  }
+
+int __attribute__ ((optimize (0))) main ()
+{
+  TEST_LOOP (float, 7)
+  TEST_LOOP (double, 7)
+  TEST_LOOP (float, 16)
+  TEST_LOOP (double, 16)
+  TEST_LOOP (float, 77)
+  TEST_LOOP (double, 77)
+  TEST_LOOP (float, 128)
+  TEST_LOOP (double, 128)
+  TEST_LOOP (float, 795)
+  TEST_LOOP (double, 795)
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-1.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-1.c
new file mode 100644
index 0000000..84fcb68
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-1.c
@@ -0,0 +1,35 @@
+/* { dg-do run { target { riscv_vector && riscv_zvfh_hw } } } */
+/* { dg-additional-options "--param=riscv-autovec-preference=scalable -ffast-math" } */
+
+#include "ternop-1.c"
+
+#define TEST_LOOP(TYPE, NUM)                                                   \
+  {                                                                            \
+    TYPE array1_##NUM[NUM] = {};                                               \
+    TYPE array2_##NUM[NUM] = {};                                               \
+    TYPE array3_##NUM[NUM] = {};                                               \
+    TYPE array4_##NUM[NUM] = {};                                               \
+    for (int i = 0; i < NUM; ++i)                                              \
+      {                                                                        \
+	array1_##NUM[i] = (i & 1) + 5;                                         \
+	array2_##NUM[i] = i - NUM / 3;                                         \
+	array3_##NUM[i] = NUM - NUM / 3 - i;                                   \
+	array4_##NUM[i] = NUM - NUM / 3 - i;                                   \
+	asm volatile("" ::: "memory");                                         \
+      }                                                                        \
+    ternop_##TYPE (array3_##NUM, array1_##NUM, array2_##NUM, NUM);             \
+    for (int i = 0; i < NUM; i++)                                              \
+      if (array3_##NUM[i]                                                      \
+	  != (TYPE) (array1_##NUM[i] * array2_##NUM[i] + array4_##NUM[i]))     \
+	__builtin_abort ();                                                    \
+  }
+
+int __attribute__ ((optimize (0))) main ()
+{
+  TEST_LOOP (_Float16, 7)
+  TEST_LOOP (_Float16, 16)
+  TEST_LOOP (_Float16, 77)
+  TEST_LOOP (_Float16, 128)
+  TEST_LOOP (_Float16, 795)
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-10.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-10.c
new file mode 100644
index 0000000..d669cd4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-10.c
@@ -0,0 +1,35 @@
+/* { dg-do run { target { riscv_vector && riscv_zvfh_hw } } } */
+/* { dg-additional-options "--param=riscv-autovec-preference=scalable -ffast-math" } */
+
+#include "ternop-10.c"
+
+#define TEST_LOOP(TYPE, NUM)                                                   \
+  {                                                                            \
+    TYPE array1_##NUM[NUM] = {};                                               \
+    TYPE array2_##NUM[NUM] = {};                                               \
+    TYPE array3_##NUM[NUM] = {};                                               \
+    TYPE array4_##NUM[NUM] = {};                                               \
+    for (int i = 0; i < NUM; ++i)                                              \
+      {                                                                        \
+ array1_##NUM[i] = (i & 1) + 5;                                         \
+ array2_##NUM[i] = i - NUM / 3;                                         \
+ array3_##NUM[i] = NUM - NUM / 3 - i;                                   \
+ array4_##NUM[i] = NUM - NUM / 3 - i;                                   \
+ asm volatile("" ::: "memory");                                         \
+      }                                                                        \
+    ternop_##TYPE (array3_##NUM, array1_##NUM, array2_##NUM, NUM);             \
+    for (int i = 0; i < NUM; i++)                                              \
+      if (array3_##NUM[i]                                                      \
+   != (TYPE) (-(array1_##NUM[i] * array2_##NUM[i]) - array4_##NUM[i]))  \
+ __builtin_abort ();                                                    \
+  }
+
+int __attribute__ ((optimize (0))) main ()
+{
+  TEST_LOOP (_Float16, 7)
+  TEST_LOOP (_Float16, 16)
+  TEST_LOOP (_Float16, 77)
+  TEST_LOOP (_Float16, 128)
+  TEST_LOOP (_Float16, 795)
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-11.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-11.c
new file mode 100644
index 0000000..fac17b6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-11.c
@@ -0,0 +1,55 @@
+/* { dg-do run { target { riscv_vector && riscv_zvfh_hw } } } */
+/* { dg-additional-options "--param=riscv-autovec-preference=scalable -ffast-math" } */
+
+#include "ternop-11.c"
+
+#define TEST_LOOP(TYPE, NUM)                                                   \
+  {                                                                            \
+    TYPE array1_##NUM[NUM] = {};                                               \
+    TYPE array2_##NUM[NUM] = {};                                               \
+    TYPE array3_##NUM[NUM] = {};                                               \
+    TYPE array4_##NUM[NUM] = {};                                               \
+    TYPE array5_##NUM[NUM] = {};                                               \
+    TYPE array6_##NUM[NUM] = {};                                               \
+    TYPE array7_##NUM[NUM] = {};                                               \
+    TYPE array8_##NUM[NUM] = {};                                               \
+    for (int i = 0; i < NUM; ++i)                                              \
+      {                                                                        \
+ array1_##NUM[i] = (i & 1) + 5;                                         \
+ array2_##NUM[i] = i - NUM / 3;                                         \
+ array3_##NUM[i] = NUM - NUM / 3 - i;                                   \
+ array6_##NUM[i] = NUM - NUM / 3 - i;                                   \
+ array4_##NUM[i] = NUM - NUM / 2 + i;                                   \
+ array7_##NUM[i] = NUM - NUM / 2 + i;                                   \
+ array5_##NUM[i] = NUM + i * 7;                                         \
+ array8_##NUM[i] = NUM + i * 7;                                         \
+ asm volatile("" ::: "memory");                                         \
+      }                                                                        \
+    ternop_##TYPE (array3_##NUM, array4_##NUM, array5_##NUM, array1_##NUM,     \
+    array2_##NUM, NUM);                                         \
+    for (int i = 0; i < NUM; i++)                                              \
+      {                                                                        \
+ array6_##NUM[i]                                                        \
+   = (TYPE) (-(array1_##NUM[i] * array2_##NUM[i]) - array6_##NUM[i]);   \
+ if (array3_##NUM[i] != array6_##NUM[i])                                \
+   __builtin_abort ();                                                  \
+ array7_##NUM[i]                                                        \
+   = (TYPE) (array1_##NUM[i] * array6_##NUM[i] - array7_##NUM[i]);      \
+ if (array4_##NUM[i] != array7_##NUM[i])                                \
+   __builtin_abort ();                                                  \
+ array8_##NUM[i]                                                        \
+   = (TYPE) (array2_##NUM[i] * array7_##NUM[i] - array8_##NUM[i]);      \
+ if (array5_##NUM[i] != array8_##NUM[i])                                \
+   __builtin_abort ();                                                  \
+      }                                                                        \
+  }
+
+int __attribute__ ((optimize (0))) main ()
+{
+  TEST_LOOP (_Float16, 7)
+  TEST_LOOP (_Float16, 16)
+  TEST_LOOP (_Float16, 77)
+  TEST_LOOP (_Float16, 128)
+  TEST_LOOP (_Float16, 795)
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-12.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-12.c
new file mode 100644
index 0000000..a51b926
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-12.c
@@ -0,0 +1,55 @@
+/* { dg-do run { target { riscv_vector && riscv_zvfh_hw } } } */
+/* { dg-additional-options "--param=riscv-autovec-preference=scalable -ffast-math" } */
+
+#include "ternop-12.c"
+
+#define TEST_LOOP(TYPE, NUM)                                                   \
+  {                                                                            \
+    TYPE array1_##NUM[NUM] = {};                                               \
+    TYPE array2_##NUM[NUM] = {};                                               \
+    TYPE array3_##NUM[NUM] = {};                                               \
+    TYPE array4_##NUM[NUM] = {};                                               \
+    TYPE array5_##NUM[NUM] = {};                                               \
+    TYPE array6_##NUM[NUM] = {};                                               \
+    TYPE array7_##NUM[NUM] = {};                                               \
+    TYPE array8_##NUM[NUM] = {};                                               \
+    for (int i = 0; i < NUM; ++i)                                              \
+      {                                                                        \
+ array1_##NUM[i] = (i & 1) + 5;                                         \
+ array2_##NUM[i] = i - NUM / 3;                                         \
+ array3_##NUM[i] = NUM - NUM / 3 - i;                                   \
+ array6_##NUM[i] = NUM - NUM / 3 - i;                                   \
+ array4_##NUM[i] = NUM - NUM / 2 + i;                                   \
+ array7_##NUM[i] = NUM - NUM / 2 + i;                                   \
+ array5_##NUM[i] = NUM + i * 7;                                         \
+ array8_##NUM[i] = NUM + i * 7;                                         \
+ asm volatile("" ::: "memory");                                         \
+      }                                                                        \
+    ternop_##TYPE (array3_##NUM, array4_##NUM, array5_##NUM, array1_##NUM,     \
+    array2_##NUM, NUM);                                         \
+    for (int i = 0; i < NUM; i++)                                              \
+      {                                                                        \
+ array6_##NUM[i]                                                        \
+   = (TYPE) (-(array1_##NUM[i] * array2_##NUM[i]) - array7_##NUM[i]);   \
+ if (array3_##NUM[i] != array6_##NUM[i])                                \
+   __builtin_abort ();                                                  \
+ array7_##NUM[i]                                                        \
+   = (TYPE) (array1_##NUM[i] * array6_##NUM[i] - array7_##NUM[i]);      \
+ if (array4_##NUM[i] != array7_##NUM[i])                                \
+   __builtin_abort ();                                                  \
+ array8_##NUM[i]                                                        \
+   = (TYPE) (array2_##NUM[i] * array7_##NUM[i] - array8_##NUM[i]);      \
+ if (array5_##NUM[i] != array8_##NUM[i])                                \
+   __builtin_abort ();                                                  \
+      }                                                                        \
+  }
+
+int __attribute__ ((optimize (0))) main ()
+{
+  TEST_LOOP (_Float16, 7)
+  TEST_LOOP (_Float16, 16)
+  TEST_LOOP (_Float16, 77)
+  TEST_LOOP (_Float16, 128)
+  TEST_LOOP (_Float16, 795)
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-2.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-2.c
new file mode 100644
index 0000000..8fc6a1b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-2.c
@@ -0,0 +1,55 @@
+/* { dg-do run { target { riscv_vector && riscv_zvfh_hw } } } */
+/* { dg-additional-options "--param=riscv-autovec-preference=scalable -ffast-math" } */
+
+#include "ternop-2.c"
+
+#define TEST_LOOP(TYPE, NUM)                                                   \
+  {                                                                            \
+    TYPE array1_##NUM[NUM] = {};                                               \
+    TYPE array2_##NUM[NUM] = {};                                               \
+    TYPE array3_##NUM[NUM] = {};                                               \
+    TYPE array4_##NUM[NUM] = {};                                               \
+    TYPE array5_##NUM[NUM] = {};                                               \
+    TYPE array6_##NUM[NUM] = {};                                               \
+    TYPE array7_##NUM[NUM] = {};                                               \
+    TYPE array8_##NUM[NUM] = {};                                               \
+    for (int i = 0; i < NUM; ++i)                                              \
+      {                                                                        \
+	array1_##NUM[i] = (i & 1) + 5;                                         \
+	array2_##NUM[i] = i - NUM / 3;                                         \
+	array3_##NUM[i] = NUM - NUM / 3 - i;                                   \
+	array6_##NUM[i] = NUM - NUM / 3 - i;                                   \
+	array4_##NUM[i] = NUM - NUM / 2 + i;                                   \
+	array7_##NUM[i] = NUM - NUM / 2 + i;                                   \
+	array5_##NUM[i] = NUM + i * 7;                                         \
+	array8_##NUM[i] = NUM + i * 7;                                         \
+	asm volatile("" ::: "memory");                                         \
+      }                                                                        \
+    ternop_##TYPE (array3_##NUM, array4_##NUM, array5_##NUM, array1_##NUM,     \
+		   array2_##NUM, NUM);                                         \
+    for (int i = 0; i < NUM; i++)                                              \
+      {                                                                        \
+	array6_##NUM[i]                                                        \
+	  = (TYPE) (array1_##NUM[i] * array2_##NUM[i] + array6_##NUM[i]);      \
+	if (array3_##NUM[i] != array6_##NUM[i])                                \
+	  __builtin_abort ();                                                  \
+	array7_##NUM[i]                                                        \
+	  = (TYPE) (array1_##NUM[i] * array6_##NUM[i] + array7_##NUM[i]);      \
+	if (array4_##NUM[i] != array7_##NUM[i])                                \
+	  __builtin_abort ();                                                  \
+	array8_##NUM[i]                                                        \
+	  = (TYPE) (array2_##NUM[i] * array7_##NUM[i] + array8_##NUM[i]);      \
+	if (array5_##NUM[i] != array8_##NUM[i])                                \
+	  __builtin_abort ();                                                  \
+      }                                                                        \
+  }
+
+int __attribute__ ((optimize (0))) main ()
+{
+  TEST_LOOP (_Float16, 7)
+  TEST_LOOP (_Float16, 16)
+  TEST_LOOP (_Float16, 77)
+  TEST_LOOP (_Float16, 128)
+  TEST_LOOP (_Float16, 795)
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-3.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-3.c
new file mode 100644
index 0000000..3601307
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-3.c
@@ -0,0 +1,55 @@
+/* { dg-do run { target { riscv_vector && riscv_zvfh_hw } } } */
+/* { dg-additional-options "--param=riscv-autovec-preference=scalable -ffast-math" } */
+
+#include "ternop-3.c"
+
+#define TEST_LOOP(TYPE, NUM)                                                   \
+  {                                                                            \
+    TYPE array1_##NUM[NUM] = {};                                               \
+    TYPE array2_##NUM[NUM] = {};                                               \
+    TYPE array3_##NUM[NUM] = {};                                               \
+    TYPE array4_##NUM[NUM] = {};                                               \
+    TYPE array5_##NUM[NUM] = {};                                               \
+    TYPE array6_##NUM[NUM] = {};                                               \
+    TYPE array7_##NUM[NUM] = {};                                               \
+    TYPE array8_##NUM[NUM] = {};                                               \
+    for (int i = 0; i < NUM; ++i)                                              \
+      {                                                                        \
+	array1_##NUM[i] = (i & 1) + 5;                                         \
+	array2_##NUM[i] = i - NUM / 3;                                         \
+	array3_##NUM[i] = NUM - NUM / 3 - i;                                   \
+	array6_##NUM[i] = NUM - NUM / 3 - i;                                   \
+	array4_##NUM[i] = NUM - NUM / 2 + i;                                   \
+	array7_##NUM[i] = NUM - NUM / 2 + i;                                   \
+	array5_##NUM[i] = NUM + i * 7;                                         \
+	array8_##NUM[i] = NUM + i * 7;                                         \
+	asm volatile("" ::: "memory");                                         \
+      }                                                                        \
+    ternop_##TYPE (array3_##NUM, array4_##NUM, array5_##NUM, array1_##NUM,     \
+		   array2_##NUM, NUM);                                         \
+    for (int i = 0; i < NUM; i++)                                              \
+      {                                                                        \
+	array6_##NUM[i]                                                        \
+	  = (TYPE) (array1_##NUM[i] * array2_##NUM[i] + array7_##NUM[i]);      \
+	if (array3_##NUM[i] != array6_##NUM[i])                                \
+	  __builtin_abort ();                                                  \
+	array7_##NUM[i]                                                        \
+	  = (TYPE) (array1_##NUM[i] * array6_##NUM[i] + array7_##NUM[i]);      \
+	if (array4_##NUM[i] != array7_##NUM[i])                                \
+	  __builtin_abort ();                                                  \
+	array8_##NUM[i]                                                        \
+	  = (TYPE) (array2_##NUM[i] * array7_##NUM[i] + array8_##NUM[i]);      \
+	if (array5_##NUM[i] != array8_##NUM[i])                                \
+	  __builtin_abort ();                                                  \
+      }                                                                        \
+  }
+
+int __attribute__ ((optimize (0))) main ()
+{
+  TEST_LOOP (_Float16, 7)
+  TEST_LOOP (_Float16, 16)
+  TEST_LOOP (_Float16, 77)
+  TEST_LOOP (_Float16, 128)
+  TEST_LOOP (_Float16, 795)
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-4.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-4.c
new file mode 100644
index 0000000..a26bcaa
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-4.c
@@ -0,0 +1,35 @@
+/* { dg-do run { target { riscv_vector && riscv_zvfh_hw } } } */
+/* { dg-additional-options "--param=riscv-autovec-preference=scalable -ffast-math" } */
+
+#include "ternop-4.c"
+
+#define TEST_LOOP(TYPE, NUM)                                                   \
+  {                                                                            \
+    TYPE array1_##NUM[NUM] = {};                                               \
+    TYPE array2_##NUM[NUM] = {};                                               \
+    TYPE array3_##NUM[NUM] = {};                                               \
+    TYPE array4_##NUM[NUM] = {};                                               \
+    for (int i = 0; i < NUM; ++i)                                              \
+      {                                                                        \
+ array1_##NUM[i] = (i & 1) + 5;                                         \
+ array2_##NUM[i] = i - NUM / 3;                                         \
+ array3_##NUM[i] = NUM - NUM / 3 - i;                                   \
+ array4_##NUM[i] = NUM - NUM / 3 - i;                                   \
+ asm volatile("" ::: "memory");                                         \
+      }                                                                        \
+    ternop_##TYPE (array3_##NUM, array1_##NUM, array2_##NUM, NUM);             \
+    for (int i = 0; i < NUM; i++)                                              \
+      if (array3_##NUM[i]                                                      \
+   != (TYPE) (-(array1_##NUM[i] * array2_##NUM[i]) + array4_##NUM[i]))  \
+ __builtin_abort ();                                                    \
+  }
+
+int __attribute__ ((optimize (0))) main ()
+{
+  TEST_LOOP (_Float16, 7)
+  TEST_LOOP (_Float16, 16)
+  TEST_LOOP (_Float16, 77)
+  TEST_LOOP (_Float16, 128)
+  TEST_LOOP (_Float16, 795)
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-5.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-5.c
new file mode 100644
index 0000000..6dee6ba
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-5.c
@@ -0,0 +1,55 @@
+/* { dg-do run { target { riscv_vector && riscv_zvfh_hw } } } */
+/* { dg-additional-options "--param=riscv-autovec-preference=scalable -ffast-math" } */
+
+#include "ternop-5.c"
+
+#define TEST_LOOP(TYPE, NUM)                                                   \
+  {                                                                            \
+    TYPE array1_##NUM[NUM] = {};                                               \
+    TYPE array2_##NUM[NUM] = {};                                               \
+    TYPE array3_##NUM[NUM] = {};                                               \
+    TYPE array4_##NUM[NUM] = {};                                               \
+    TYPE array5_##NUM[NUM] = {};                                               \
+    TYPE array6_##NUM[NUM] = {};                                               \
+    TYPE array7_##NUM[NUM] = {};                                               \
+    TYPE array8_##NUM[NUM] = {};                                               \
+    for (int i = 0; i < NUM; ++i)                                              \
+      {                                                                        \
+ array1_##NUM[i] = (i & 1) + 5;                                         \
+ array2_##NUM[i] = i - NUM / 3;                                         \
+ array3_##NUM[i] = NUM - NUM / 3 - i;                                   \
+ array6_##NUM[i] = NUM - NUM / 3 - i;                                   \
+ array4_##NUM[i] = NUM - NUM / 2 + i;                                   \
+ array7_##NUM[i] = NUM - NUM / 2 + i;                                   \
+ array5_##NUM[i] = NUM + i * 7;                                         \
+ array8_##NUM[i] = NUM + i * 7;                                         \
+ asm volatile("" ::: "memory");                                         \
+      }                                                                        \
+    ternop_##TYPE (array3_##NUM, array4_##NUM, array5_##NUM, array1_##NUM,     \
+    array2_##NUM, NUM);                                         \
+    for (int i = 0; i < NUM; i++)                                              \
+      {                                                                        \
+ array6_##NUM[i]                                                        \
+   = (TYPE) (-(array1_##NUM[i] * array2_##NUM[i]) + array6_##NUM[i]);   \
+ if (array3_##NUM[i] != array6_##NUM[i])                                \
+   __builtin_abort ();                                                  \
+ array7_##NUM[i]                                                        \
+   = (TYPE) (array1_##NUM[i] * array6_##NUM[i] + array7_##NUM[i]);      \
+ if (array4_##NUM[i] != array7_##NUM[i])                                \
+   __builtin_abort ();                                                  \
+ array8_##NUM[i]                                                        \
+   = (TYPE) (array2_##NUM[i] * array7_##NUM[i] + array8_##NUM[i]);      \
+ if (array5_##NUM[i] != array8_##NUM[i])                                \
+   __builtin_abort ();                                                  \
+      }                                                                        \
+  }
+
+int __attribute__ ((optimize (0))) main ()
+{
+  TEST_LOOP (_Float16, 7)
+  TEST_LOOP (_Float16, 16)
+  TEST_LOOP (_Float16, 77)
+  TEST_LOOP (_Float16, 128)
+  TEST_LOOP (_Float16, 795)
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-6.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-6.c
new file mode 100644
index 0000000..3fdf2d3
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-6.c
@@ -0,0 +1,55 @@
+/* { dg-do run { target { riscv_vector && riscv_zvfh_hw } } } */
+/* { dg-additional-options "--param=riscv-autovec-preference=scalable -ffast-math" } */
+
+#include "ternop-6.c"
+
+#define TEST_LOOP(TYPE, NUM)                                                   \
+  {                                                                            \
+    TYPE array1_##NUM[NUM] = {};                                               \
+    TYPE array2_##NUM[NUM] = {};                                               \
+    TYPE array3_##NUM[NUM] = {};                                               \
+    TYPE array4_##NUM[NUM] = {};                                               \
+    TYPE array5_##NUM[NUM] = {};                                               \
+    TYPE array6_##NUM[NUM] = {};                                               \
+    TYPE array7_##NUM[NUM] = {};                                               \
+    TYPE array8_##NUM[NUM] = {};                                               \
+    for (int i = 0; i < NUM; ++i)                                              \
+      {                                                                        \
+ array1_##NUM[i] = (i & 1) + 5;                                         \
+ array2_##NUM[i] = i - NUM / 3;                                         \
+ array3_##NUM[i] = NUM - NUM / 3 - i;                                   \
+ array6_##NUM[i] = NUM - NUM / 3 - i;                                   \
+ array4_##NUM[i] = NUM - NUM / 2 + i;                                   \
+ array7_##NUM[i] = NUM - NUM / 2 + i;                                   \
+ array5_##NUM[i] = NUM + i * 7;                                         \
+ array8_##NUM[i] = NUM + i * 7;                                         \
+ asm volatile("" ::: "memory");                                         \
+      }                                                                        \
+    ternop_##TYPE (array3_##NUM, array4_##NUM, array5_##NUM, array1_##NUM,     \
+    array2_##NUM, NUM);                                         \
+    for (int i = 0; i < NUM; i++)                                              \
+      {                                                                        \
+ array6_##NUM[i]                                                        \
+   = (TYPE) (-(array1_##NUM[i] * array2_##NUM[i]) + array7_##NUM[i]);   \
+ if (array3_##NUM[i] != array6_##NUM[i])                                \
+   __builtin_abort ();                                                  \
+ array7_##NUM[i]                                                        \
+   = (TYPE) (array1_##NUM[i] * array6_##NUM[i] + array7_##NUM[i]);      \
+ if (array4_##NUM[i] != array7_##NUM[i])                                \
+   __builtin_abort ();                                                  \
+ array8_##NUM[i]                                                        \
+   = (TYPE) (array2_##NUM[i] * array7_##NUM[i] + array8_##NUM[i]);      \
+ if (array5_##NUM[i] != array8_##NUM[i])                                \
+   __builtin_abort ();                                                  \
+      }                                                                        \
+  }
+
+int __attribute__ ((optimize (0))) main ()
+{
+  TEST_LOOP (_Float16, 7)
+  TEST_LOOP (_Float16, 16)
+  TEST_LOOP (_Float16, 77)
+  TEST_LOOP (_Float16, 128)
+  TEST_LOOP (_Float16, 795)
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-7.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-7.c
new file mode 100644
index 0000000..a25a6f7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-7.c
@@ -0,0 +1,35 @@
+/* { dg-do run { target { riscv_vector && riscv_zvfh_hw } } } */
+/* { dg-additional-options "--param=riscv-autovec-preference=scalable -ffast-math" } */
+
+#include "ternop-7.c"
+
+#define TEST_LOOP(TYPE, NUM)                                                   \
+  {                                                                            \
+    TYPE array1_##NUM[NUM] = {};                                               \
+    TYPE array2_##NUM[NUM] = {};                                               \
+    TYPE array3_##NUM[NUM] = {};                                               \
+    TYPE array4_##NUM[NUM] = {};                                               \
+    for (int i = 0; i < NUM; ++i)                                              \
+      {                                                                        \
+	array1_##NUM[i] = (i & 1) + 5;                                         \
+	array2_##NUM[i] = i - NUM / 3;                                         \
+	array3_##NUM[i] = NUM - NUM / 3 - i;                                   \
+	array4_##NUM[i] = NUM - NUM / 3 - i;                                   \
+	asm volatile("" ::: "memory");                                         \
+      }                                                                        \
+    ternop_##TYPE (array3_##NUM, array1_##NUM, array2_##NUM, NUM);             \
+    for (int i = 0; i < NUM; i++)                                              \
+      if (array3_##NUM[i]                                                      \
+	  != (TYPE) (array1_##NUM[i] * array2_##NUM[i] - array4_##NUM[i]))     \
+	__builtin_abort ();                                                    \
+  }
+
+int __attribute__ ((optimize (0))) main ()
+{
+  TEST_LOOP (_Float16, 7)
+  TEST_LOOP (_Float16, 16)
+  TEST_LOOP (_Float16, 77)
+  TEST_LOOP (_Float16, 128)
+  TEST_LOOP (_Float16, 795)
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-8.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-8.c
new file mode 100644
index 0000000..1d90bee
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-8.c
@@ -0,0 +1,55 @@
+/* { dg-do run { target { riscv_vector && riscv_zvfh_hw } } } */
+/* { dg-additional-options "--param=riscv-autovec-preference=scalable -ffast-math" } */
+
+#include "ternop-8.c"
+
+#define TEST_LOOP(TYPE, NUM)                                                   \
+  {                                                                            \
+    TYPE array1_##NUM[NUM] = {};                                               \
+    TYPE array2_##NUM[NUM] = {};                                               \
+    TYPE array3_##NUM[NUM] = {};                                               \
+    TYPE array4_##NUM[NUM] = {};                                               \
+    TYPE array5_##NUM[NUM] = {};                                               \
+    TYPE array6_##NUM[NUM] = {};                                               \
+    TYPE array7_##NUM[NUM] = {};                                               \
+    TYPE array8_##NUM[NUM] = {};                                               \
+    for (int i = 0; i < NUM; ++i)                                              \
+      {                                                                        \
+	array1_##NUM[i] = (i & 1) + 5;                                         \
+	array2_##NUM[i] = i - NUM / 3;                                         \
+	array3_##NUM[i] = NUM - NUM / 3 - i;                                   \
+	array6_##NUM[i] = NUM - NUM / 3 - i;                                   \
+	array4_##NUM[i] = NUM - NUM / 2 + i;                                   \
+	array7_##NUM[i] = NUM - NUM / 2 + i;                                   \
+	array5_##NUM[i] = NUM + i * 7;                                         \
+	array8_##NUM[i] = NUM + i * 7;                                         \
+	asm volatile("" ::: "memory");                                         \
+      }                                                                        \
+    ternop_##TYPE (array3_##NUM, array4_##NUM, array5_##NUM, array1_##NUM,     \
+		   array2_##NUM, NUM);                                         \
+    for (int i = 0; i < NUM; i++)                                              \
+      {                                                                        \
+	array6_##NUM[i]                                                        \
+	  = (TYPE) (array1_##NUM[i] * array2_##NUM[i] - array6_##NUM[i]);      \
+	if (array3_##NUM[i] != array6_##NUM[i])                                \
+	  __builtin_abort ();                                                  \
+	array7_##NUM[i]                                                        \
+	  = (TYPE) (array1_##NUM[i] * array6_##NUM[i] - array7_##NUM[i]);      \
+	if (array4_##NUM[i] != array7_##NUM[i])                                \
+	  __builtin_abort ();                                                  \
+	array8_##NUM[i]                                                        \
+	  = (TYPE) (array2_##NUM[i] * array7_##NUM[i] - array8_##NUM[i]);      \
+	if (array5_##NUM[i] != array8_##NUM[i])                                \
+	  __builtin_abort ();                                                  \
+      }                                                                        \
+  }
+
+int __attribute__ ((optimize (0))) main ()
+{
+  TEST_LOOP (_Float16, 7)
+  TEST_LOOP (_Float16, 16)
+  TEST_LOOP (_Float16, 77)
+  TEST_LOOP (_Float16, 128)
+  TEST_LOOP (_Float16, 795)
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-9.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-9.c
new file mode 100644
index 0000000..c633f54
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/ternop/ternop_run_zvfh-9.c
@@ -0,0 +1,55 @@
+/* { dg-do run { target { riscv_vector && riscv_zvfh_hw } } } */
+/* { dg-additional-options "--param=riscv-autovec-preference=scalable -ffast-math" } */
+
+#include "ternop-9.c"
+
+#define TEST_LOOP(TYPE, NUM)                                                   \
+  {                                                                            \
+    TYPE array1_##NUM[NUM] = {};                                               \
+    TYPE array2_##NUM[NUM] = {};                                               \
+    TYPE array3_##NUM[NUM] = {};                                               \
+    TYPE array4_##NUM[NUM] = {};                                               \
+    TYPE array5_##NUM[NUM] = {};                                               \
+    TYPE array6_##NUM[NUM] = {};                                               \
+    TYPE array7_##NUM[NUM] = {};                                               \
+    TYPE array8_##NUM[NUM] = {};                                               \
+    for (int i = 0; i < NUM; ++i)                                              \
+      {                                                                        \
+	array1_##NUM[i] = (i & 1) + 5;                                         \
+	array2_##NUM[i] = i - NUM / 3;                                         \
+	array3_##NUM[i] = NUM - NUM / 3 - i;                                   \
+	array6_##NUM[i] = NUM - NUM / 3 - i;                                   \
+	array4_##NUM[i] = NUM - NUM / 2 + i;                                   \
+	array7_##NUM[i] = NUM - NUM / 2 + i;                                   \
+	array5_##NUM[i] = NUM + i * 7;                                         \
+	array8_##NUM[i] = NUM + i * 7;                                         \
+	asm volatile("" ::: "memory");                                         \
+      }                                                                        \
+    ternop_##TYPE (array3_##NUM, array4_##NUM, array5_##NUM, array1_##NUM,     \
+		   array2_##NUM, NUM);                                         \
+    for (int i = 0; i < NUM; i++)                                              \
+      {                                                                        \
+	array6_##NUM[i]                                                        \
+	  = (TYPE) (array1_##NUM[i] * array2_##NUM[i] - array7_##NUM[i]);      \
+	if (array3_##NUM[i] != array6_##NUM[i])                                \
+	  __builtin_abort ();                                                  \
+	array7_##NUM[i]                                                        \
+	  = (TYPE) (array1_##NUM[i] * array6_##NUM[i] - array7_##NUM[i]);      \
+	if (array4_##NUM[i] != array7_##NUM[i])                                \
+	  __builtin_abort ();                                                  \
+	array8_##NUM[i]                                                        \
+	  = (TYPE) (array2_##NUM[i] * array7_##NUM[i] - array8_##NUM[i]);      \
+	if (array5_##NUM[i] != array8_##NUM[i])                                \
+	  __builtin_abort ();                                                  \
+      }                                                                        \
+  }
+
+int __attribute__ ((optimize (0))) main ()
+{
+  TEST_LOOP (_Float16, 7)
+  TEST_LOOP (_Float16, 16)
+  TEST_LOOP (_Float16, 77)
+  TEST_LOOP (_Float16, 128)
+  TEST_LOOP (_Float16, 795)
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/base/tuple-28.c b/gcc/testsuite/gcc.target/riscv/rvv/base/tuple-28.c
deleted file mode 100644
index f36129e..0000000
--- a/gcc/testsuite/gcc.target/riscv/rvv/base/tuple-28.c
+++ /dev/null
@@ -1,59 +0,0 @@
-/* { dg-do compile } */
-/* { dg-options "-O3 -march=rv32gcv_zvfh -mabi=ilp32d" } */
-
-#include "riscv_vector.h"
-
-void
-f_vfloat16mf4x2_t (void *base, void *out)
-{
-  vfloat16mf4x2_t v = *(vfloat16mf4x2_t*)base;
-  *(vfloat16mf4x2_t*)out = v;
-}
-
-void
-f_vfloat16mf4x3_t (void *base, void *out)
-{
-  vfloat16mf4x3_t v = *(vfloat16mf4x3_t*)base;
-  *(vfloat16mf4x3_t*)out = v;
-}
-
-void
-f_vfloat16mf4x4_t (void *base, void *out)
-{
-  vfloat16mf4x4_t v = *(vfloat16mf4x4_t*)base;
-  *(vfloat16mf4x4_t*)out = v;
-}
-
-void
-f_vfloat16mf4x5_t (void *base, void *out)
-{
-  vfloat16mf4x5_t v = *(vfloat16mf4x5_t*)base;
-  *(vfloat16mf4x5_t*)out = v;
-}
-
-void
-f_vfloat16mf4x6_t (void *base, void *out)
-{
-  vfloat16mf4x6_t v = *(vfloat16mf4x6_t*)base;
-  *(vfloat16mf4x6_t*)out = v;
-}
-
-void
-f_vfloat16mf4x7_t (void *base, void *out)
-{
-  vfloat16mf4x7_t v = *(vfloat16mf4x7_t*)base;
-  *(vfloat16mf4x7_t*)out = v;
-}
-
-void
-f_vfloat16mf4x8_t (void *base, void *out)
-{
-  vfloat16mf4x8_t v = *(vfloat16mf4x8_t*)base;
-  *(vfloat16mf4x8_t*)out = v;
-}
-
-/* { dg-final { scan-assembler-times {vsetvli\s+[a-x0-9]+,\s*zero,\s*e16,\s*mf4,\s*t[au],\s*m[au]} 7 } } */
-/* { dg-final { scan-assembler {srai} } } */
-/* { dg-final { scan-assembler-not {slli} } } */
-/* { dg-final { scan-assembler-times {vle16\.v\tv[0-9]+,0\([a-x0-9]+\)} 35 } } */
-/* { dg-final { scan-assembler-times {vse16\.v\tv[0-9]+,0\([a-x0-9]+\)} 35 } } */
\ No newline at end of file
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/base/tuple-29.c b/gcc/testsuite/gcc.target/riscv/rvv/base/tuple-29.c
deleted file mode 100644
index c6807c1..0000000
--- a/gcc/testsuite/gcc.target/riscv/rvv/base/tuple-29.c
+++ /dev/null
@@ -1,59 +0,0 @@
-/* { dg-do compile } */
-/* { dg-options "-O3 -march=rv32gcv_zvfh -mabi=ilp32d" } */
-
-#include "riscv_vector.h"
-
-void
-f_vfloat16mf2x2_t (void *base, void *out)
-{
-  vfloat16mf2x2_t v = *(vfloat16mf2x2_t*)base;
-  *(vfloat16mf2x2_t*)out = v;
-}
-
-void
-f_vfloat16mf2x3_t (void *base, void *out)
-{
-  vfloat16mf2x3_t v = *(vfloat16mf2x3_t*)base;
-  *(vfloat16mf2x3_t*)out = v;
-}
-
-void
-f_vfloat16mf2x4_t (void *base, void *out)
-{
-  vfloat16mf2x4_t v = *(vfloat16mf2x4_t*)base;
-  *(vfloat16mf2x4_t*)out = v;
-}
-
-void
-f_vfloat16mf2x5_t (void *base, void *out)
-{
-  vfloat16mf2x5_t v = *(vfloat16mf2x5_t*)base;
-  *(vfloat16mf2x5_t*)out = v;
-}
-
-void
-f_vfloat16mf2x6_t (void *base, void *out)
-{
-  vfloat16mf2x6_t v = *(vfloat16mf2x6_t*)base;
-  *(vfloat16mf2x6_t*)out = v;
-}
-
-void
-f_vfloat16mf2x7_t (void *base, void *out)
-{
-  vfloat16mf2x7_t v = *(vfloat16mf2x7_t*)base;
-  *(vfloat16mf2x7_t*)out = v;
-}
-
-void
-f_vfloat16mf2x8_t (void *base, void *out)
-{
-  vfloat16mf2x8_t v = *(vfloat16mf2x8_t*)base;
-  *(vfloat16mf2x8_t*)out = v;
-}
-
-/* { dg-final { scan-assembler-times {vsetvli\s+[a-x0-9]+,\s*zero,\s*e16,\s*mf2,\s*t[au],\s*m[au]} 7 } } */
-/* { dg-final { scan-assembler {srai} } } */
-/* { dg-final { scan-assembler-not {slli} } } */
-/* { dg-final { scan-assembler-times {vle16\.v\tv[0-9]+,0\([a-x0-9]+\)} 35 } } */
-/* { dg-final { scan-assembler-times {vse16\.v\tv[0-9]+,0\([a-x0-9]+\)} 35 } } */
\ No newline at end of file
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/base/tuple-30.c b/gcc/testsuite/gcc.target/riscv/rvv/base/tuple-30.c
deleted file mode 100644
index dd4de3c..0000000
--- a/gcc/testsuite/gcc.target/riscv/rvv/base/tuple-30.c
+++ /dev/null
@@ -1,58 +0,0 @@
-/* { dg-do compile } */
-/* { dg-options "-O3 -march=rv32gcv_zvfh -mabi=ilp32d" } */
-
-#include "riscv_vector.h"
-
-void
-f_vfloat16m1x2_t (void *base, void *out)
-{
-  vfloat16m1x2_t v = *(vfloat16m1x2_t*)base;
-  *(vfloat16m1x2_t*)out = v;
-}
-
-void
-f_vfloat16m1x3_t (void *base, void *out)
-{
-  vfloat16m1x3_t v = *(vfloat16m1x3_t*)base;
-  *(vfloat16m1x3_t*)out = v;
-}
-
-void
-f_vfloat16m1x4_t (void *base, void *out)
-{
-  vfloat16m1x4_t v = *(vfloat16m1x4_t*)base;
-  *(vfloat16m1x4_t*)out = v;
-}
-
-void
-f_vfloat16m1x5_t (void *base, void *out)
-{
-  vfloat16m1x5_t v = *(vfloat16m1x5_t*)base;
-  *(vfloat16m1x5_t*)out = v;
-}
-
-void
-f_vfloat16m1x6_t (void *base, void *out)
-{
-  vfloat16m1x6_t v = *(vfloat16m1x6_t*)base;
-  *(vfloat16m1x6_t*)out = v;
-}
-
-void
-f_vfloat16m1x7_t (void *base, void *out)
-{
-  vfloat16m1x7_t v = *(vfloat16m1x7_t*)base;
-  *(vfloat16m1x7_t*)out = v;
-}
-
-void
-f_vfloat16m1x8_t (void *base, void *out)
-{
-  vfloat16m1x8_t v = *(vfloat16m1x8_t*)base;
-  *(vfloat16m1x8_t*)out = v;
-}
-
-/* { dg-final { scan-assembler-not {srai} } } */
-/* { dg-final { scan-assembler-not {slli} } } */
-/* { dg-final { scan-assembler-times {vl1re16\.v\tv[0-9]+,0\([a-x0-9]+\)} 35 } } */
-/* { dg-final { scan-assembler-times {vs1r\.v\tv[0-9]+,0\([a-x0-9]+\)} 35 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/base/tuple-31.c b/gcc/testsuite/gcc.target/riscv/rvv/base/tuple-31.c
deleted file mode 100644
index 48b084e..0000000
--- a/gcc/testsuite/gcc.target/riscv/rvv/base/tuple-31.c
+++ /dev/null
@@ -1,30 +0,0 @@
-/* { dg-do compile } */
-/* { dg-options "-O3 -march=rv32gcv_zvfh -mabi=ilp32d" } */
-
-#include "riscv_vector.h"
-
-void
-f_vfloat16m2x2_t (void *base, void *out)
-{
-  vfloat16m2x2_t v = *(vfloat16m2x2_t*)base;
-  *(vfloat16m2x2_t*)out = v;
-}
-
-void
-f_vfloat16m2x3_t (void *base, void *out)
-{
-  vfloat16m2x3_t v = *(vfloat16m2x3_t*)base;
-  *(vfloat16m2x3_t*)out = v;
-}
-
-void
-f_vfloat16m2x4_t (void *base, void *out)
-{
-  vfloat16m2x4_t v = *(vfloat16m2x4_t*)base;
-  *(vfloat16m2x4_t*)out = v;
-}
-
-/* { dg-final { scan-assembler-not {srai} } } */
-/* { dg-final { scan-assembler {slli} } } */
-/* { dg-final { scan-assembler-times {vl2re16\.v\tv[0-9]+,0\([a-x0-9]+\)} 9 } } */
-/* { dg-final { scan-assembler-times {vs2r\.v\tv[0-9]+,0\([a-x0-9]+\)} 9 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/base/tuple-32.c b/gcc/testsuite/gcc.target/riscv/rvv/base/tuple-32.c
deleted file mode 100644
index 90693d6..0000000
--- a/gcc/testsuite/gcc.target/riscv/rvv/base/tuple-32.c
+++ /dev/null
@@ -1,16 +0,0 @@
-/* { dg-do compile } */
-/* { dg-options "-O3 -march=rv32gcv_zvfh -mabi=ilp32d" } */
-
-#include "riscv_vector.h"
-
-void
-f_vfloat16m4x2_t (void *base, void *out)
-{
-  vfloat16m4x2_t v = *(vfloat16m4x2_t*)base;
-  *(vfloat16m4x2_t*)out = v;
-}
-
-/* { dg-final { scan-assembler-not {srai} } } */
-/* { dg-final { scan-assembler {slli} } } */
-/* { dg-final { scan-assembler-times {vl4re16\.v\tv[0-9]+,0\([a-x0-9]+\)} 2 } } */
-/* { dg-final { scan-assembler-times {vs4r\.v\tv[0-9]+,0\([a-x0-9]+\)} 2 } } */
\ No newline at end of file
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/base/vlmul_ext-2.c b/gcc/testsuite/gcc.target/riscv/rvv/base/vlmul_ext-2.c
new file mode 100644
index 0000000..3749d97
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/rvv/base/vlmul_ext-2.c
@@ -0,0 +1,8 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcv -mabi=lp64d -O0 -Wno-psabi" } */
+
+#include "riscv_vector.h"
+
+void test_vlmul_ext_v_i8mf8_i8mf4(vint8mf8_t op1) {
+  vint8mf4_t res = __riscv_vlmul_ext_v_i8mf8_i8mf4(op1);
+}
diff --git a/gcc/testsuite/gcc.target/s390/larl-1.c b/gcc/testsuite/gcc.target/s390/larl-1.c
new file mode 100644
index 0000000..5ef2ef6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/larl-1.c
@@ -0,0 +1,32 @@
+/* Check if load-address-relative instructions are created */
+
+/* { dg-do compile { target { s390*-*-* } } } */
+/* { dg-options "-O2 -march=z10 -mzarch -fno-section-anchors" } */
+
+/* An explicitely misaligned symbol.  This symbol is NOT aligned as
+   mandated by our ABI.  However, the back-end needs to handle that in
+   order to make things like __attribute__((packed)) work.  The symbol
+   address is expected to be loaded from literal pool.  */
+/* { dg-final { scan-assembler "lgrl\t%r2," { target { lp64 } } } } */
+/* { dg-final { scan-assembler "lrl\t%r2," { target { ! lp64 } } } } */
+extern char align1 __attribute__((aligned(1)));
+
+/* { dg-final { scan-assembler "larl\t%r2,align2" } } */
+extern char align2 __attribute__((aligned(2)));
+
+/* { dg-final { scan-assembler "larl\t%r2,align4" } } */
+extern char align4 __attribute__((aligned(4)));
+
+/* An external char symbol without explicit alignment has a DECL_ALIGN
+   of just 8. In contrast to local definitions DATA_ABI_ALIGNMENT is
+   NOT applied to DECL_ALIGN in that case.  Make sure the backend
+   still assumes this symbol to be aligned according to ABI
+   requirements.  */
+/* { dg-final { scan-assembler "larl\t%r2,align_default" } } */
+extern char align_default;
+
+char * foo1 () { return &align1; }
+char * foo2 () { return &align2; }
+char * foo3 () { return &align4; }
+char * foo4 () { return &align_default; }
+
diff --git a/gcc/testsuite/gdc.dg/pr110359.d b/gcc/testsuite/gdc.dg/pr110359.d
new file mode 100644
index 0000000..bf69201
--- /dev/null
+++ b/gcc/testsuite/gdc.dg/pr110359.d
@@ -0,0 +1,22 @@
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110359
+// { dg-do compile }
+// { dg-options "-fdump-tree-original" }
+double pow(in double x, in ulong p)
+{
+    import gcc.builtins : __builtin_expect;
+    if (__builtin_expect(p == 0, false))
+        return 1;
+    if (__builtin_expect(p == 1, false))
+        return x;
+
+    double s = x;
+    double v = 1;
+    for (ulong i = p; i > 1; i >>= 1)
+    {
+        v = (i & 0x1) ? s * v : v;
+        s = s * s;
+    }
+    return v * s;
+}
+// { dg-final { scan-tree-dump "if \\(__builtin_expect \\(p == 0, 0\\) != 0\\)" "original" } }
+// { dg-final { scan-tree-dump "if \\(__builtin_expect \\(p == 1, 0\\) != 0\\)" "original" } }
diff --git a/gcc/testsuite/gdc.test/compilable/shared.d b/gcc/testsuite/gdc.test/compilable/shared.d
index 695083a..647910e 100644
--- a/gcc/testsuite/gdc.test/compilable/shared.d
+++ b/gcc/testsuite/gdc.test/compilable/shared.d
@@ -11,34 +11,48 @@
 }
 
 // https://issues.dlang.org/show_bug.cgi?id=20908
+struct S
+{
+    int i = 2;
+}
+
+union U
+{
+    int i = 1;
+    bool b;
+}
+
 void test20908()
 {
-  // shared locals (or struct members) should be able to be initialised:
-  shared int x;
+    // shared locals (or struct members) should be able to be initialised:
+    shared int x;
 
-  ref shared(int) fun()
-  {
-    static shared(int) val;
+    ref shared(int) fun()
+    {
+        static shared(int) val;
 
-    // return by reference
-    return val;
-  }
+        // return by reference
+        return val;
+    }
 
-  ref shared(int) fun2()
-  {
-    static shared(int)* val;
+    ref shared(int) fun2()
+    {
+        static shared(int)* val;
 
-    // transfer pointer to reference
-    return *val;
-  }
+        // transfer pointer to reference
+        return *val;
+    }
 
-  ref shared(int) fun3()
-  {
-    static shared(int)*** val;
+    ref shared(int) fun3()
+    {
+        static shared(int)*** val;
 
-    // Multiple indirections
-    return ***val;
-  }
+        // Multiple indirections
+        return ***val;
+    }
+
+    shared S s;
+    shared U u;
 }
 
 // Simple tests for `DotVarExp`
@@ -130,3 +144,15 @@
 {
     auto b = new shared Class();
 }
+
+// https://issues.dlang.org/show_bug.cgi?id=23790
+bool cas(shared bool*, bool, bool) { return true; }
+
+struct Argh
+{
+    bool locked;
+    void lock() shared
+    {
+        while(!cas(&locked, false, true)) {}
+    }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test22739.d b/gcc/testsuite/gdc.test/compilable/test22739.d
new file mode 100644
index 0000000..6aeb5d6
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test22739.d
@@ -0,0 +1,10 @@
+// https://issues.dlang.org/show_bug.cgi?id=22739
+
+extern(C++) auto f(T)()
+{
+    return T.init;
+}
+void main()
+{
+    f!int;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test23799.d b/gcc/testsuite/gdc.test/compilable/test23799.d
new file mode 100644
index 0000000..0073516
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test23799.d
@@ -0,0 +1,37 @@
+// https://issues.dlang.org/show_bug.cgi?id=23799
+
+// REQUIRED_ARGS: -betterC
+
+struct Data
+{
+	Data[] range;
+	string test;
+}
+
+Data[] foo()
+{
+	Data[] ret;
+	if (__ctfe)
+	{
+		Data tmp;
+		tmp.range ~= Data.init;
+		ret ~= tmp;
+	}
+	return ret;
+}
+
+void func(Data dat)()
+{
+}
+
+void bar(Data dat)()
+{
+	if (dat.test.length)
+		func!(dat.range[0])();
+}
+
+extern (C) void main()
+{
+	static immutable data = foo();
+	bar!(data[0])();
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/bug9631.d b/gcc/testsuite/gdc.test/fail_compilation/bug9631.d
index f456454..c980d76 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/bug9631.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/bug9631.d
@@ -4,7 +4,7 @@
 fail_compilation/bug9631.d(20): Error: cannot implicitly convert expression `F()` of type `bug9631.T1!().F` to `bug9631.T2!().F`
 ---
 */
-
+// DISABLED: win32
 template T1()
 {
     struct F { }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/cerrors.d b/gcc/testsuite/gdc.test/fail_compilation/cerrors.d
index 3d69d41..db306c1 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/cerrors.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/cerrors.d
@@ -1,10 +1,12 @@
 /* REQUIRED_ARGS: -wi
 TEST_OUTPUT:
 ---
-fail_compilation/cerrors.d(11): Error: C preprocessor directive `#if` is not supported, use `version` or `static if`
-fail_compilation/cerrors.d(11): Error: declaration expected, not `#`
-fail_compilation/cerrors.d(15): Warning: C preprocessor directive `#endif` is not supported
-fail_compilation/cerrors.d(15): Error: declaration expected, not `#`
+fail_compilation/cerrors.d(13): Error: C preprocessor directive `#if` is not supported, use `version` or `static if`
+fail_compilation/cerrors.d(13): Error: declaration expected, not `#`
+fail_compilation/cerrors.d(17): Error: C preprocessor directive `#endif` is not supported
+fail_compilation/cerrors.d(17): Error: declaration expected, not `#`
+fail_compilation/cerrors.d(21): Error: token string requires valid D tokens, not `#if`
+fail_compilation/cerrors.d(22): Deprecation: token string requires valid D tokens, not `#include`
 ---
 */
 
@@ -13,3 +15,9 @@
 void test(wchar_t u);
 
 #endif
+
+// https://issues.dlang.org/show_bug.cgi?id=23792
+enum s1 = q{
+#if 1
+#include <test>
+};
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail17646.d b/gcc/testsuite/gdc.test/fail_compilation/fail17646.d
index 39e7cb9..2074b47 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail17646.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail17646.d
@@ -4,7 +4,7 @@
 TEST_OUTPUT:
 ---
 fail_compilation/imports/fail17646.d(10): Error: found `}` instead of statement
-fail_compilation/fail17646.d(11): Error: function `fail17646.runTests!"".runTests` has no `return` statement, but is expected to return a value of type `int`
+fail_compilation/fail17646.d(15): Error: template instance `allTestData!Modules` template `allTestData` is not defined
 fail_compilation/fail17646.d(18): Error: template instance `fail17646.runTests!""` error instantiating
 ---
 */
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19948.d b/gcc/testsuite/gdc.test/fail_compilation/fail19948.d
index 6122e41..e8a9e77 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail19948.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19948.d
@@ -7,7 +7,7 @@
 fail_compilation/fail19948.d(15):        cannot pass argument `X()` of type `fail19948.main.X` to parameter `const(fail19948.X)`
 ---
 */
-
+// DISABLED: win32
 struct X {}
 void main()
 {
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail22857.d b/gcc/testsuite/gdc.test/fail_compilation/fail22857.d
new file mode 100644
index 0000000..061eb62
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail22857.d
@@ -0,0 +1,18 @@
+// https://issues.dlang.org/show_bug.cgi?id=22857
+// EXTRA_FILES: imports/import22857.d
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/imports/import22857.d(4): Error: (expression) expected following `static if`
+fail_compilation/imports/import22857.d(4): Error: declaration expected, not `}`
+fail_compilation/fail22857.d(17): Error: template instance `unaryFun!()` template `unaryFun` is not defined
+---
+*/
+
+void isPrettyPropertyName()
+{
+    import imports.import22857;
+
+    unaryFun!();
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail23816.d b/gcc/testsuite/gdc.test/fail_compilation/fail23816.d
new file mode 100644
index 0000000..574a712
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail23816.d
@@ -0,0 +1,16 @@
+// https://issues.dlang.org/show_bug.cgi?id=23816
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail23816.d(14): Error: opcode expected, not `NOP`
+---
+*/
+
+void main()
+{
+    asm
+    {
+        NOP;
+    }
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/import22857.d b/gcc/testsuite/gdc.test/fail_compilation/imports/import22857.d
new file mode 100644
index 0000000..280c2eb
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/imports/import22857.d
@@ -0,0 +1,4 @@
+template unaryFun()
+{
+    static if
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/shared.d b/gcc/testsuite/gdc.test/fail_compilation/shared.d
index afdea64..8b94a79 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/shared.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/shared.d
@@ -259,3 +259,22 @@
     auto x1 = cast(int*)s;
     auto x2 = cast(const(shared(int*)))s;
 }
+
+#line 3100
+
+// https://issues.dlang.org/show_bug.cgi?id=23783
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/shared.d(3114): Error: direct access to shared `x` is not allowed, see `core.atomic`
+fail_compilation/shared.d(3115): Error: direct access to shared `x` is not allowed, see `core.atomic`
+---
+*/
+
+void test23783()
+{
+    shared int x = 3;
+    assert(x == 3);
+    bool b = x == 3;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test21164.d b/gcc/testsuite/gdc.test/fail_compilation/test21164.d
index f42c4bc..a120024 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test21164.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test21164.d
@@ -3,7 +3,8 @@
 ---
 fail_compilation/imports/test21164d.d(3): Error: (expression) expected following `static if`
 fail_compilation/imports/test21164d.d(3): Error: found `}` instead of statement
-fail_compilation/test21164.d(11): Error: template instance `test21164a.D!(R!(O(), 1))` error instantiating
+fail_compilation/imports/test21164a.d(5): Error: undefined identifier `I`
+fail_compilation/test21164.d(12): Error: template instance `test21164a.D!(R!(O(), 1))` error instantiating
 ---
 */
 import imports.test21164a;
diff --git a/gcc/testsuite/gdc.test/runnable/complex3.d b/gcc/testsuite/gdc.test/runnable/complex3.d
new file mode 100644
index 0000000..7167b0b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/complex3.d
@@ -0,0 +1,31 @@
+// https://issues.dlang.org/show_bug.cgi?id=23778
+
+
+enum __c_long_double : double;
+
+alias __c_long_double c_long_double;
+
+struct _Complex
+{
+    c_long_double re;
+    c_long_double im;
+}
+
+version (all) // bug to test
+{
+    enum __c_complex_real   : _Complex;
+    alias c_complex_real = __c_complex_real;
+}
+else // works
+    enum c_complex_real   : _Complex;
+
+c_complex_real toNative2(real re, real im)
+{
+    return c_complex_real(re, im);
+}
+
+void main()
+{
+    c_complex_real n = toNative2(123, 456);
+    assert(123 == n.re && 456 == n.im);
+}
diff --git a/gcc/testsuite/gfortran.dg/value_9.f90 b/gcc/testsuite/gfortran.dg/value_9.f90
new file mode 100644
index 0000000..f649064
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/value_9.f90
@@ -0,0 +1,78 @@
+! { dg-do run }
+! PR fortran/110360 - ABI for scalar character(len=1),value dummy argument
+
+program p
+  implicit none
+  character,               allocatable :: ca
+  character,               pointer     :: cp
+  character(len=:),        allocatable :: cd
+  character      (kind=4), allocatable :: ca4
+  character      (kind=4), pointer     :: cp4
+  character(len=:,kind=4), allocatable :: cd4
+  integer :: a = 65
+  allocate (ca, cp, ca4, cp4)
+
+  ! Check len=1 actual argument cases first
+  ca  =   "a"; cp  =   "b"; cd  =   "c"
+  ca4 = 4_"d"; cp4 = 4_"e"; cd4 = 4_"f"
+  call val  ("B","B")
+  call val  ("A",char(65))
+  call val  ("A",char(a))
+  call val  ("A",mychar(65))
+  call val  ("A",mychar(a))
+  call val4 (4_"C",4_"C")
+  call val4 (4_"A",char(65,kind=4))
+  call val4 (4_"A",char(a, kind=4))
+  call val  (ca,ca)
+  call val  (cp,cp)
+  call val  (cd,cd)
+  call val4 (ca4,ca4)
+  call val4 (cp4,cp4)
+  call val4 (cd4,cd4)
+  call sub  ("S")
+  call sub4 (4_"T")
+
+  ! Check that always the first character of the string is finally used
+  call val  (  "U++",  "U--")
+  call val4 (4_"V**",4_"V//")
+  call sub  (  "WTY")
+  call sub4 (4_"ZXV")
+  cd = "gkl"; cd4 = 4_"hmn"
+  call val  (cd,cd)
+  call val4 (cd4,cd4)
+  call sub  (cd)
+  call sub4 (cd4)
+  deallocate (ca, cp, ca4, cp4, cd, cd4)
+contains
+  subroutine val (x, c)
+    character(kind=1), intent(in) :: x  ! control: pass by reference
+    character(kind=1), value      :: c
+    print *, "by value(kind=1): ", c
+    if (c /= x)   stop 1
+    c = "*"
+    if (c /= "*") stop 2
+  end
+
+  subroutine val4 (x, c)
+    character(kind=4), intent(in) :: x  ! control: pass by reference
+    character(kind=4), value      :: c
+    print *, "by value(kind=4): ", c
+    if (c /= x)     stop 3
+    c = 4_"#"
+    if (c /= 4_"#") stop 4
+  end
+
+  subroutine sub (s)
+    character(*), intent(in) :: s
+    call val (s, s)
+  end
+  subroutine sub4 (s)
+    character(kind=4,len=*), intent(in) :: s
+    call val4 (s, s)
+  end
+
+  character function mychar (i)
+    integer, intent(in) :: i
+    mychar = char (i)
+  end
+end
diff --git a/gcc/testsuite/lib/gcc-dg.exp b/gcc/testsuite/lib/gcc-dg.exp
index 01c8c02..28529f5 100644
--- a/gcc/testsuite/lib/gcc-dg.exp
+++ b/gcc/testsuite/lib/gcc-dg.exp
@@ -364,6 +364,11 @@
     # Always remember to clear it in .exp file after executed all tests.
     global dg_runtest_extra_prunes
 
+    # Call into multiline.exp to handle any multiline output directives.
+    # This is done before the check for blank lines so that multiline
+    # output directives can have blank lines within them.
+    set text [handle-multiline-outputs $text]
+
     # Complain about blank lines in the output (PR other/69006)
     global allow_blank_lines
     if { !$allow_blank_lines } {
diff --git a/gcc/testsuite/lib/multiline.exp b/gcc/testsuite/lib/multiline.exp
index 73621a0..4c25bb7 100644
--- a/gcc/testsuite/lib/multiline.exp
+++ b/gcc/testsuite/lib/multiline.exp
@@ -139,7 +139,7 @@
     verbose "within dg-end-multiline-output: multiline_expected_outputs: $multiline_expected_outputs" 3
 }
 
-# Hook to be called by prune.exp's prune_gcc_output to
+# Hook to be called by gcc-dg.exp's gcc-dg-prune to
 # look for the expected multiline outputs, pruning them,
 # reporting PASS for those that are found, and FAIL for
 # those that weren't found.
@@ -149,6 +149,11 @@
 proc handle-multiline-outputs { text } {
     global multiline_expected_outputs
     global testname_with_flags
+
+    # If dg-enable-nn-line-numbers was provided, then obscure source-margin
+    # line numbers by converting them to "NN" form.
+    set text [maybe-handle-nn-line-numbers $text]
+    
     set index 0
     foreach entry $multiline_expected_outputs {
 	verbose "  entry: $entry" 3
diff --git a/gcc/testsuite/lib/prune.exp b/gcc/testsuite/lib/prune.exp
index cfe427c..8d37b24 100644
--- a/gcc/testsuite/lib/prune.exp
+++ b/gcc/testsuite/lib/prune.exp
@@ -108,13 +108,6 @@
     # Many tests that use visibility will still pass on platforms that don't support it.
     regsub -all "(^|\n)\[^\n\]*lto1: warning: visibility attribute not supported in this configuration; ignored\[^\n\]*" $text "" text
 
-    # If dg-enable-nn-line-numbers was provided, then obscure source-margin
-    # line numbers by converting them to "NN" form.
-    set text [maybe-handle-nn-line-numbers $text]
-    
-    # Call into multiline.exp to handle any multiline output directives.
-    set text [handle-multiline-outputs $text]
-
     #send_user "After:$text\n"
 
     return $text
diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp
index d79ad4b..c04db2b 100644
--- a/gcc/testsuite/lib/target-supports.exp
+++ b/gcc/testsuite/lib/target-supports.exp
@@ -10785,7 +10785,35 @@
     return 0
 }
 proc check_effective_target_c++23 { } {
-    return [check_effective_target_c++23_only]
+    if [check_effective_target_c++23_only] {
+	return 1
+    }
+    return [check_effective_target_c++26]
+}
+
+proc check_effective_target_c++23_down { } {
+    if ![check_effective_target_c++] {
+	return 0
+    }
+    return [expr ![check_effective_target_c++26] ]
+}
+
+proc check_effective_target_c++26_only { } {
+    global cxx_default
+    if ![check_effective_target_c++] {
+	return 0
+    }
+    if [check-flags { { } { } { -std=c++26 -std=gnu++26 -std=c++2c -std=gnu++2c } }] {
+	return 1
+    }
+    if { $cxx_default == "c++26" && [check-flags { { } { } { } { -std=* } }] } {
+	return 1
+    }
+    return 0
+}
+
+proc check_effective_target_c++26 { } {
+    return [check_effective_target_c++26_only]
 }
 
 # Check for C++ Concepts support, i.e. -fconcepts flag.
diff --git a/gcc/testsuite/objc-obj-c++-shared/GNUStep/Foundation/NSObjCRuntime.h b/gcc/testsuite/objc-obj-c++-shared/GNUStep/Foundation/NSObjCRuntime.h
index 189af80..62556f9 100644
--- a/gcc/testsuite/objc-obj-c++-shared/GNUStep/Foundation/NSObjCRuntime.h
+++ b/gcc/testsuite/objc-obj-c++-shared/GNUStep/Foundation/NSObjCRuntime.h
@@ -29,6 +29,9 @@
 #ifndef __NSObjCRuntime_h_GNUSTEP_BASE_INCLUDE
 #define __NSObjCRuntime_h_GNUSTEP_BASE_INCLUDE
 
+/* Allow the elaborated enum use in _GS_NAMED_ENUM. */
+#pragma GCC system_header
+
 #ifdef __cplusplus
 #ifndef __STDC_LIMIT_MACROS
 #define __STDC_LIMIT_MACROS 1
diff --git a/gcc/text-art/box-drawing-chars.inc b/gcc/text-art/box-drawing-chars.inc
new file mode 100644
index 0000000..a370255
--- /dev/null
+++ b/gcc/text-art/box-drawing-chars.inc
@@ -0,0 +1,18 @@
+/* Generated by contrib/unicode/gen-box-drawing-chars.py.  */
+
+0x0020, /* " ": U+0020: SPACE */
+0x2576, /* "╶": U+2576: BOX DRAWINGS LIGHT RIGHT */
+0x2574, /* "╴": U+2574: BOX DRAWINGS LIGHT LEFT */
+0x2500, /* "─": U+2500: BOX DRAWINGS LIGHT HORIZONTAL */
+0x2577, /* "╷": U+2577: BOX DRAWINGS LIGHT DOWN */
+0x250C, /* "┌": U+250C: BOX DRAWINGS LIGHT DOWN AND RIGHT */
+0x2510, /* "┐": U+2510: BOX DRAWINGS LIGHT DOWN AND LEFT */
+0x252C, /* "┬": U+252C: BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */
+0x2575, /* "╵": U+2575: BOX DRAWINGS LIGHT UP */
+0x2514, /* "└": U+2514: BOX DRAWINGS LIGHT UP AND RIGHT */
+0x2518, /* "┘": U+2518: BOX DRAWINGS LIGHT UP AND LEFT */
+0x2534, /* "┴": U+2534: BOX DRAWINGS LIGHT UP AND HORIZONTAL */
+0x2502, /* "│": U+2502: BOX DRAWINGS LIGHT VERTICAL */
+0x251C, /* "├": U+251C: BOX DRAWINGS LIGHT VERTICAL AND RIGHT */
+0x2524, /* "┤": U+2524: BOX DRAWINGS LIGHT VERTICAL AND LEFT */
+0x253C  /* "┼": U+253C: BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */
diff --git a/gcc/text-art/box-drawing.cc b/gcc/text-art/box-drawing.cc
new file mode 100644
index 0000000..7d49921
--- /dev/null
+++ b/gcc/text-art/box-drawing.cc
@@ -0,0 +1,73 @@
+/* Procedural lookup of box drawing characters.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#define INCLUDE_VECTOR
+#include "system.h"
+#include "coretypes.h"
+#include "text-art/box-drawing.h"
+#include "selftest.h"
+#include "text-art/selftests.h"
+
+
+/* According to
+     https://en.wikipedia.org/wiki/Box-drawing_character#Character_code
+   "DOS line- and box-drawing characters are not ordered in any programmatic
+   manner, so calculating a particular character shape needs to use a look-up
+   table. "
+   Hence this array.  */
+static const cppchar_t box_drawing_chars[] = {
+#include "text-art/box-drawing-chars.inc"
+};
+
+cppchar_t
+text_art::get_box_drawing_char (directions line_dirs)
+{
+  const size_t idx = line_dirs.as_index ();
+  gcc_assert (idx < 16);
+  return box_drawing_chars[idx];
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Run all selftests in this file.  */
+
+void
+text_art_box_drawing_cc_tests ()
+{
+  ASSERT_EQ (text_art::get_box_drawing_char
+	      (text_art::directions (false, false, false, false)),
+	     ' ');
+  ASSERT_EQ (text_art::get_box_drawing_char
+	       (text_art::directions (false, false, true, true)),
+	     0x2500); /* BOX DRAWINGS LIGHT HORIZONTAL */
+  ASSERT_EQ (text_art::get_box_drawing_char
+	       (text_art::directions (true, true, false, false)),
+	     0x2502); /* BOX DRAWINGS LIGHT VERTICAL */
+  ASSERT_EQ (text_art::get_box_drawing_char
+	       (text_art::directions (true, false, true, false)),
+	     0x2518); /* BOX DRAWINGS LIGHT UP AND LEFT */
+}
+
+} // namespace selftest
+
+#endif /* #if CHECKING_P */
diff --git a/gcc/text-art/box-drawing.h b/gcc/text-art/box-drawing.h
new file mode 100644
index 0000000..29f4d99
--- /dev/null
+++ b/gcc/text-art/box-drawing.h
@@ -0,0 +1,32 @@
+/* Procedural lookup of box drawing characters.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_TEXT_ART_BOX_DRAWING_H
+#define GCC_TEXT_ART_BOX_DRAWING_H
+
+#include "text-art/types.h"
+
+namespace text_art {
+
+extern cppchar_t get_box_drawing_char (directions line_dirs);
+
+} // namespace text_art
+
+#endif /* GCC_TEXT_ART_BOX_DRAWING_H */
diff --git a/gcc/text-art/canvas.cc b/gcc/text-art/canvas.cc
new file mode 100644
index 0000000..26ea051
--- /dev/null
+++ b/gcc/text-art/canvas.cc
@@ -0,0 +1,438 @@
+/* Canvas for random-access procedural text art.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#define INCLUDE_VECTOR
+#include "system.h"
+#include "coretypes.h"
+#include "pretty-print.h"
+#include "selftest.h"
+#include "text-art/selftests.h"
+#include "text-art/canvas.h"
+
+using namespace text_art;
+
+canvas::canvas (size_t size, const style_manager &style_mgr)
+: m_cells (size_t (size.w, size.h)),
+  m_style_mgr (style_mgr)
+{
+  m_cells.fill (cell_t (' '));
+}
+
+void
+canvas::paint (coord_t coord, styled_unichar ch)
+{
+  m_cells.set (coord, std::move (ch));
+}
+
+void
+canvas::paint_text (coord_t coord, const styled_string &text)
+{
+  for (auto ch : text)
+    {
+      paint (coord, ch);
+      if (ch.double_width_p ())
+	coord.x += 2;
+      else
+	coord.x++;
+    }
+}
+
+void
+canvas::fill (rect_t rect, cell_t c)
+{
+  for (int y = rect.get_min_y (); y < rect.get_next_y (); y++)
+    for (int x = rect.get_min_x (); x < rect.get_next_x (); x++)
+      paint(coord_t (x, y), c);
+}
+
+void
+canvas::debug_fill ()
+{
+  fill (rect_t (coord_t (0, 0), get_size ()), cell_t ('*'));
+}
+
+void
+canvas::print_to_pp (pretty_printer *pp,
+		     const char *per_line_prefix) const
+{
+  for (int y = 0; y < m_cells.get_size ().h; y++)
+    {
+      style::id_t curr_style_id = 0;
+      if (per_line_prefix)
+	pp_string (pp, per_line_prefix);
+
+      pretty_printer line_pp;
+      line_pp.show_color = pp->show_color;
+      line_pp.url_format = pp->url_format;
+      const int final_x_in_row = get_final_x_in_row (y);
+      for (int x = 0; x <= final_x_in_row; x++)
+	{
+	  if (x > 0)
+	    {
+	      const cell_t prev_cell = m_cells.get (coord_t (x - 1, y));
+	      if (prev_cell.double_width_p ())
+		 /* This cell is just a placeholder for the
+		    2nd column of a double width cell; skip it.  */
+		continue;
+	    }
+	  const cell_t cell = m_cells.get (coord_t (x, y));
+	  if (cell.get_style_id () != curr_style_id)
+	    {
+	      m_style_mgr.print_any_style_changes (&line_pp,
+						   curr_style_id,
+						   cell.get_style_id ());
+	      curr_style_id = cell.get_style_id ();
+	    }
+	  pp_unicode_character (&line_pp, cell.get_code ());
+	  if (cell.emoji_variant_p ())
+	    /* Append U+FE0F VARIATION SELECTOR-16 to select the emoji
+	       variation of the char.  */
+	    pp_unicode_character (&line_pp, 0xFE0F);
+	}
+      /* Reset the style at the end of each line.  */
+      m_style_mgr.print_any_style_changes (&line_pp, curr_style_id, 0);
+
+      /* Print from line_pp to pp, stripping trailing whitespace from
+	 the line.  */
+      const char *line_buf = pp_formatted_text (&line_pp);
+      ::size_t len = strlen (line_buf);
+      while (len > 0)
+	{
+	  if (line_buf[len - 1] == ' ')
+	    len--;
+	  else
+	    break;
+	}
+      pp_append_text (pp, line_buf, line_buf + len);
+      pp_newline (pp);
+    }
+}
+
+DEBUG_FUNCTION void
+canvas::debug (bool styled) const
+{
+  pretty_printer pp;
+  if (styled)
+    {
+      pp_show_color (&pp) = true;
+      pp.url_format = determine_url_format (DIAGNOSTICS_URL_AUTO);
+    }
+  print_to_pp (&pp);
+  fprintf (stderr, "%s\n", pp_formatted_text (&pp));
+}
+
+/* Find right-most non-default cell in this row,
+   or -1 if all are default.  */
+
+int
+canvas::get_final_x_in_row (int y) const
+{
+  for (int x = m_cells.get_size ().w - 1; x >= 0; x--)
+    {
+      cell_t cell = m_cells.get (coord_t (x, y));
+      if (cell.get_code () != ' '
+	  || cell.get_style_id () != style::id_plain)
+	return x;
+    }
+  return -1;
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+static void
+test_blank ()
+{
+  style_manager sm;
+  canvas c (canvas::size_t (5, 5), sm);
+  ASSERT_CANVAS_STREQ (c, false,
+		       ("\n"
+			"\n"
+			"\n"
+			"\n"
+			"\n"));
+}
+
+static void
+test_abc ()
+{
+  style_manager sm;
+  canvas c (canvas::size_t (3, 3), sm);
+  c.paint (canvas::coord_t (0, 0), styled_unichar ('A'));
+  c.paint (canvas::coord_t (1, 1), styled_unichar ('B'));
+  c.paint (canvas::coord_t (2, 2), styled_unichar ('C'));
+
+  ASSERT_CANVAS_STREQ (c, false,
+		       "A\n B\n  C\n");
+}
+
+static void
+test_debug_fill ()
+{
+  style_manager sm;
+  canvas c (canvas::size_t (5, 3), sm);
+  c.debug_fill();
+  ASSERT_CANVAS_STREQ (c, false,
+		       ("*****\n"
+			"*****\n"
+			"*****\n"));
+}
+
+static void
+test_text ()
+{
+  style_manager sm;
+  canvas c (canvas::size_t (6, 1), sm);
+  c.paint_text (canvas::coord_t (0, 0), styled_string (sm, "012345"));
+  ASSERT_CANVAS_STREQ (c, false,
+		       ("012345\n"));
+
+  /* Paint an emoji character that should occupy two canvas columns when
+     printed.  */
+  c.paint_text (canvas::coord_t (2, 0), styled_string ((cppchar_t)0x1f642));
+  ASSERT_CANVAS_STREQ (c, false,
+		       ("01🙂45\n"));
+}
+
+static void
+test_circle ()
+{
+  canvas::size_t sz (30, 30);
+  style_manager sm;
+  canvas canvas (sz, sm);
+  canvas::coord_t center (sz.w / 2, sz.h / 2);
+  const int radius = 12;
+  const int radius_squared = radius * radius;
+  for (int x = 0; x < sz.w; x++)
+    for (int y = 0; y < sz.h; y++)
+      {
+	int dx = x - center.x;
+	int dy = y - center.y;
+	char ch = "AB"[(x + y) % 2];
+	if (dx * dx + dy * dy < radius_squared)
+	  canvas.paint (canvas::coord_t (x, y), styled_unichar (ch));
+      }
+  ASSERT_CANVAS_STREQ
+    (canvas, false,
+     ("\n"
+      "\n"
+      "\n"
+      "\n"
+      "           BABABABAB\n"
+      "         ABABABABABABA\n"
+      "        ABABABABABABABA\n"
+      "       ABABABABABABABABA\n"
+      "      ABABABABABABABABABA\n"
+      "     ABABABABABABABABABABA\n"
+      "     BABABABABABABABABABAB\n"
+      "    BABABABABABABABABABABAB\n"
+      "    ABABABABABABABABABABABA\n"
+      "    BABABABABABABABABABABAB\n"
+      "    ABABABABABABABABABABABA\n"
+      "    BABABABABABABABABABABAB\n"
+      "    ABABABABABABABABABABABA\n"
+      "    BABABABABABABABABABABAB\n"
+      "    ABABABABABABABABABABABA\n"
+      "    BABABABABABABABABABABAB\n"
+      "     BABABABABABABABABABAB\n"
+      "     ABABABABABABABABABABA\n"
+      "      ABABABABABABABABABA\n"
+      "       ABABABABABABABABA\n"
+      "        ABABABABABABABA\n"
+      "         ABABABABABABA\n"
+      "           BABABABAB\n"
+      "\n"
+      "\n"
+      "\n"));
+}
+
+static void
+test_color_circle ()
+{
+  const canvas::size_t sz (10, 10);
+  const canvas::coord_t center (sz.w / 2, sz.h / 2);
+  const int outer_r2 = 25;
+  const int inner_r2 = 10;
+  style_manager sm;
+  canvas c (sz, sm);
+  for (int x = 0; x < sz.w; x++)
+    for (int y = 0; y < sz.h; y++)
+      {
+	const int dist_from_center_squared
+	  = ((x - center.x) * (x - center.x) + (y - center.y) * (y - center.y));
+	if (dist_from_center_squared < outer_r2)
+	  {
+	    style s;
+	    if (dist_from_center_squared < inner_r2)
+	      s.m_fg_color = style::named_color::RED;
+	    else
+	      s.m_fg_color = style::named_color::GREEN;
+	    c.paint (canvas::coord_t (x, y),
+		     styled_unichar ('*', false, sm.get_or_create_id (s)));
+	  }
+      }
+  ASSERT_EQ (sm.get_num_styles (), 3);
+  ASSERT_CANVAS_STREQ
+    (c, false,
+     ("\n"
+      "   *****\n"
+      "  *******\n"
+      " *********\n"
+      " *********\n"
+      " *********\n"
+      " *********\n"
+      " *********\n"
+      "  *******\n"
+      "   *****\n"));
+  ASSERT_CANVAS_STREQ
+    (c, true,
+     ("\n"
+      "   *****\n"
+      "  *******\n"
+      " *********\n"
+      " *********\n"
+      " *********\n"
+      " *********\n"
+      " *********\n"
+      "  *******\n"
+      "   *****\n"));
+}
+
+static void
+test_bold ()
+{
+  auto_fix_quotes fix_quotes;
+  style_manager sm;
+  styled_string s (styled_string::from_fmt (sm, nullptr,
+					    "before %qs after", "foo"));
+  canvas c (canvas::size_t (s.calc_canvas_width (), 1), sm);
+  c.paint_text (canvas::coord_t (0, 0), s);
+  ASSERT_CANVAS_STREQ (c, false,
+		       "before `foo' after\n");
+  ASSERT_CANVAS_STREQ (c, true,
+		       "before `foo' after\n");
+}
+
+static void
+test_emoji ()
+{
+  style_manager sm;
+  styled_string s (0x26A0, /* U+26A0 WARNING SIGN.  */
+		   true);
+  canvas c (canvas::size_t (s.calc_canvas_width (), 1), sm);
+  c.paint_text (canvas::coord_t (0, 0), s);
+  ASSERT_CANVAS_STREQ (c, false, "⚠️\n");
+  ASSERT_CANVAS_STREQ (c, true, "⚠️\n");
+}
+
+static void
+test_emoji_2 ()
+{
+  style_manager sm;
+  styled_string s;
+  s.append (styled_string (0x26A0, /* U+26A0 WARNING SIGN.  */
+			   true));
+  s.append (styled_string (sm, "test"));
+  ASSERT_EQ (s.size (), 5);
+  ASSERT_EQ (s.calc_canvas_width (), 5);
+  canvas c (canvas::size_t (s.calc_canvas_width (), 1), sm);
+  c.paint_text (canvas::coord_t (0, 0), s);
+  ASSERT_CANVAS_STREQ (c, false,
+		       /* U+26A0 WARNING SIGN, as UTF-8: 0xE2 0x9A 0xA0.  */
+		       "\xE2\x9A\xA0"
+		       /* U+FE0F VARIATION SELECTOR-16, as UTF-8: 0xEF 0xB8 0x8F.  */
+		       "\xEF\xB8\x8F"
+		       "test\n");
+}
+
+static void
+test_canvas_urls ()
+{
+  style_manager sm;
+  canvas canvas (canvas::size_t (9, 3), sm);
+  styled_string foo_ss (sm, "foo");
+  foo_ss.set_url (sm, "https://www.example.com/foo");
+  styled_string bar_ss (sm, "bar");
+  bar_ss.set_url (sm, "https://www.example.com/bar");
+  canvas.paint_text(canvas::coord_t (1, 1), foo_ss);
+  canvas.paint_text(canvas::coord_t (5, 1), bar_ss);
+
+  ASSERT_CANVAS_STREQ (canvas, false,
+		       ("\n"
+			" foo bar\n"
+			"\n"));
+  {
+    pretty_printer pp;
+    pp_show_color (&pp) = true;
+    pp.url_format = URL_FORMAT_ST;
+    assert_canvas_streq (SELFTEST_LOCATION, canvas, &pp,
+			 (/* Line 1.  */
+			  "\n"
+			  /* Line 2.  */
+			  " "
+			  "\33]8;;https://www.example.com/foo\33\\foo\33]8;;\33\\"
+			  " "
+			  "\33]8;;https://www.example.com/bar\33\\bar\33]8;;\33\\"
+			  "\n"
+			  /* Line 3.  */
+			  "\n"));
+  }
+
+  {
+    pretty_printer pp;
+    pp_show_color (&pp) = true;
+    pp.url_format = URL_FORMAT_BEL;
+    assert_canvas_streq (SELFTEST_LOCATION, canvas, &pp,
+			 (/* Line 1.  */
+			  "\n"
+			  /* Line 2.  */
+			  " "
+			  "\33]8;;https://www.example.com/foo\afoo\33]8;;\a"
+			  " "
+			  "\33]8;;https://www.example.com/bar\abar\33]8;;\a"
+			  "\n"
+			  /* Line 3.  */
+			  "\n"));
+  }
+}
+
+/* Run all selftests in this file.  */
+
+void
+text_art_canvas_cc_tests ()
+{
+  test_blank ();
+  test_abc ();
+  test_debug_fill ();
+  test_text ();
+  test_circle ();
+  test_color_circle ();
+  test_bold ();
+  test_emoji ();
+  test_emoji_2 ();
+  test_canvas_urls ();
+}
+
+} // namespace selftest
+
+
+#endif /* #if CHECKING_P */
diff --git a/gcc/text-art/canvas.h b/gcc/text-art/canvas.h
new file mode 100644
index 0000000..4954977
--- /dev/null
+++ b/gcc/text-art/canvas.h
@@ -0,0 +1,74 @@
+/* Canvas for random-access procedural text art.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_TEXT_ART_CANVAS_H
+#define GCC_TEXT_ART_CANVAS_H
+
+#include "text-art/types.h"
+
+namespace text_art {
+
+class canvas;
+
+/* A 2 dimensional grid of text cells (a "canvas"), which
+   can be written to ("painted") via random access, and then
+   written out to a pretty_printer once the picture is complete.
+
+   Each text cell can be styled independently (colorization,
+   URLs, etc).  */
+
+class canvas
+{
+ public:
+  typedef styled_unichar cell_t;
+  typedef size<class canvas> size_t;
+  typedef coord<class canvas> coord_t;
+  typedef range<class canvas> range_t;
+  typedef rect<class canvas> rect_t;
+
+  canvas (size_t size, const style_manager &style_mgr);
+
+  size_t get_size () const { return m_cells.get_size (); }
+
+  void paint (coord_t coord, cell_t c);
+  void paint_text (coord_t coord, const styled_string &text);
+
+  void fill (rect_t rect, cell_t c);
+  void debug_fill ();
+
+  void print_to_pp (pretty_printer *pp,
+		    const char *per_line_prefix = NULL) const;
+  void debug (bool styled) const;
+
+  const cell_t &get (coord_t coord) const
+  {
+    return m_cells.get (coord);
+  }
+
+ private:
+  int get_final_x_in_row (int y) const;
+
+  array2<cell_t, size_t, coord_t> m_cells;
+  const style_manager &m_style_mgr;
+};
+
+} // namespace text_art
+
+#endif /* GCC_TEXT_ART_CANVAS_H */
diff --git a/gcc/text-art/ruler.cc b/gcc/text-art/ruler.cc
new file mode 100644
index 0000000..3323a05
--- /dev/null
+++ b/gcc/text-art/ruler.cc
@@ -0,0 +1,724 @@
+/* Classes for printing labelled rulers.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#define INCLUDE_ALGORITHM
+#define INCLUDE_VECTOR
+#include "system.h"
+#include "coretypes.h"
+#include "pretty-print.h"
+#include "selftest.h"
+#include "text-art/selftests.h"
+#include "text-art/ruler.h"
+#include "text-art/theme.h"
+
+using namespace text_art;
+
+void
+x_ruler::add_label (const canvas::range_t &r,
+		    styled_string text,
+		    style::id_t style_id,
+		    label_kind kind)
+{
+  m_labels.push_back (label (r, std::move (text), style_id, kind));
+  m_has_layout = false;
+}
+
+int
+x_ruler::get_canvas_y (int rel_y) const
+{
+  gcc_assert (rel_y >= 0);
+  gcc_assert (rel_y < m_size.h);
+  switch (m_label_dir)
+    {
+    default:
+      gcc_unreachable ();
+    case label_dir::ABOVE:
+      return m_size.h - (rel_y + 1);
+    case label_dir::BELOW:
+      return rel_y;
+    }
+}
+
+void
+x_ruler::paint_to_canvas (canvas &canvas,
+			  canvas::coord_t offset,
+			  const theme &theme)
+{
+  ensure_layout ();
+
+  if (0)
+    canvas.fill (canvas::rect_t (offset, m_size),
+		 canvas::cell_t ('*'));
+
+  for (size_t idx = 0; idx < m_labels.size (); idx++)
+    {
+      const label &iter_label = m_labels[idx];
+
+      /* Paint the ruler itself.  */
+      const int ruler_rel_y = get_canvas_y (0);
+      for (int rel_x = iter_label.m_range.start;
+	   rel_x < iter_label.m_range.next;
+	   rel_x++)
+	{
+	  enum theme::cell_kind kind = theme::cell_kind::X_RULER_MIDDLE;
+
+	  if (rel_x == iter_label.m_range.start)
+	    {
+	      kind = theme::cell_kind::X_RULER_LEFT_EDGE;
+	      if (idx > 0)
+		{
+		  const label &prev_label = m_labels[idx - 1];
+		  if (prev_label.m_range.get_max () == iter_label.m_range.start)
+		    kind = theme::cell_kind::X_RULER_INTERNAL_EDGE;
+		}
+	    }
+	  else if (rel_x == iter_label.m_range.get_max ())
+	    kind = theme::cell_kind::X_RULER_RIGHT_EDGE;
+	  else if (rel_x == iter_label.m_connector_x)
+	    {
+	      switch (m_label_dir)
+		{
+		default:
+		  gcc_unreachable ();
+		case label_dir::ABOVE:
+		  kind = theme::cell_kind::X_RULER_CONNECTOR_TO_LABEL_ABOVE;
+		  break;
+		case label_dir::BELOW:
+		  kind = theme::cell_kind::X_RULER_CONNECTOR_TO_LABEL_BELOW;
+		  break;
+		}
+	    }
+	  canvas.paint (canvas::coord_t (rel_x, ruler_rel_y) + offset,
+			theme.get_cell (kind, iter_label.m_style_id));
+	}
+
+      /* Paint the connector to the text.  */
+      for (int connector_rel_y = 1;
+	   connector_rel_y < iter_label.m_text_rect.get_min_y ();
+	   connector_rel_y++)
+	{
+	  canvas.paint
+	    ((canvas::coord_t (iter_label.m_connector_x,
+			       get_canvas_y (connector_rel_y))
+	      + offset),
+	     theme.get_cell (theme::cell_kind::X_RULER_VERTICAL_CONNECTOR,
+			     iter_label.m_style_id));
+	}
+
+      /* Paint the text.  */
+      switch (iter_label.m_kind)
+	{
+	default:
+	  gcc_unreachable ();
+	case x_ruler::label_kind::TEXT:
+	  canvas.paint_text
+	    ((canvas::coord_t (iter_label.m_text_rect.get_min_x (),
+			       get_canvas_y (iter_label.m_text_rect.get_min_y ()))
+	      + offset),
+	     iter_label.m_text);
+	  break;
+
+	case x_ruler::label_kind::TEXT_WITH_BORDER:
+	  {
+	    const canvas::range_t rel_x_range
+	      (iter_label.m_text_rect.get_x_range ());
+
+	    enum theme::cell_kind inner_left_kind;
+	    enum theme::cell_kind inner_connector_kind;
+	    enum theme::cell_kind inner_right_kind;
+	    enum theme::cell_kind outer_left_kind;
+	    enum theme::cell_kind outer_right_kind;
+
+	      switch (m_label_dir)
+		{
+		default:
+		  gcc_unreachable ();
+		case label_dir::ABOVE:
+		  outer_left_kind = theme::cell_kind::TEXT_BORDER_TOP_LEFT;
+		  outer_right_kind = theme::cell_kind::TEXT_BORDER_TOP_RIGHT;
+		  inner_left_kind = theme::cell_kind::TEXT_BORDER_BOTTOM_LEFT;
+		  inner_connector_kind = theme::cell_kind::X_RULER_CONNECTOR_TO_LABEL_BELOW;
+		  inner_right_kind = theme::cell_kind::TEXT_BORDER_BOTTOM_RIGHT;
+		  break;
+		case label_dir::BELOW:
+		  inner_left_kind = theme::cell_kind::TEXT_BORDER_TOP_LEFT;
+		  inner_connector_kind = theme::cell_kind::X_RULER_CONNECTOR_TO_LABEL_ABOVE;
+		  inner_right_kind = theme::cell_kind::TEXT_BORDER_TOP_RIGHT;
+		  outer_left_kind = theme::cell_kind::TEXT_BORDER_BOTTOM_LEFT;
+		  outer_right_kind = theme::cell_kind::TEXT_BORDER_BOTTOM_RIGHT;
+		  break;
+		}
+	    /* Inner border.  */
+	    {
+	      const int rel_canvas_y
+		= get_canvas_y (iter_label.m_text_rect.get_min_y ());
+	      /* Left corner.  */
+	      canvas.paint ((canvas::coord_t (rel_x_range.get_min (),
+					      rel_canvas_y)
+			     + offset),
+			    theme.get_cell (inner_left_kind,
+					    iter_label.m_style_id));
+	      /* Edge.  */
+	      const canvas::cell_t edge_border_cell
+		= theme.get_cell (theme::cell_kind::TEXT_BORDER_HORIZONTAL,
+				  iter_label.m_style_id);
+	      const canvas::cell_t connector_border_cell
+		= theme.get_cell (inner_connector_kind,
+				  iter_label.m_style_id);
+	      for (int rel_x = rel_x_range.get_min () + 1;
+		   rel_x < rel_x_range.get_max ();
+		   rel_x++)
+		if (rel_x == iter_label.m_connector_x)
+		  canvas.paint ((canvas::coord_t (rel_x, rel_canvas_y)
+				 + offset),
+				connector_border_cell);
+		else
+		  canvas.paint ((canvas::coord_t (rel_x, rel_canvas_y)
+				 + offset),
+				edge_border_cell);
+
+	      /* Right corner.  */
+	      canvas.paint ((canvas::coord_t (rel_x_range.get_max (),
+					      rel_canvas_y)
+			     + offset),
+			    theme.get_cell (inner_right_kind,
+					    iter_label.m_style_id));
+	    }
+
+	    {
+	      const int rel_canvas_y
+		= get_canvas_y (iter_label.m_text_rect.get_min_y () + 1);
+	      const canvas::cell_t border_cell
+		= theme.get_cell (theme::cell_kind::TEXT_BORDER_VERTICAL,
+				  iter_label.m_style_id);
+
+	      /* Left border.  */
+	      canvas.paint ((canvas::coord_t (rel_x_range.get_min (),
+					      rel_canvas_y)
+			     + offset),
+			    border_cell);
+	      /* Text.  */
+	      canvas.paint_text ((canvas::coord_t (rel_x_range.get_min () + 1,
+						   rel_canvas_y)
+				  + offset),
+				 iter_label.m_text);
+	      /* Right border.  */
+	      canvas.paint ((canvas::coord_t (rel_x_range.get_max (),
+					      rel_canvas_y)
+			     + offset),
+			    border_cell);
+	    }
+
+	    /* Outer border.  */
+	    {
+	      const int rel_canvas_y
+		= get_canvas_y (iter_label.m_text_rect.get_max_y ());
+	      /* Left corner.  */
+	      canvas.paint ((canvas::coord_t (rel_x_range.get_min (),
+					      rel_canvas_y)
+			     + offset),
+			    theme.get_cell (outer_left_kind,
+					    iter_label.m_style_id));
+	      /* Edge.  */
+	      const canvas::cell_t border_cell
+		= theme.get_cell (theme::cell_kind::TEXT_BORDER_HORIZONTAL,
+				  iter_label.m_style_id);
+	      for (int rel_x = rel_x_range.get_min () + 1;
+		   rel_x < rel_x_range.get_max ();
+		   rel_x++)
+		canvas.paint ((canvas::coord_t (rel_x, rel_canvas_y)
+			       + offset),
+			      border_cell);
+
+	      /* Right corner.  */
+	      canvas.paint ((canvas::coord_t (rel_x_range.get_max (),
+					      rel_canvas_y)
+			     + offset),
+			    theme.get_cell (outer_right_kind,
+					    iter_label.m_style_id));
+	    }
+	  }
+	  break;
+	}
+    }
+}
+
+DEBUG_FUNCTION void
+x_ruler::debug (const style_manager &sm)
+{
+  canvas c (get_size (), sm);
+  paint_to_canvas (c, canvas::coord_t (0, 0), unicode_theme ());
+  c.debug (true);
+}
+
+x_ruler::label::label (const canvas::range_t &range,
+		       styled_string text,
+		       style::id_t style_id,
+		       label_kind kind)
+: m_range (range),
+  m_text (std::move (text)),
+  m_style_id (style_id),
+  m_kind (kind),
+  m_text_rect (canvas::coord_t (0, 0),
+	       canvas::size_t (m_text.calc_canvas_width (), 1)),
+  m_connector_x ((m_range.get_min () + m_range.get_max ()) / 2)
+{
+  if (kind == label_kind::TEXT_WITH_BORDER)
+    {
+      m_text_rect.m_size.w += 2;
+      m_text_rect.m_size.h += 2;
+    }
+}
+
+bool
+x_ruler::label::operator< (const label &other) const
+{
+  int cmp = m_range.start - other.m_range.start;
+  if (cmp)
+    return cmp < 0;
+  return m_range.next < other.m_range.next;
+}
+
+void
+x_ruler::ensure_layout ()
+{
+  if (m_has_layout)
+    return;
+  update_layout ();
+  m_has_layout = true;
+}
+
+void
+x_ruler::update_layout ()
+{
+  if (m_labels.empty ())
+    return;
+
+  std::sort (m_labels.begin (), m_labels.end ());
+
+  /* Place labels.  */
+  int ruler_width = m_labels.back ().m_range.get_next ();
+  int width_with_labels = ruler_width;
+
+  /* Get x coordinates of text parts of each label
+     (m_text_rect.m_top_left.x for each label).  */
+  for (size_t idx = 0; idx < m_labels.size (); idx++)
+    {
+      label &iter_label = m_labels[idx];
+      /* Attempt to center the text label.  */
+      int min_x;
+      if (idx > 0)
+	{
+	  /* ...but don't overlap with the connector to the left.  */
+	  int left_neighbor_connector_x = m_labels[idx - 1].m_connector_x;
+	  min_x = left_neighbor_connector_x + 1;
+	}
+      else
+	{
+	  /* ...or go beyond the leftmost column.  */
+	  min_x = 0;
+	}
+      int connector_x = iter_label.m_connector_x;
+      int centered_x
+	= connector_x - ((int)iter_label.m_text_rect.get_width () / 2);
+      int text_x = std::max (min_x, centered_x);
+      iter_label.m_text_rect.m_top_left.x = text_x;
+    }
+
+  /* Now walk backwards trying to place them vertically,
+     setting m_text_rect.m_top_left.y for each label,
+     consolidating the rows where possible.
+     The y cooordinates are stored with respect to label_dir::BELOW.  */
+  int label_y = 2;
+  for (int idx = m_labels.size () - 1; idx >= 0; idx--)
+    {
+      label &iter_label = m_labels[idx];
+      /* Does it fit on the same row as the text label to the right?  */
+      size_t text_len = iter_label.m_text_rect.get_width ();
+      /* Get the x-coord of immediately beyond iter_label's text.  */
+      int next_x = iter_label.m_text_rect.get_min_x () + text_len;
+      if (idx < (int)m_labels.size () - 1)
+	{
+	  if (next_x >= m_labels[idx + 1].m_text_rect.get_min_x ())
+	    {
+	      /* If not, start a new row.  */
+	      label_y += m_labels[idx + 1].m_text_rect.get_height ();
+	    }
+	}
+      iter_label.m_text_rect.m_top_left.y = label_y;
+      width_with_labels = std::max (width_with_labels, next_x);
+    }
+
+  m_size = canvas::size_t (width_with_labels,
+			   label_y + m_labels[0].m_text_rect.get_height ());
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+static void
+assert_x_ruler_streq (const location &loc,
+		      x_ruler &ruler,
+		      const theme &theme,
+		      const style_manager &sm,
+		      bool styled,
+		      const char *expected_str)
+{
+  canvas c (ruler.get_size (), sm);
+  ruler.paint_to_canvas (c, canvas::coord_t (0, 0), theme);
+  if (0)
+    c.debug (styled);
+  assert_canvas_streq (loc, c, styled, expected_str);
+}
+
+#define ASSERT_X_RULER_STREQ(RULER, THEME, SM, STYLED, EXPECTED_STR)	\
+  SELFTEST_BEGIN_STMT							\
+    assert_x_ruler_streq ((SELFTEST_LOCATION),				\
+			  (RULER),					\
+			  (THEME),					\
+			  (SM),						\
+			  (STYLED),					\
+			  (EXPECTED_STR));				\
+  SELFTEST_END_STMT
+
+static void
+test_single ()
+{
+  style_manager sm;
+  x_ruler r (x_ruler::label_dir::BELOW);
+  r.add_label (canvas::range_t (0, 11), styled_string (sm, "foo"),
+	       style::id_plain, x_ruler::label_kind::TEXT);
+  ASSERT_X_RULER_STREQ
+    (r, ascii_theme (), sm, true,
+     ("|~~~~+~~~~|\n"
+      "     |\n"
+      "    foo\n"));
+  ASSERT_X_RULER_STREQ
+    (r, unicode_theme (), sm, true,
+     ("├────┬────┤\n"
+      "     │\n"
+      "    foo\n"));
+}
+
+static void
+test_single_above ()
+{
+  style_manager sm;
+  x_ruler r (x_ruler::label_dir::ABOVE);
+  r.add_label (canvas::range_t (0, 11), styled_string (sm, "hello world"),
+	       style::id_plain);
+  ASSERT_X_RULER_STREQ
+    (r, ascii_theme (), sm, true,
+     ("hello world\n"
+      "     |\n"
+      "|~~~~+~~~~|\n"));
+  ASSERT_X_RULER_STREQ
+    (r, unicode_theme (), sm, true,
+     ("hello world\n"
+      "     │\n"
+      "├────┴────┤\n"));
+}
+
+static void
+test_multiple_contiguous ()
+{
+  style_manager sm;
+  x_ruler r (x_ruler::label_dir::BELOW);
+  r.add_label (canvas::range_t (0, 11), styled_string (sm, "foo"),
+	       style::id_plain);
+  r.add_label (canvas::range_t (10, 16), styled_string (sm, "bar"),
+	       style::id_plain);
+  ASSERT_X_RULER_STREQ
+    (r, ascii_theme (), sm, true,
+     ("|~~~~+~~~~|~+~~|\n"
+      "     |      |\n"
+      "    foo    bar\n"));
+  ASSERT_X_RULER_STREQ
+    (r, unicode_theme (), sm, true,
+     ("├────┬────┼─┬──┤\n"
+      "     │      │\n"
+      "    foo    bar\n"));
+}
+
+static void
+test_multiple_contiguous_above ()
+{
+  style_manager sm;
+  x_ruler r (x_ruler::label_dir::ABOVE);
+  r.add_label (canvas::range_t (0, 11), styled_string (sm, "foo"),
+	       style::id_plain);
+  r.add_label (canvas::range_t (10, 16), styled_string (sm, "bar"),
+	       style::id_plain);
+  ASSERT_X_RULER_STREQ
+    (r, ascii_theme (), sm, true,
+     ("    foo    bar\n"
+      "     |      |\n"
+      "|~~~~+~~~~|~+~~|\n"));
+  ASSERT_X_RULER_STREQ
+    (r, unicode_theme (), sm, true,
+     ("    foo    bar\n"
+      "     │      │\n"
+      "├────┴────┼─┴──┤\n"));
+}
+
+static void
+test_multiple_contiguous_abutting_labels ()
+{
+  style_manager sm;
+  x_ruler r (x_ruler::label_dir::BELOW);
+  r.add_label (canvas::range_t (0, 11), styled_string (sm, "12345678"),
+	       style::id_plain);
+  r.add_label (canvas::range_t (10, 16), styled_string (sm, "1234678"),
+	       style::id_plain);
+  ASSERT_X_RULER_STREQ
+    (r, unicode_theme (), sm, true,
+     ("├────┬────┼─┬──┤\n"
+      "     │      │\n"
+      "     │   1234678\n"
+      " 12345678\n"));
+}
+
+static void
+test_multiple_contiguous_overlapping_labels ()
+{
+  style_manager sm;
+  x_ruler r (x_ruler::label_dir::BELOW);
+  r.add_label (canvas::range_t (0, 11), styled_string (sm, "123456789"),
+	       style::id_plain);
+  r.add_label (canvas::range_t (10, 16), styled_string (sm, "12346789"),
+	       style::id_plain);
+  ASSERT_X_RULER_STREQ
+    (r, unicode_theme (), sm, true,
+     ("├────┬────┼─┬──┤\n"
+      "     │      │\n"
+      "     │  12346789\n"
+      " 123456789\n"));
+}
+static void
+test_abutting_left_border ()
+{
+  style_manager sm;
+  x_ruler r (x_ruler::label_dir::BELOW);
+  r.add_label (canvas::range_t (0, 6),
+	       styled_string (sm, "this is a long label"),
+	       style::id_plain);
+  ASSERT_X_RULER_STREQ
+    (r, unicode_theme (), sm, true,
+     ("├─┬──┤\n"
+      "  │\n"
+      "this is a long label\n"));
+}
+
+static void
+test_too_long_to_consolidate_vertically ()
+{
+  style_manager sm;
+  x_ruler r (x_ruler::label_dir::BELOW);
+  r.add_label (canvas::range_t (0, 11),
+	       styled_string (sm, "long string A"),
+	       style::id_plain);
+  r.add_label (canvas::range_t (10, 16),
+	       styled_string (sm, "long string B"),
+	       style::id_plain);
+  ASSERT_X_RULER_STREQ
+    (r, unicode_theme (), sm, true,
+     ("├────┬────┼─┬──┤\n"
+      "     │      │\n"
+      "     │long string B\n"
+      "long string A\n"));
+}
+
+static void
+test_abutting_neighbor ()
+{
+  style_manager sm;
+  x_ruler r (x_ruler::label_dir::BELOW);
+  r.add_label (canvas::range_t (0, 11),
+	       styled_string (sm, "very long string A"),
+	       style::id_plain);
+  r.add_label (canvas::range_t (10, 16),
+	       styled_string (sm, "very long string B"),
+	       style::id_plain);
+  ASSERT_X_RULER_STREQ
+    (r, unicode_theme (), sm, true,
+     ("├────┬────┼─┬──┤\n"
+      "     │      │\n"
+      "     │very long string B\n"
+      "very long string A\n"));
+}
+
+static void
+test_gaps ()
+{
+  style_manager sm;
+  x_ruler r (x_ruler::label_dir::BELOW);
+  r.add_label (canvas::range_t (0, 5),
+	       styled_string (sm, "foo"),
+	       style::id_plain);
+  r.add_label (canvas::range_t (10, 15),
+	       styled_string (sm, "bar"),
+	       style::id_plain);
+  ASSERT_X_RULER_STREQ
+    (r, ascii_theme (), sm, true,
+     ("|~+~|     |~+~|\n"
+      "  |         |\n"
+      " foo       bar\n"));
+}
+
+static void
+test_styled ()
+{
+  style_manager sm;
+  style s1, s2;
+  s1.m_bold = true;
+  s1.m_fg_color = style::named_color::YELLOW;
+  s2.m_bold = true;
+  s2.m_fg_color = style::named_color::BLUE;
+  style::id_t sid1 = sm.get_or_create_id (s1);
+  style::id_t sid2 = sm.get_or_create_id (s2);
+
+  x_ruler r (x_ruler::label_dir::BELOW);
+  r.add_label (canvas::range_t (0, 5), styled_string (sm, "foo"), sid1);
+  r.add_label (canvas::range_t (10, 15), styled_string (sm, "bar"), sid2);
+  ASSERT_X_RULER_STREQ
+    (r, ascii_theme (), sm, true,
+     ("|~+~|     |~+~|\n"
+      "  |         |\n"
+      " foo       bar\n"));
+}
+
+static void
+test_borders ()
+{
+  style_manager sm;
+  {
+    x_ruler r (x_ruler::label_dir::BELOW);
+    r.add_label (canvas::range_t (0, 5),
+		 styled_string (sm, "label 1"),
+		 style::id_plain,
+		 x_ruler::label_kind::TEXT_WITH_BORDER);
+    r.add_label (canvas::range_t (10, 15),
+		 styled_string (sm, "label 2"),
+		 style::id_plain);
+    r.add_label (canvas::range_t (20, 25),
+		 styled_string (sm, "label 3"),
+		 style::id_plain,
+		 x_ruler::label_kind::TEXT_WITH_BORDER);
+    ASSERT_X_RULER_STREQ
+      (r, ascii_theme (), sm, true,
+       "|~+~|     |~+~|     |~+~|\n"
+       "  |         |         |\n"
+       "  |      label 2  +---+---+\n"
+       "+-+-----+         |label 3|\n"
+       "|label 1|         +-------+\n"
+       "+-------+\n");
+    ASSERT_X_RULER_STREQ
+      (r, unicode_theme (), sm, true,
+       "├─┬─┤     ├─┬─┤     ├─┬─┤\n"
+       "  │         │         │\n"
+       "  │      label 2  ╭───┴───╮\n"
+       "╭─┴─────╮         │label 3│\n"
+       "│label 1│         ╰───────╯\n"
+       "╰───────╯\n");
+  }
+  {
+    x_ruler r (x_ruler::label_dir::ABOVE);
+    r.add_label (canvas::range_t (0, 5),
+		 styled_string (sm, "label 1"),
+		 style::id_plain,
+		 x_ruler::label_kind::TEXT_WITH_BORDER);
+    r.add_label (canvas::range_t (10, 15),
+		 styled_string (sm, "label 2"),
+		 style::id_plain);
+    r.add_label (canvas::range_t (20, 25),
+		 styled_string (sm, "label 3"),
+		 style::id_plain,
+		 x_ruler::label_kind::TEXT_WITH_BORDER);
+    ASSERT_X_RULER_STREQ
+      (r, ascii_theme (), sm, true,
+       "+-------+\n"
+       "|label 1|         +-------+\n"
+       "+-+-----+         |label 3|\n"
+       "  |      label 2  +---+---+\n"
+       "  |         |         |\n"
+       "|~+~|     |~+~|     |~+~|\n");
+    ASSERT_X_RULER_STREQ
+      (r, unicode_theme (), sm, true,
+       "╭───────╮\n"
+       "│label 1│         ╭───────╮\n"
+       "╰─┬─────╯         │label 3│\n"
+       "  │      label 2  ╰───┬───╯\n"
+       "  │         │         │\n"
+       "├─┴─┤     ├─┴─┤     ├─┴─┤\n");
+  }
+}
+
+static void
+test_emoji ()
+{
+  style_manager sm;
+
+  styled_string s;
+  s.append (styled_string (0x26A0, /* U+26A0 WARNING SIGN.  */
+			   true));
+  s.append (styled_string (sm, "  "));
+  s.append (styled_string (sm, "this is a warning"));
+
+  x_ruler r (x_ruler::label_dir::BELOW);
+  r.add_label (canvas::range_t (0, 5),
+	       std::move (s),
+	       style::id_plain,
+	       x_ruler::label_kind::TEXT_WITH_BORDER);
+
+  ASSERT_X_RULER_STREQ
+    (r, ascii_theme (), sm, true,
+     "|~+~|\n"
+     "  |\n"
+     "+-+------------------+\n"
+     "|⚠️  this is a warning|\n"
+     "+--------------------+\n");
+}
+
+/* Run all selftests in this file.  */
+
+void
+text_art_ruler_cc_tests ()
+{
+  test_single ();
+  test_single_above ();
+  test_multiple_contiguous ();
+  test_multiple_contiguous_above ();
+  test_multiple_contiguous_abutting_labels ();
+  test_multiple_contiguous_overlapping_labels ();
+  test_abutting_left_border ();
+  test_too_long_to_consolidate_vertically ();
+  test_abutting_neighbor ();
+  test_gaps ();
+  test_styled ();
+  test_borders ();
+  test_emoji ();
+}
+
+} // namespace selftest
+
+
+#endif /* #if CHECKING_P */
diff --git a/gcc/text-art/ruler.h b/gcc/text-art/ruler.h
new file mode 100644
index 0000000..31f5354
--- /dev/null
+++ b/gcc/text-art/ruler.h
@@ -0,0 +1,125 @@
+/* Classes for printing labelled rulers.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_TEXT_ART_RULER_H
+#define GCC_TEXT_ART_RULER_H
+
+#include "text-art/canvas.h"
+
+namespace text_art {
+
+/* A way to annotate a series of ranges of canvas coordinates
+   with text labels either above or, in this example, below:
+     ├───────┬───────┼───────┬───────┼───────┬───────┤
+             │               │               │
+           label A         label B          label C
+   with logic to ensure that the text labels don't overlap
+   when printed.  */
+
+class x_ruler
+{
+ public:
+  enum class label_dir { ABOVE, BELOW };
+  enum class label_kind
+  {
+    TEXT,
+    TEXT_WITH_BORDER
+  };
+
+  x_ruler (label_dir dir)
+  : m_label_dir (dir),
+    m_size (canvas::size_t (0, 0)),
+    m_has_layout (false)
+  {}
+
+  void add_label (const canvas::range_t &r,
+		  styled_string text,
+		  style::id_t style_id,
+		  label_kind kind = label_kind::TEXT);
+
+  canvas::size_t get_size ()
+  {
+    ensure_layout ();
+    return m_size;
+  }
+
+  void paint_to_canvas (canvas &canvas,
+			canvas::coord_t offset,
+			const theme &theme);
+
+  void debug (const style_manager &sm);
+
+ private:
+  /* A particular label within an x_ruler.
+     Consider e.g.:
+
+     #   x:  01234567890123456789012345678901234567890123456789
+     # y: 0: ├───────┬───────┼───────┬───────┼───────┬───────┤
+     #    1:         │               │               │
+     #    2:       label A         label B          label C
+     #
+
+     Then "label A" is:
+
+     #               m_connector_x == 8
+     #               V
+     #   x:  0123456789012
+     # y: 0:         ┬
+     #    1:         │
+     #    2:       label A
+     #   x:  0123456789012
+     #             ^
+     #             m_text_coord.x == 6
+
+     and m_text_coord is (2, 6).
+     The y cooordinates are stored with respect to label_dir::BELOW;
+     for label_dir::ABOVE we flip them when painting the ruler.  */
+  class label
+  {
+    friend class x_ruler;
+  public:
+    label (const canvas::range_t &range, styled_string text, style::id_t style_id,
+	   label_kind kind);
+
+    bool operator< (const label &other) const;
+
+  private:
+    canvas::range_t m_range;
+    styled_string m_text;
+    style::id_t m_style_id;
+    label_kind m_kind;
+    canvas::rect_t m_text_rect; // includes any border
+    int m_connector_x;
+  };
+
+  void ensure_layout ();
+  void update_layout ();
+  int get_canvas_y (int rel_y) const;
+
+  label_dir m_label_dir;
+  std::vector<label> m_labels;
+  canvas::size_t m_size;
+  bool m_has_layout = false;
+
+};
+
+} // namespace text_art
+
+#endif /* GCC_TEXT_ART_RULER_H */
diff --git a/gcc/text-art/selftests.cc b/gcc/text-art/selftests.cc
new file mode 100644
index 0000000..25d81c1
--- /dev/null
+++ b/gcc/text-art/selftests.cc
@@ -0,0 +1,78 @@
+/* Selftests for text art.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#define INCLUDE_VECTOR
+#include "system.h"
+#include "coretypes.h"
+#include "selftest.h"
+#include "pretty-print.h"
+#include "text-art/selftests.h"
+#include "text-art/canvas.h"
+
+#if CHECKING_P
+
+/* Run all tests, aborting if any fail.  */
+
+void
+selftest::text_art_tests ()
+{
+  text_art_style_cc_tests ();
+  text_art_styled_string_cc_tests ();
+
+  text_art_box_drawing_cc_tests ();
+  text_art_canvas_cc_tests ();
+  text_art_ruler_cc_tests ();
+  text_art_table_cc_tests ();
+  text_art_widget_cc_tests ();
+}
+
+/* Implementation detail of ASSERT_CANVAS_STREQ.  */
+
+void
+selftest::assert_canvas_streq (const location &loc,
+			       const text_art::canvas &canvas,
+			       pretty_printer *pp,
+			       const char *expected_str)
+{
+  canvas.print_to_pp (pp);
+  if (0)
+    fprintf (stderr, "%s\n", pp_formatted_text (pp));
+  ASSERT_STREQ_AT (loc, pp_formatted_text (pp), expected_str);
+}
+
+/* Implementation detail of ASSERT_CANVAS_STREQ.  */
+
+void
+selftest::assert_canvas_streq (const location &loc,
+			       const text_art::canvas &canvas,
+			       bool styled,
+			       const char *expected_str)
+{
+  pretty_printer pp;
+  if (styled)
+    {
+      pp_show_color (&pp) = true;
+      pp.url_format = URL_FORMAT_DEFAULT;
+    }
+  assert_canvas_streq (loc, canvas, &pp, expected_str);
+}
+
+#endif /* #if CHECKING_P */
diff --git a/gcc/text-art/selftests.h b/gcc/text-art/selftests.h
new file mode 100644
index 0000000..ba29f69
--- /dev/null
+++ b/gcc/text-art/selftests.h
@@ -0,0 +1,62 @@
+/* Copyright (C) 2023 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_TEXT_ART_SELFTESTS_H
+#define GCC_TEXT_ART_SELFTESTS_H
+
+#if CHECKING_P
+
+namespace text_art {
+  class canvas;
+} // namespace text_art
+
+namespace selftest {
+
+extern void text_art_box_drawing_cc_tests ();
+extern void text_art_canvas_cc_tests ();
+extern void text_art_ruler_cc_tests ();
+extern void text_art_style_cc_tests ();
+extern void text_art_styled_string_cc_tests ();
+extern void text_art_table_cc_tests ();
+extern void text_art_widget_cc_tests ();
+
+extern void text_art_tests ();
+
+extern void assert_canvas_streq (const location &loc,
+				 const text_art::canvas &canvas,
+				 pretty_printer *pp,
+				 const char *expected_str);
+extern void assert_canvas_streq (const location &loc,
+				 const text_art::canvas &canvas,
+				 bool styled,
+				 const char *expected_str);
+
+#define ASSERT_CANVAS_STREQ(CANVAS, STYLED, EXPECTED_STR)		\
+  SELFTEST_BEGIN_STMT							\
+    assert_canvas_streq ((SELFTEST_LOCATION),				\
+			 (CANVAS),					\
+			 (STYLED), 					\
+			 (EXPECTED_STR));				\
+  SELFTEST_END_STMT
+
+} /* end of namespace selftest.  */
+
+#endif /* #if CHECKING_P */
+
+#endif /* GCC_TEXT_ART_SELFTESTS_H */
diff --git a/gcc/text-art/style.cc b/gcc/text-art/style.cc
new file mode 100644
index 0000000..85ad49e
--- /dev/null
+++ b/gcc/text-art/style.cc
@@ -0,0 +1,633 @@
+/* Classes for styling text cells (color, URLs).
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#define INCLUDE_ALGORITHM
+#define INCLUDE_MEMORY
+#define INCLUDE_VECTOR
+#include "system.h"
+#include "coretypes.h"
+#include "make-unique.h"
+#include "pretty-print.h"
+#include "intl.h"
+#include "selftest.h"
+#include "text-art/selftests.h"
+#include "text-art/types.h"
+#include "color-macros.h"
+
+using namespace text_art;
+
+/* class text_art::style.  */
+
+style &
+style::set_style_url (const char *url)
+{
+  m_url.clear ();
+  while (*url)
+    m_url.push_back (*(url++));
+  return *this;
+}
+
+/* class text_art::style::color.  */
+
+bool
+style::color::operator== (const style::color &other) const
+{
+  if (m_kind != other.m_kind)
+    return false;
+  switch (m_kind)
+    {
+    default:
+      gcc_unreachable ();
+    case kind::NAMED:
+      return (u.m_named.m_name == other.u.m_named.m_name
+	      && u.m_named.m_bright == other.u.m_named.m_bright);
+    case kind::BITS_8:
+      return u.m_8bit == other.u.m_8bit;
+    case kind::BITS_24:
+      return (u.m_24bit.r == other.u.m_24bit.r
+	      && u.m_24bit.g == other.u.m_24bit.g
+	      && u.m_24bit.b == other.u.m_24bit.b);
+    }
+}
+
+static void
+ensure_separator (pretty_printer *pp, bool &need_separator)
+{
+  if (need_separator)
+    pp_string (pp, COLOR_SEPARATOR);
+  need_separator = true;
+}
+
+void
+style::color::print_sgr (pretty_printer *pp,
+			 bool fg,
+			 bool &need_separator) const
+{
+  switch (m_kind)
+    {
+    default:
+      gcc_unreachable ();
+    case kind::NAMED:
+      {
+	static const char * const fg_normal[] = {"", // reset, for DEFAULT
+						 COLOR_FG_BLACK,
+						 COLOR_FG_RED,
+						 COLOR_FG_GREEN,
+						 COLOR_FG_YELLOW,
+						 COLOR_FG_BLUE,
+						 COLOR_FG_MAGENTA,
+						 COLOR_FG_CYAN,
+						 COLOR_FG_WHITE};
+	static const char * const fg_bright[] = {"", // reset, for DEFAULT
+						 COLOR_FG_BRIGHT_BLACK,
+						 COLOR_FG_BRIGHT_RED,
+						 COLOR_FG_BRIGHT_GREEN,
+						 COLOR_FG_BRIGHT_YELLOW,
+						 COLOR_FG_BRIGHT_BLUE,
+						 COLOR_FG_BRIGHT_MAGENTA,
+						 COLOR_FG_BRIGHT_CYAN,
+						 COLOR_FG_BRIGHT_WHITE};
+	static const char * const bg_normal[] = {"", // reset, for DEFAULT
+						 COLOR_BG_BLACK,
+						 COLOR_BG_RED,
+						 COLOR_BG_GREEN,
+						 COLOR_BG_YELLOW,
+						 COLOR_BG_BLUE,
+						 COLOR_BG_MAGENTA,
+						 COLOR_BG_CYAN,
+						 COLOR_BG_WHITE};
+	static const char * const bg_bright[] = {"", // reset, for DEFAULT
+						 COLOR_BG_BRIGHT_BLACK,
+						 COLOR_BG_BRIGHT_RED,
+						 COLOR_BG_BRIGHT_GREEN,
+						 COLOR_BG_BRIGHT_YELLOW,
+						 COLOR_BG_BRIGHT_BLUE,
+						 COLOR_BG_BRIGHT_MAGENTA,
+						 COLOR_BG_BRIGHT_CYAN,
+						 COLOR_BG_BRIGHT_WHITE};
+	STATIC_ASSERT (ARRAY_SIZE (fg_normal) == ARRAY_SIZE (fg_bright));
+	STATIC_ASSERT (ARRAY_SIZE (fg_normal) == ARRAY_SIZE (bg_normal));
+	STATIC_ASSERT (ARRAY_SIZE (fg_normal) == ARRAY_SIZE (bg_bright));
+	gcc_assert ((size_t)u.m_named.m_name < ARRAY_SIZE (fg_normal));
+	const char *const *arr;
+	if (fg)
+	  arr = u.m_named.m_bright ? fg_bright : fg_normal;
+	else
+	  arr = u.m_named.m_bright ? bg_bright : bg_normal;
+	const char *str = arr[(size_t)u.m_named.m_name];
+	if (strlen (str) > 0)
+	  {
+	    ensure_separator (pp, need_separator);
+	    pp_string (pp, str);
+	  }
+      }
+      break;
+    case kind::BITS_8:
+      {
+	ensure_separator (pp, need_separator);
+	if (fg)
+	  pp_string (pp, "38");
+	else
+	  pp_string (pp, "48");
+	pp_printf (pp, ";5;%i", (int)u.m_8bit);
+      }
+      break;
+    case kind::BITS_24:
+      {
+	ensure_separator (pp, need_separator);
+	if (fg)
+	  pp_string (pp, "38");
+	else
+	  pp_string (pp, "48");
+	pp_printf (pp, ";2;%i;%i;%i",
+		   (int)u.m_24bit.r,
+		   (int)u.m_24bit.g,
+		   (int)u.m_24bit.b);
+      }
+      break;
+    }
+}
+
+/* class text_art::style.  */
+
+/* See https://www.ecma-international.org/wp-content/uploads/ECMA-48_5th_edition_june_1991.pdf
+   GRCM - GRAPHIC RENDITION COMBINATION MODE can be "REPLACING" or
+   "CUMULATIVE", which affects whether we need to respecify all attributes
+   at each SGR, or can accumulate them.  Looks like we can't rely on the value
+   of this, so we have to emit a single SGR for all changes, with a "0" reset
+   at the front, forcing it to be effectively replacing.  */
+
+void
+style::print_changes (pretty_printer *pp,
+		      const style &old_style,
+		      const style &new_style)
+{
+  if (pp_show_color (pp))
+    {
+      bool needs_sgr = ((old_style.m_bold != new_style.m_bold)
+			|| (old_style.m_underscore != new_style.m_underscore)
+			|| (old_style.m_blink != new_style.m_blink)
+			|| (old_style.m_fg_color != new_style.m_fg_color)
+			|| (old_style.m_bg_color != new_style.m_bg_color));
+      if (needs_sgr)
+	{
+	  bool emit_reset = (old_style.m_bold
+			     || new_style.m_bold
+			     || old_style.m_underscore
+			     || new_style.m_underscore
+			     || old_style.m_blink
+			     || new_style.m_blink);
+	  bool need_separator = false;
+
+	  pp_string (pp, SGR_START);
+	  if (emit_reset)
+	    {
+	      pp_string (pp, COLOR_NONE);
+	      need_separator = true;
+	    }
+	  if (new_style.m_bold)
+	    {
+	      gcc_assert (emit_reset);
+	      ensure_separator (pp, need_separator);
+	      pp_string (pp, COLOR_BOLD);
+	    }
+	  if (new_style.m_underscore)
+	    {
+	      gcc_assert (emit_reset);
+	      ensure_separator (pp, need_separator);
+	      pp_string (pp, COLOR_UNDERSCORE);
+	    }
+	  if (new_style.m_blink)
+	    {
+	      gcc_assert (emit_reset);
+	      ensure_separator (pp, need_separator);
+	      pp_string (pp, COLOR_BLINK);
+	    }
+	  new_style.m_fg_color.print_sgr (pp, true, need_separator);
+	  new_style.m_bg_color.print_sgr (pp, false, need_separator);
+	  pp_string (pp, SGR_END);
+	}
+    }
+
+  if (old_style.m_url != new_style.m_url)
+    {
+      if (!old_style.m_url.empty ())
+	pp_end_url (pp);
+      if (pp->url_format != URL_FORMAT_NONE
+	  && !new_style.m_url.empty ())
+	{
+	  /* Adapted from pp_begin_url, but encoding the
+	     chars to UTF-8 on the fly, rather than converting
+	     to a buffer.  */
+	  pp_string (pp, "\33]8;;");
+	  for (auto ch : new_style.m_url)
+	    pp_unicode_character (pp, ch);
+	  switch (pp->url_format)
+	    {
+	    default:
+	    case URL_FORMAT_NONE:
+	      gcc_unreachable ();
+	    case URL_FORMAT_ST:
+	      pp_string (pp, "\33\\");
+	      break;
+	    case URL_FORMAT_BEL:
+	      pp_string (pp, "\a");
+	      break;
+	    }
+	}
+    }
+}
+
+/* class text_art::style_manager.  */
+
+style_manager::style_manager ()
+{
+  // index 0 will be the default style
+  m_styles.push_back (style ());
+}
+
+style::id_t
+style_manager::get_or_create_id (const style &s)
+{
+  // For now, linear search
+  std::vector<style>::iterator existing
+    (std::find (m_styles.begin (), m_styles.end (), s));
+
+  /* If found, return index of slot.  */
+  if (existing != m_styles.end ())
+    return std::distance (m_styles.begin (), existing);
+
+  /* Not found.  */
+
+  /* styled_str uses 7 bits for style information, so we can only support
+     up to 128 different style combinations.
+     Gracefully fail by turning off styling when this limit is reached.  */
+  if (m_styles.size () >= 127)
+    return 0;
+
+  m_styles.push_back (s);
+  return m_styles.size () - 1;
+}
+
+void
+style_manager::print_any_style_changes (pretty_printer *pp,
+					style::id_t old_id,
+					style::id_t new_id) const
+{
+  gcc_assert (pp);
+  if (old_id == new_id)
+    return;
+
+  const style &old_style = m_styles[old_id];
+  const style &new_style = m_styles[new_id];
+  gcc_assert (!(old_style == new_style));
+  style::print_changes (pp, old_style, new_style);
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+void
+assert_style_change_streq (const location &loc,
+			   const style &old_style,
+			   const style &new_style,
+			   const char *expected_str)
+{
+  pretty_printer pp;
+  pp_show_color (&pp) = true;
+  style::print_changes (&pp, old_style, new_style);
+  ASSERT_STREQ_AT (loc, pp_formatted_text (&pp), expected_str);
+}
+
+#define ASSERT_STYLE_CHANGE_STREQ(OLD_STYLE, NEW_STYLE, EXPECTED_STR) \
+  SELFTEST_BEGIN_STMT						      \
+    assert_style_change_streq ((SELFTEST_LOCATION),		      \
+			       (OLD_STYLE),			      \
+			       (NEW_STYLE),			      \
+			       (EXPECTED_STR));			      \
+  SELFTEST_END_STMT
+
+static void
+test_bold ()
+{
+  style_manager sm;
+  ASSERT_EQ (sm.get_num_styles (), 1);
+
+  style plain;
+  ASSERT_EQ (sm.get_or_create_id (plain), 0);
+  ASSERT_EQ (sm.get_num_styles (), 1);
+
+  style bold;
+  bold.m_bold = true;
+
+  ASSERT_EQ (sm.get_or_create_id (bold), 1);
+  ASSERT_EQ (sm.get_num_styles (), 2);
+  ASSERT_EQ (sm.get_or_create_id (bold), 1);
+  ASSERT_EQ (sm.get_num_styles (), 2);
+
+  ASSERT_STYLE_CHANGE_STREQ (plain, bold, "\33[00;01m\33[K");
+  ASSERT_STYLE_CHANGE_STREQ (bold, plain, "\33[00m\33[K");
+}
+
+static void
+test_underscore ()
+{
+  style_manager sm;
+  ASSERT_EQ (sm.get_num_styles (), 1);
+
+  style plain;
+  ASSERT_EQ (sm.get_or_create_id (plain), 0);
+  ASSERT_EQ (sm.get_num_styles (), 1);
+
+  style underscore;
+  underscore.m_underscore = true;
+
+  ASSERT_EQ (sm.get_or_create_id (underscore), 1);
+  ASSERT_EQ (sm.get_num_styles (), 2);
+  ASSERT_EQ (sm.get_or_create_id (underscore), 1);
+  ASSERT_EQ (sm.get_num_styles (), 2);
+
+  ASSERT_STYLE_CHANGE_STREQ (plain, underscore, "\33[00;04m\33[K");
+  ASSERT_STYLE_CHANGE_STREQ (underscore, plain, "\33[00m\33[K");
+}
+
+static void
+test_blink ()
+{
+  style_manager sm;
+  ASSERT_EQ (sm.get_num_styles (), 1);
+
+  style plain;
+  ASSERT_EQ (sm.get_or_create_id (plain), 0);
+  ASSERT_EQ (sm.get_num_styles (), 1);
+
+  style blink;
+  blink.m_blink = true;
+
+  ASSERT_EQ (sm.get_or_create_id (blink), 1);
+  ASSERT_EQ (sm.get_num_styles (), 2);
+  ASSERT_EQ (sm.get_or_create_id (blink), 1);
+  ASSERT_EQ (sm.get_num_styles (), 2);
+
+  ASSERT_STYLE_CHANGE_STREQ (plain, blink, "\33[00;05m\33[K");
+  ASSERT_STYLE_CHANGE_STREQ (blink, plain, "\33[00m\33[K");
+}
+
+#define ASSERT_NAMED_COL_STREQ(NAMED_COLOR, FG, BRIGHT, EXPECTED_STR) \
+  SELFTEST_BEGIN_STMT						      \
+  {								      \
+    style plain;						      \
+    style s;							      \
+    if (FG)							      \
+      s.m_fg_color = style::color ((NAMED_COLOR), (BRIGHT));	      \
+    else							      \
+      s.m_bg_color = style::color ((NAMED_COLOR), (BRIGHT));	      \
+    assert_style_change_streq ((SELFTEST_LOCATION),		      \
+			       plain,				      \
+			       s,				      \
+			       (EXPECTED_STR));			      \
+  }								      \
+  SELFTEST_END_STMT
+
+static void
+test_named_colors ()
+{
+  /* Foreground colors.  */
+  {
+    const bool fg = true;
+    {
+      const bool bright = false;
+      ASSERT_NAMED_COL_STREQ (style::named_color::DEFAULT, fg, bright, "");
+      ASSERT_NAMED_COL_STREQ (style::named_color::BLACK, fg, bright,
+			      "");
+      ASSERT_NAMED_COL_STREQ (style::named_color::RED, fg, bright,
+			      "");
+      ASSERT_NAMED_COL_STREQ (style::named_color::GREEN, fg, bright,
+			      "");
+      ASSERT_NAMED_COL_STREQ (style::named_color::YELLOW, fg, bright,
+			      "");
+      ASSERT_NAMED_COL_STREQ (style::named_color::BLUE, fg, bright,
+			      "");
+      ASSERT_NAMED_COL_STREQ (style::named_color::MAGENTA, fg, bright,
+			      "");
+      ASSERT_NAMED_COL_STREQ (style::named_color::CYAN, fg, bright,
+			      "");
+      ASSERT_NAMED_COL_STREQ (style::named_color::WHITE, fg, bright,
+			      "");
+    }
+    {
+      const bool bright = true;
+      ASSERT_NAMED_COL_STREQ (style::named_color::DEFAULT, fg, bright,
+			      "");
+      ASSERT_NAMED_COL_STREQ (style::named_color::BLACK, fg, bright,
+			      "");
+      ASSERT_NAMED_COL_STREQ (style::named_color::RED, fg, bright,
+			      "");
+      ASSERT_NAMED_COL_STREQ (style::named_color::GREEN, fg, bright,
+			      "");
+      ASSERT_NAMED_COL_STREQ (style::named_color::YELLOW, fg, bright,
+			      "");
+      ASSERT_NAMED_COL_STREQ (style::named_color::BLUE, fg, bright,
+			      "");
+      ASSERT_NAMED_COL_STREQ (style::named_color::MAGENTA, fg, bright,
+			      "");
+      ASSERT_NAMED_COL_STREQ (style::named_color::CYAN, fg, bright,
+			      "");
+      ASSERT_NAMED_COL_STREQ (style::named_color::WHITE, fg, bright,
+			      "");
+    }
+  }
+
+  /* Background colors.  */
+  {
+    const bool fg = false;
+    {
+      const bool bright = false;
+      ASSERT_NAMED_COL_STREQ (style::named_color::DEFAULT, fg, bright, "");
+      ASSERT_NAMED_COL_STREQ (style::named_color::BLACK, fg, bright,
+			      "");
+      ASSERT_NAMED_COL_STREQ (style::named_color::RED, fg, bright,
+			      "");
+      ASSERT_NAMED_COL_STREQ (style::named_color::GREEN, fg, bright,
+			      "");
+      ASSERT_NAMED_COL_STREQ (style::named_color::YELLOW, fg, bright,
+			      "");
+      ASSERT_NAMED_COL_STREQ (style::named_color::BLUE, fg, bright,
+			      "");
+      ASSERT_NAMED_COL_STREQ (style::named_color::MAGENTA, fg, bright,
+			      "");
+      ASSERT_NAMED_COL_STREQ (style::named_color::CYAN, fg, bright,
+			      "");
+      ASSERT_NAMED_COL_STREQ (style::named_color::WHITE, fg, bright,
+			      "");
+    }
+    {
+      const bool bright = true;
+      ASSERT_NAMED_COL_STREQ (style::named_color::DEFAULT, fg, bright,
+			      "");
+      ASSERT_NAMED_COL_STREQ (style::named_color::BLACK, fg, bright,
+			      "");
+      ASSERT_NAMED_COL_STREQ (style::named_color::RED, fg, bright,
+			      "");
+      ASSERT_NAMED_COL_STREQ (style::named_color::GREEN, fg, bright,
+			      "");
+      ASSERT_NAMED_COL_STREQ (style::named_color::YELLOW, fg, bright,
+			      "");
+      ASSERT_NAMED_COL_STREQ (style::named_color::BLUE, fg, bright,
+			      "");
+      ASSERT_NAMED_COL_STREQ (style::named_color::MAGENTA, fg, bright,
+			      "");
+      ASSERT_NAMED_COL_STREQ (style::named_color::CYAN, fg, bright,
+			      "");
+      ASSERT_NAMED_COL_STREQ (style::named_color::WHITE, fg, bright,
+			      "");
+    }
+  }
+}
+
+#define ASSERT_8_BIT_COL_STREQ(COL_VAL, FG, EXPECTED_STR) \
+  SELFTEST_BEGIN_STMT						      \
+  {								      \
+    style plain;						      \
+    style s;							      \
+    if (FG)							      \
+      s.m_fg_color = style::color (COL_VAL);			      \
+    else							      \
+      s.m_bg_color = style::color (COL_VAL);			      \
+    assert_style_change_streq ((SELFTEST_LOCATION),		      \
+			       plain,				      \
+			       s,				      \
+			       (EXPECTED_STR));			      \
+  }								      \
+  SELFTEST_END_STMT
+
+static void
+test_8_bit_colors ()
+{
+  /* Foreground colors.  */
+  {
+    const bool fg = true;
+    /* 0-15: standard and high-intensity standard colors.  */
+    ASSERT_8_BIT_COL_STREQ (0, fg, "");
+    ASSERT_8_BIT_COL_STREQ (15, fg, "");
+    /* 16-231: 6x6x6 color cube.  */
+    ASSERT_8_BIT_COL_STREQ (16, fg, "");
+    ASSERT_8_BIT_COL_STREQ (231, fg, "");
+    /* 232-255: grayscale.  */
+    ASSERT_8_BIT_COL_STREQ (232, fg, "");
+    ASSERT_8_BIT_COL_STREQ (255, fg, "");
+  }
+  /* Background colors.  */
+  {
+    const bool fg = false;
+    /* 0-15: standard and high-intensity standard colors.  */
+    ASSERT_8_BIT_COL_STREQ (0, fg, "");
+    ASSERT_8_BIT_COL_STREQ (15, fg, "");
+    /* 16-231: 6x6x6 color cube.  */
+    ASSERT_8_BIT_COL_STREQ (16, fg, "");
+    ASSERT_8_BIT_COL_STREQ (231, fg, "");
+    /* 232-255: grayscale.  */
+    ASSERT_8_BIT_COL_STREQ (232, fg, "");
+    ASSERT_8_BIT_COL_STREQ (255, fg, "");
+  }
+}
+
+#define ASSERT_24_BIT_COL_STREQ(R, G, B, FG, EXPECTED_STR)	      \
+  SELFTEST_BEGIN_STMT						      \
+  {								      \
+    style plain;						      \
+    style s;							      \
+    if (FG)							      \
+      s.m_fg_color = style::color ((R), (G), (B));		      \
+    else							      \
+      s.m_bg_color = style::color ((R), (G), (B));		      \
+    assert_style_change_streq ((SELFTEST_LOCATION),		      \
+			       plain,				      \
+			       s,				      \
+			       (EXPECTED_STR));			      \
+  }								      \
+  SELFTEST_END_STMT
+
+static void
+test_24_bit_colors ()
+{
+  /* Foreground colors.  */
+  {
+    const bool fg = true;
+    // #F3FAF2:
+    ASSERT_24_BIT_COL_STREQ (0xf3, 0xfa, 0xf2, fg,
+			     "");
+  }
+  /* Background colors.  */
+  {
+    const bool fg = false;
+    // #FDF7E7
+    ASSERT_24_BIT_COL_STREQ (0xfd, 0xf7, 0xe7, fg,
+			     "");
+  }
+}
+
+static void
+test_style_combinations ()
+{
+  style_manager sm;
+  ASSERT_EQ (sm.get_num_styles (), 1);
+
+  style plain;
+  ASSERT_EQ (sm.get_or_create_id (plain), 0);
+  ASSERT_EQ (sm.get_num_styles (), 1);
+
+  style bold;
+  bold.m_bold = true;
+
+  ASSERT_EQ (sm.get_or_create_id (bold), 1);
+  ASSERT_EQ (sm.get_num_styles (), 2);
+  ASSERT_EQ (sm.get_or_create_id (bold), 1);
+  ASSERT_EQ (sm.get_num_styles (), 2);
+
+  style magenta_on_blue;
+  magenta_on_blue.m_fg_color = style::named_color::MAGENTA;
+  magenta_on_blue.m_bg_color = style::named_color::BLUE;
+  ASSERT_EQ (sm.get_or_create_id (magenta_on_blue), 2);
+  ASSERT_EQ (sm.get_num_styles (), 3);
+  ASSERT_EQ (sm.get_or_create_id (magenta_on_blue), 2);
+  ASSERT_EQ (sm.get_num_styles (), 3);
+}
+
+/* Run all selftests in this file.  */
+
+void
+text_art_style_cc_tests ()
+{
+  test_bold ();
+  test_underscore ();
+  test_blink ();
+  test_named_colors ();
+  test_8_bit_colors ();
+  test_24_bit_colors ();
+  test_style_combinations ();
+}
+
+} // namespace selftest
+
+
+#endif /* #if CHECKING_P */
diff --git a/gcc/text-art/styled-string.cc b/gcc/text-art/styled-string.cc
new file mode 100644
index 0000000..a0cc187
--- /dev/null
+++ b/gcc/text-art/styled-string.cc
@@ -0,0 +1,1108 @@
+/* Implementation of text_art::styled_string.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#define INCLUDE_MEMORY
+#define INCLUDE_VECTOR
+#include "system.h"
+#include "coretypes.h"
+#include "make-unique.h"
+#include "pretty-print.h"
+#include "intl.h"
+#include "diagnostic.h"
+#include "selftest.h"
+#include "text-art/selftests.h"
+#include "text-art/types.h"
+#include "color-macros.h"
+
+using namespace text_art;
+
+namespace {
+
+/* Support class for parsing text containing escape codes.
+   See e.g. https://en.wikipedia.org/wiki/ANSI_escape_code
+   We only support the codes that pretty-print.cc can generate.  */
+
+class escape_code_parser
+{
+public:
+  escape_code_parser (style_manager &sm,
+		      std::vector<styled_unichar> &out)
+  : m_sm (sm),
+    m_out (out),
+    m_cur_style_obj (),
+    m_cur_style_id (style::id_plain),
+    m_state (state::START)
+  {
+  }
+
+  void on_char (cppchar_t ch)
+  {
+    switch (m_state)
+      {
+      default:
+	gcc_unreachable ();
+      case state::START:
+	if (ch == '\033')
+	  {
+	    /* The start of an escape sequence.  */
+	    m_state = state::AFTER_ESC;
+	    return;
+	  }
+	break;
+      case state::AFTER_ESC:
+	if (ch == '[')
+	  {
+	    /* ESC [ is a Control Sequence Introducer.  */
+	    m_state = state::CS_PARAMETER_BYTES;
+	    return;
+	  }
+	else if (ch == ']')
+	  {
+	    /* ESC ] is an Operating System Command.  */
+	    m_state = state::WITHIN_OSC;
+	    return;
+	  }
+	break;
+      case state::CS_PARAMETER_BYTES:
+	if (parameter_byte_p (ch))
+	  {
+	    m_parameter_bytes.push_back ((char)ch);
+	    return;
+	  }
+	else if (intermediate_byte_p (ch))
+	  {
+	    m_intermediate_bytes.push_back ((char)ch);
+	    m_state = state::CS_INTERMEDIATE_BYTES;
+	    return;
+	  }
+	else if (final_byte_p (ch))
+	  {
+	    on_final_csi_char (ch);
+	    return;
+	  }
+	break;
+      case state::CS_INTERMEDIATE_BYTES:
+	/* Expect zero or more intermediate bytes.  */
+	if (intermediate_byte_p (ch))
+	  {
+	    m_intermediate_bytes.push_back ((char)ch);
+	    return;
+	  }
+	else if (final_byte_p (ch))
+	  {
+	    on_final_csi_char (ch);
+	    return;
+	  }
+	break;
+      case state::WITHIN_OSC:
+	/* Accumulate chars into m_osc_string, until we see an ST or a BEL.  */
+	{
+	  /* Check for ESC \, the String Terminator (aka "ST").  */
+	  if (ch == '\\'
+	      && m_osc_string.size () > 0
+	      && m_osc_string.back () == '\033')
+	    {
+	      m_osc_string.pop_back ();
+	      on_final_osc_char ();
+	      return;
+	    }
+	  else if (ch == '\a')
+	    {
+	      // BEL
+	      on_final_osc_char ();
+	      return;
+	    }
+	  m_osc_string.push_back (ch);
+	  return;
+	}
+	break;
+      }
+
+    /* Test of handling U+FE0F VARIATION SELECTOR-16 to select the emoji
+       variation for the previous character.  */
+    if (ch == 0xFE0F)
+      {
+	if (m_out.size () > 0)
+	  m_out.back ().set_emoji_variant ();
+	return;
+      }
+
+    if (cpp_is_combining_char (ch))
+      {
+	if (m_out.size () > 0)
+	  {
+	    m_out.back ().add_combining_char (ch);
+	    return;
+	  }
+      }
+    /* By default, add the char.  */
+    m_out.push_back (styled_unichar (ch, false, m_cur_style_id));
+  }
+
+private:
+  void on_final_csi_char (cppchar_t ch)
+  {
+    switch (ch)
+      {
+      default:
+	/* Unrecognized.  */
+	break;
+      case 'm':
+	{
+	  /* SGR control sequence.  */
+	  if (m_parameter_bytes.empty ())
+	    reset_style ();
+	  std::vector<int> params (params_from_decimal ());
+	  for (auto iter = params.begin (); iter != params.end (); )
+	    {
+	      const int param = *iter;
+	      switch (param)
+		{
+		default:
+		  /* Unrecognized SGR parameter.  */
+		  break;
+		case 0:
+		  reset_style ();
+		  break;
+		case 1:
+		  set_style_bold ();
+		  break;
+		case 4:
+		  set_style_underscore ();
+		  break;
+		case 5:
+		  set_style_blink ();
+		  break;
+
+		/* Named foreground colors.  */
+		case 30:
+		  set_style_fg_color (style::named_color::BLACK);
+		  break;
+		case 31:
+		  set_style_fg_color (style::named_color::RED);
+		  break;
+		case 32:
+		  set_style_fg_color (style::named_color::GREEN);
+		  break;
+		case 33:
+		  set_style_fg_color (style::named_color::YELLOW);
+		  break;
+		case 34:
+		  set_style_fg_color (style::named_color::BLUE);
+		  break;
+		case 35:
+		  set_style_fg_color (style::named_color::MAGENTA);
+		  break;
+		case 36:
+		  set_style_fg_color (style::named_color::CYAN);
+		  break;
+		case 37:
+		  set_style_fg_color (style::named_color::WHITE);
+		  break;
+
+		  /* 8-bit and 24-bit color */
+		case 38:
+		case 48:
+		  {
+		    const bool fg = (param == 38);
+		    iter++;
+		    if (iter != params.end ())
+		      switch (*(iter++))
+			{
+			default:
+			  break;
+			case 5:
+			  /* 8-bit color.  */
+			  if (iter != params.end ())
+			    {
+			      const uint8_t col = *(iter++);
+			      if (fg)
+				set_style_fg_color (style::color (col));
+			      else
+				set_style_bg_color (style::color (col));
+			    }
+			  continue;
+			case 2:
+			  /* 24-bit color.  */
+			  if (iter != params.end ())
+			    {
+			      const uint8_t r = *(iter++);
+			      if (iter != params.end ())
+				{
+				  const uint8_t g = *(iter++);
+				  if (iter != params.end ())
+				    {
+				      const uint8_t b = *(iter++);
+				      if (fg)
+					set_style_fg_color (style::color (r,
+									  g,
+									  b));
+				      else
+					set_style_bg_color (style::color (r,
+									  g,
+									  b));
+				    }
+				}
+			    }
+			  continue;
+			}
+		    continue;
+		  }
+		  break;
+
+		/* Named background colors.  */
+		case 40:
+		  set_style_bg_color (style::named_color::BLACK);
+		  break;
+		case 41:
+		  set_style_bg_color (style::named_color::RED);
+		  break;
+		case 42:
+		  set_style_bg_color (style::named_color::GREEN);
+		  break;
+		case 43:
+		  set_style_bg_color (style::named_color::YELLOW);
+		  break;
+		case 44:
+		  set_style_bg_color (style::named_color::BLUE);
+		  break;
+		case 45:
+		  set_style_bg_color (style::named_color::MAGENTA);
+		  break;
+		case 46:
+		  set_style_bg_color (style::named_color::CYAN);
+		  break;
+		case 47:
+		  set_style_bg_color (style::named_color::WHITE);
+		  break;
+
+		/* Named foreground colors, bright.  */
+		case 90:
+		  set_style_fg_color (style::color (style::named_color::BLACK,
+						    true));
+		  break;
+		case 91:
+		  set_style_fg_color (style::color (style::named_color::RED,
+						    true));
+		  break;
+		case 92:
+		  set_style_fg_color (style::color (style::named_color::GREEN,
+						    true));
+		  break;
+		case 93:
+		  set_style_fg_color (style::color (style::named_color::YELLOW,
+						    true));
+		  break;
+		case 94:
+		  set_style_fg_color (style::color (style::named_color::BLUE,
+						    true));
+		  break;
+		case 95:
+		  set_style_fg_color (style::color (style::named_color::MAGENTA,
+						    true));
+		  break;
+		case 96:
+		  set_style_fg_color (style::color (style::named_color::CYAN,
+						    true));
+		  break;
+		case 97:
+		  set_style_fg_color (style::color (style::named_color::WHITE,
+						    true));
+		  break;
+
+		/* Named foreground colors, bright.  */
+		case 100:
+		  set_style_bg_color (style::color (style::named_color::BLACK,
+						    true));
+		  break;
+		case 101:
+		  set_style_bg_color (style::color (style::named_color::RED,
+						    true));
+		  break;
+		case 102:
+		  set_style_bg_color (style::color (style::named_color::GREEN,
+						    true));
+		  break;
+		case 103:
+		  set_style_bg_color (style::color (style::named_color::YELLOW,
+						    true));
+		  break;
+		case 104:
+		  set_style_bg_color (style::color (style::named_color::BLUE,
+						    true));
+		  break;
+		case 105:
+		  set_style_bg_color (style::color (style::named_color::MAGENTA,
+						    true));
+		  break;
+		case 106:
+		  set_style_bg_color (style::color (style::named_color::CYAN,
+						    true));
+		  break;
+		case 107:
+		  set_style_bg_color (style::color (style::named_color::WHITE,
+						    true));
+		  break;
+		}
+	      ++iter;
+	    }
+	}
+	break;
+      }
+    m_parameter_bytes.clear ();
+    m_intermediate_bytes.clear ();
+    m_state = state::START;
+  }
+
+  void on_final_osc_char ()
+  {
+    if (!m_osc_string.empty ())
+      {
+	switch (m_osc_string[0])
+	  {
+	  default:
+	    break;
+	  case '8':
+	    /* Hyperlink support; see:
+	       https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
+	       We don't support params, so we expect either:
+	       (a) "8;;URL" to begin a url (see pp_begin_url), or
+	       (b) "8;;" to end a URL (see pp_end_url).  */
+	    if (m_osc_string.size () >= 3
+		&& m_osc_string[1] == ';'
+		&& m_osc_string[2] == ';')
+	      {
+		set_style_url (m_osc_string.begin () + 3,
+			       m_osc_string.end ());
+	      }
+	    break;
+	  }
+      }
+    m_osc_string.clear ();
+    m_state = state::START;
+  }
+
+  std::vector<int> params_from_decimal () const
+  {
+    std::vector<int> result;
+
+    int curr_int = -1;
+    for (auto param_ch : m_parameter_bytes)
+      {
+	if (param_ch >= '0' && param_ch <= '9')
+	  {
+	    if (curr_int == -1)
+	      curr_int = 0;
+	    else
+	      curr_int *= 10;
+	    curr_int += param_ch - '0';
+	  }
+	else
+	  {
+	    if (curr_int != -1)
+	      {
+		result.push_back (curr_int);
+		curr_int = -1;
+	      }
+	  }
+      }
+    if (curr_int != -1)
+      result.push_back (curr_int);
+    return result;
+  }
+
+  void refresh_style_id ()
+  {
+    m_cur_style_id = m_sm.get_or_create_id (m_cur_style_obj);
+  }
+  void reset_style ()
+  {
+    m_cur_style_obj = style ();
+    refresh_style_id ();
+  }
+  void set_style_bold ()
+  {
+    m_cur_style_obj.m_bold = true;
+    refresh_style_id ();
+  }
+  void set_style_underscore ()
+  {
+    m_cur_style_obj.m_underscore = true;
+    refresh_style_id ();
+  }
+  void set_style_blink ()
+  {
+    m_cur_style_obj.m_blink = true;
+    refresh_style_id ();
+  }
+  void set_style_fg_color (style::color color)
+  {
+    m_cur_style_obj.m_fg_color = color;
+    refresh_style_id ();
+  }
+  void set_style_bg_color (style::color color)
+  {
+    m_cur_style_obj.m_bg_color = color;
+    refresh_style_id ();
+  }
+  void set_style_url (std::vector<cppchar_t>::iterator begin,
+		      std::vector<cppchar_t>::iterator end)
+  {
+    // The empty string means "no URL"
+    m_cur_style_obj.m_url = std::vector<cppchar_t> (begin, end);
+    refresh_style_id ();
+  }
+
+  static bool parameter_byte_p (cppchar_t ch)
+  {
+    return ch >= 0x30 && ch <= 0x3F;
+  }
+
+  static bool intermediate_byte_p (cppchar_t ch)
+  {
+    return ch >= 0x20 && ch <= 0x2F;
+  }
+
+  static bool final_byte_p (cppchar_t ch)
+  {
+    return ch >= 0x40 && ch <= 0x7E;
+  }
+
+  style_manager &m_sm;
+  std::vector<styled_unichar> &m_out;
+
+  style m_cur_style_obj;
+  style::id_t m_cur_style_id;
+
+  /* Handling of control sequences.  */
+  enum class state
+  {
+   START,
+
+   /* After ESC, expecting '['.  */
+   AFTER_ESC,
+
+   /* Expecting zero or more parameter bytes, an
+      intermediate byte, or a final byte.  */
+   CS_PARAMETER_BYTES,
+
+   /* Expecting zero or more intermediate bytes, or a final byte.  */
+   CS_INTERMEDIATE_BYTES,
+
+   /* Within OSC.  */
+   WITHIN_OSC
+
+  } m_state;
+  std::vector<char> m_parameter_bytes;
+  std::vector<char> m_intermediate_bytes;
+  std::vector<cppchar_t> m_osc_string;
+};
+
+} // anon namespace
+
+/* class text_art::styled_string.  */
+
+/* Construct a styled_string from STR.
+   STR is assumed to be UTF-8 encoded and 0-terminated.
+
+   Parse SGR formatting chars from being in-band (within in the sequence
+   of chars) to being out-of-band, as style elements.
+   We only support parsing the subset of SGR chars that can be emitted
+   by pretty-print.cc   */
+
+styled_string::styled_string (style_manager &sm, const char *str)
+: m_chars ()
+{
+  escape_code_parser parser (sm, m_chars);
+
+  /* We don't actually want the display widths here, but
+     it's an easy way to decode UTF-8.  */
+  cpp_char_column_policy policy (8, cpp_wcwidth);
+  cpp_display_width_computation dw (str, strlen (str), policy);
+  while (!dw.done ())
+    {
+      cpp_decoded_char decoded_char;
+      dw.process_next_codepoint (&decoded_char);
+
+      if (!decoded_char.m_valid_ch)
+	/* Skip bytes that aren't valid UTF-8.  */
+	continue;
+
+      /* Decode SGR formatting.  */
+      cppchar_t ch = decoded_char.m_ch;
+      parser.on_char (ch);
+    }
+}
+
+styled_string::styled_string (cppchar_t cppchar, bool emoji)
+{
+  m_chars.push_back (styled_unichar (cppchar, emoji, style::id_plain));
+}
+
+styled_string
+styled_string::from_fmt_va (style_manager &sm,
+			    printer_fn format_decoder,
+			    const char *fmt,
+			    va_list *args)
+{
+  text_info text;
+  text.err_no = errno;
+  text.args_ptr = args;
+  text.format_spec = fmt;
+  pretty_printer pp;
+  pp_show_color (&pp) = true;
+  pp.url_format = URL_FORMAT_DEFAULT;
+  pp_format_decoder (&pp) = format_decoder;
+  pp_format (&pp, &text);
+  pp_output_formatted_text (&pp);
+  styled_string result (sm, pp_formatted_text (&pp));
+  return result;
+}
+
+styled_string
+styled_string::from_fmt (style_manager &sm,
+			 printer_fn format_decoder,
+			 const char *fmt, ...)
+{
+  va_list ap;
+  va_start (ap, fmt);
+  styled_string result = from_fmt_va (sm, format_decoder, fmt, &ap);
+  va_end (ap);
+  return result;
+}
+
+int
+styled_string::calc_canvas_width () const
+{
+  int result = 0;
+  for (auto ch : m_chars)
+    result += ch.get_canvas_width ();
+  return result;
+}
+
+void
+styled_string::append (const styled_string &suffix)
+{
+  m_chars.insert<std::vector<styled_unichar>::const_iterator> (m_chars.end (),
+							       suffix.begin (),
+							       suffix.end ());
+}
+
+void
+styled_string::set_url (style_manager &sm, const char *url)
+{
+  for (auto& ch : m_chars)
+    {
+      const style &existing_style = sm.get_style (ch.get_style_id ());
+      style with_url (existing_style);
+      with_url.set_style_url (url);
+      ch.m_style_id = sm.get_or_create_id (with_url);
+    }
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+static void
+test_combining_chars ()
+{
+  /* This really ought to be in libcpp, but we don't have
+     selftests there.  */
+  ASSERT_FALSE (cpp_is_combining_char (0));
+  ASSERT_FALSE (cpp_is_combining_char ('a'));
+
+  /* COMBINING BREVE (U+0306).  */
+  ASSERT_TRUE (cpp_is_combining_char (0x0306));
+
+  /* U+5B57 CJK UNIFIED IDEOGRAPH-5B57.  */
+  ASSERT_FALSE (cpp_is_combining_char (0x5B57));
+
+  /* U+FE0F VARIATION SELECTOR-16.  */
+  ASSERT_FALSE (cpp_is_combining_char (0xFE0F));
+}
+
+static void
+test_empty ()
+{
+  style_manager sm;
+  styled_string s (sm, "");
+  ASSERT_EQ (s.size (), 0);
+  ASSERT_EQ (s.calc_canvas_width (), 0);
+}
+
+/* Test of a pure ASCII string with no escape codes.  */
+
+static void
+test_simple ()
+{
+  const char *c_str = "hello world!";
+  style_manager sm;
+  styled_string s (sm, c_str);
+  ASSERT_EQ (s.size (), strlen (c_str));
+  ASSERT_EQ (s.calc_canvas_width (), (int)strlen (c_str));
+  for (size_t i = 0; i < strlen (c_str); i++)
+    {
+      ASSERT_EQ (s[i].get_code (), (cppchar_t)c_str[i]);
+      ASSERT_EQ (s[i].get_style_id (), 0);
+    }
+}
+
+/* Test of decoding UTF-8.  */
+
+static void
+test_pi_from_utf8 ()
+{
+  /* U+03C0 "GREEK SMALL LETTER PI".  */
+  const char * const pi_utf8 = "\xCF\x80";
+
+  style_manager sm;
+  styled_string s (sm, pi_utf8);
+  ASSERT_EQ (s.size (), 1);
+  ASSERT_EQ (s.calc_canvas_width (), 1);
+  ASSERT_EQ (s[0].get_code (), 0x03c0);
+  ASSERT_EQ (s[0].emoji_variant_p (), false);
+  ASSERT_EQ (s[0].double_width_p (), false);
+  ASSERT_EQ (s[0].get_style_id (), 0);
+}
+
+/* Test of double-width character.  */
+
+static void
+test_emoji_from_utf8 ()
+{
+  /* U+1F642 "SLIGHTLY SMILING FACE".  */
+  const char * const emoji_utf8 = "\xF0\x9F\x99\x82";
+
+  style_manager sm;
+  styled_string s (sm, emoji_utf8);
+  ASSERT_EQ (s.size (), 1);
+  ASSERT_EQ (s.calc_canvas_width (), 2);
+  ASSERT_EQ (s[0].get_code (), 0x1f642);
+  ASSERT_EQ (s[0].double_width_p (), true);
+  ASSERT_EQ (s[0].get_style_id (), 0);
+}
+
+/* Test of handling U+FE0F VARIATION SELECTOR-16 to select the emoji
+   variation for the previous character.  */
+
+static void
+test_emoji_variant_from_utf8 ()
+{
+  const char * const emoji_utf8
+    = (/* U+26A0 WARNING SIGN.  */
+       "\xE2\x9A\xA0"
+       /* U+FE0F VARIATION SELECTOR-16 (emoji variation selector).  */
+       "\xEF\xB8\x8F");
+
+  style_manager sm;
+  styled_string s (sm, emoji_utf8);
+  ASSERT_EQ (s.size (), 1);
+  ASSERT_EQ (s.calc_canvas_width (), 1);
+  ASSERT_EQ (s[0].get_code (), 0x26a0);
+  ASSERT_EQ (s[0].emoji_variant_p (), true);
+  ASSERT_EQ (s[0].double_width_p (), false);
+  ASSERT_EQ (s[0].get_style_id (), 0);
+}
+
+static void
+test_emoji_from_codepoint ()
+{
+  styled_string s ((cppchar_t)0x1f642);
+  ASSERT_EQ (s.size (), 1);
+  ASSERT_EQ (s.calc_canvas_width (), 2);
+  ASSERT_EQ (s[0].get_code (), 0x1f642);
+  ASSERT_EQ (s[0].double_width_p (), true);
+  ASSERT_EQ (s[0].get_style_id (), 0);
+}
+
+static void
+test_from_mixed_width_utf8 ()
+{
+  /* This UTF-8 string literal is of the form
+     before mojibake after
+   where the Japanese word "mojibake" is written as the following
+   four unicode code points:
+     U+6587 CJK UNIFIED IDEOGRAPH-6587
+     U+5B57 CJK UNIFIED IDEOGRAPH-5B57
+     U+5316 CJK UNIFIED IDEOGRAPH-5316
+     U+3051 HIRAGANA LETTER KE.
+   Each of these is 3 bytes wide when encoded in UTF-8, whereas the
+   "before" and "after" are 1 byte per unicode character.  */
+  const char * const mixed_width_utf8
+    = ("before "
+
+       /* U+6587 CJK UNIFIED IDEOGRAPH-6587
+	  UTF-8: 0xE6 0x96 0x87
+	  C octal escaped UTF-8: \346\226\207.  */
+       "\346\226\207"
+
+       /* U+5B57 CJK UNIFIED IDEOGRAPH-5B57
+	  UTF-8: 0xE5 0xAD 0x97
+	  C octal escaped UTF-8: \345\255\227.  */
+       "\345\255\227"
+
+       /* U+5316 CJK UNIFIED IDEOGRAPH-5316
+	  UTF-8: 0xE5 0x8C 0x96
+	  C octal escaped UTF-8: \345\214\226.  */
+       "\345\214\226"
+
+       /* U+3051 HIRAGANA LETTER KE
+	  UTF-8: 0xE3 0x81 0x91
+	  C octal escaped UTF-8: \343\201\221.  */
+       "\343\201\221"
+
+       " after");
+
+  style_manager sm;
+  styled_string s (sm, mixed_width_utf8);
+  ASSERT_EQ (s.size (), 6 + 1 + 4 + 1 + 5);
+  ASSERT_EQ (sm.get_num_styles (), 1);
+
+  // We expect the Japanese characters to be double width.
+  ASSERT_EQ (s.calc_canvas_width (), 6 + 1 + (2 * 4) + 1 + 5);
+
+  ASSERT_EQ (s[0].get_code (), 'b');
+  ASSERT_EQ (s[0].double_width_p (), false);
+  ASSERT_EQ (s[1].get_code (), 'e');
+  ASSERT_EQ (s[2].get_code (), 'f');
+  ASSERT_EQ (s[3].get_code (), 'o');
+  ASSERT_EQ (s[4].get_code (), 'r');
+  ASSERT_EQ (s[5].get_code (), 'e');
+  ASSERT_EQ (s[6].get_code (), ' ');
+  ASSERT_EQ (s[7].get_code (), 0x6587);
+  ASSERT_EQ (s[7].double_width_p (), true);
+  ASSERT_EQ (s[8].get_code (), 0x5B57);
+  ASSERT_EQ (s[9].get_code (), 0x5316);
+  ASSERT_EQ (s[10].get_code (), 0x3051);
+  ASSERT_EQ (s[11].get_code (), ' ');
+  ASSERT_EQ (s[12].get_code (), 'a');
+  ASSERT_EQ (s[13].get_code (), 'f');
+  ASSERT_EQ (s[14].get_code (), 't');
+  ASSERT_EQ (s[15].get_code (), 'e');
+  ASSERT_EQ (s[16].get_code (), 'r');
+
+  ASSERT_EQ (s[0].get_style_id (), 0);
+}
+
+static void
+assert_style_urleq (const location &loc,
+		    const style &s,
+		    const char *expected_str)
+{
+  ASSERT_EQ_AT (loc, s.m_url.size (), strlen (expected_str));
+  for (size_t i = 0; i < s.m_url.size (); i++)
+    ASSERT_EQ_AT (loc, s.m_url[i], (cppchar_t)expected_str[i]);
+}
+
+#define ASSERT_STYLE_URLEQ(STYLE, EXPECTED_STR) \
+  assert_style_urleq ((SELFTEST_LOCATION), (STYLE), (EXPECTED_STR))
+
+static void
+test_url ()
+{
+  // URL_FORMAT_ST
+  {
+    style_manager sm;
+    styled_string s
+      (sm, "\33]8;;http://example.com\33\\This is a link\33]8;;\33\\");
+    const char *expected = "This is a link";
+    ASSERT_EQ (s.size (), strlen (expected));
+    ASSERT_EQ (s.calc_canvas_width (), (int)strlen (expected));
+    ASSERT_EQ (sm.get_num_styles (), 2);
+    for (size_t i = 0; i < strlen (expected); i++)
+      {
+	ASSERT_EQ (s[i].get_code (), (cppchar_t)expected[i]);
+	ASSERT_EQ (s[i].get_style_id (), 1);
+      }
+    ASSERT_STYLE_URLEQ (sm.get_style (1), "http://example.com");
+  }
+
+  // URL_FORMAT_BEL
+  {
+    style_manager sm;
+    styled_string s
+      (sm, "\33]8;;http://example.com\aThis is a link\33]8;;\a");
+    const char *expected = "This is a link";
+    ASSERT_EQ (s.size (), strlen (expected));
+    ASSERT_EQ (s.calc_canvas_width (), (int)strlen (expected));
+    ASSERT_EQ (sm.get_num_styles (), 2);
+    for (size_t i = 0; i < strlen (expected); i++)
+      {
+	ASSERT_EQ (s[i].get_code (), (cppchar_t)expected[i]);
+	ASSERT_EQ (s[i].get_style_id (), 1);
+      }
+    ASSERT_STYLE_URLEQ (sm.get_style (1), "http://example.com");
+  }
+}
+
+static void
+test_from_fmt ()
+{
+  style_manager sm;
+  styled_string s (styled_string::from_fmt (sm, NULL, "%%i: %i", 42));
+  ASSERT_EQ (s[0].get_code (), '%');
+  ASSERT_EQ (s[1].get_code (), 'i');
+  ASSERT_EQ (s[2].get_code (), ':');
+  ASSERT_EQ (s[3].get_code (), ' ');
+  ASSERT_EQ (s[4].get_code (), '4');
+  ASSERT_EQ (s[5].get_code (), '2');
+  ASSERT_EQ (s.size (), 6);
+  ASSERT_EQ (s.calc_canvas_width (), 6);
+}
+
+static void
+test_from_fmt_qs ()
+{
+  auto_fix_quotes fix_quotes;
+  open_quote = "\xe2\x80\x98";
+  close_quote = "\xe2\x80\x99";
+
+  style_manager sm;
+  styled_string s (styled_string::from_fmt (sm, NULL, "%qs", "msg"));
+  ASSERT_EQ (sm.get_num_styles (), 2);
+  ASSERT_EQ (s[0].get_code (), 0x2018);
+  ASSERT_EQ (s[0].get_style_id (), 0);
+  ASSERT_EQ (s[1].get_code (), 'm');
+  ASSERT_EQ (s[1].get_style_id (), 1);
+  ASSERT_EQ (s[2].get_code (), 's');
+  ASSERT_EQ (s[2].get_style_id (), 1);
+  ASSERT_EQ (s[3].get_code (), 'g');
+  ASSERT_EQ (s[3].get_style_id (), 1);
+  ASSERT_EQ (s[4].get_code (), 0x2019);
+  ASSERT_EQ (s[4].get_style_id (), 0);
+  ASSERT_EQ (s.size (), 5);
+}
+
+// Test of parsing SGR codes.
+
+static void
+test_from_str_with_bold ()
+{
+  style_manager sm;
+  /* This is the result of pp_printf (pp, "%qs", "foo")
+     with auto_fix_quotes.  */
+  styled_string s (sm, "`\33[01m\33[Kfoo\33[m\33[K'");
+  ASSERT_EQ (s[0].get_code (), '`');
+  ASSERT_EQ (s[0].get_style_id (), 0);
+  ASSERT_EQ (s[1].get_code (), 'f');
+  ASSERT_EQ (s[1].get_style_id (), 1);
+  ASSERT_EQ (s[2].get_code (), 'o');
+  ASSERT_EQ (s[2].get_style_id (), 1);
+  ASSERT_EQ (s[3].get_code (), 'o');
+  ASSERT_EQ (s[3].get_style_id (), 1);
+  ASSERT_EQ (s[4].get_code (), '\'');
+  ASSERT_EQ (s[4].get_style_id (), 0);
+  ASSERT_EQ (s.size (), 5);
+  ASSERT_TRUE (sm.get_style (1).m_bold);
+}
+
+static void
+test_from_str_with_underscore ()
+{
+  style_manager sm;
+  styled_string s (sm, "\33[04m\33[KA");
+  ASSERT_EQ (s[0].get_code (), 'A');
+  ASSERT_EQ (s[0].get_style_id (), 1);
+  ASSERT_TRUE (sm.get_style (1).m_underscore);
+}
+
+static void
+test_from_str_with_blink ()
+{
+  style_manager sm;
+  styled_string s (sm, "\33[05m\33[KA");
+  ASSERT_EQ (s[0].get_code (), 'A');
+  ASSERT_EQ (s[0].get_style_id (), 1);
+  ASSERT_TRUE (sm.get_style (1).m_blink);
+}
+
+// Test of parsing SGR codes.
+
+static void
+test_from_str_with_color ()
+{
+  style_manager sm;
+
+  styled_string s (sm,
+		   ("0"
+		    SGR_SEQ (COLOR_FG_RED)
+		    "R"
+		    SGR_RESET
+		    "2"
+		    SGR_SEQ (COLOR_FG_GREEN)
+		    "G"
+		    SGR_RESET
+		    "4"));
+  ASSERT_EQ (s.size (), 5);
+  ASSERT_EQ (sm.get_num_styles (), 3);
+  ASSERT_EQ (s[0].get_code (), '0');
+  ASSERT_EQ (s[0].get_style_id (), 0);
+  ASSERT_EQ (s[1].get_code (), 'R');
+  ASSERT_EQ (s[1].get_style_id (), 1);
+  ASSERT_EQ (s[2].get_code (), '2');
+  ASSERT_EQ (s[2].get_style_id (), 0);
+  ASSERT_EQ (s[3].get_code (), 'G');
+  ASSERT_EQ (s[3].get_style_id (), 2);
+  ASSERT_EQ (s[4].get_code (), '4');
+  ASSERT_EQ (s[4].get_style_id (), 0);
+  ASSERT_EQ (sm.get_style (1).m_fg_color, style::named_color::RED);
+  ASSERT_EQ (sm.get_style (2).m_fg_color, style::named_color::GREEN);
+}
+
+static void
+test_from_str_with_named_color ()
+{
+  style_manager sm;
+  styled_string s (sm,
+		   ("F"
+		    SGR_SEQ (COLOR_FG_BLACK) "F"
+		    SGR_SEQ (COLOR_FG_RED) "F"
+		    SGR_SEQ (COLOR_FG_GREEN) "F"
+		    SGR_SEQ (COLOR_FG_YELLOW) "F"
+		    SGR_SEQ (COLOR_FG_BLUE) "F"
+		    SGR_SEQ (COLOR_FG_MAGENTA) "F"
+		    SGR_SEQ (COLOR_FG_CYAN) "F"
+		    SGR_SEQ (COLOR_FG_WHITE) "F"
+		    SGR_SEQ (COLOR_FG_BRIGHT_BLACK) "F"
+		    SGR_SEQ (COLOR_FG_BRIGHT_RED) "F"
+		    SGR_SEQ (COLOR_FG_BRIGHT_GREEN) "F"
+		    SGR_SEQ (COLOR_FG_BRIGHT_YELLOW) "F"
+		    SGR_SEQ (COLOR_FG_BRIGHT_BLUE) "F"
+		    SGR_SEQ (COLOR_FG_BRIGHT_MAGENTA) "F"
+		    SGR_SEQ (COLOR_FG_BRIGHT_CYAN) "F"
+		    SGR_SEQ (COLOR_FG_BRIGHT_WHITE) "F"
+		    SGR_SEQ (COLOR_BG_BLACK) "B"
+		    SGR_SEQ (COLOR_BG_RED) "B"
+		    SGR_SEQ (COLOR_BG_GREEN) "B"
+		    SGR_SEQ (COLOR_BG_YELLOW) "B"
+		    SGR_SEQ (COLOR_BG_BLUE) "B"
+		    SGR_SEQ (COLOR_BG_MAGENTA) "B"
+		    SGR_SEQ (COLOR_BG_CYAN) "B"
+		    SGR_SEQ (COLOR_BG_WHITE) "B"
+		    SGR_SEQ (COLOR_BG_BRIGHT_BLACK) "B"
+		    SGR_SEQ (COLOR_BG_BRIGHT_RED) "B"
+		    SGR_SEQ (COLOR_BG_BRIGHT_GREEN) "B"
+		    SGR_SEQ (COLOR_BG_BRIGHT_YELLOW) "B"
+		    SGR_SEQ (COLOR_BG_BRIGHT_BLUE) "B"
+		    SGR_SEQ (COLOR_BG_BRIGHT_MAGENTA) "B"
+		    SGR_SEQ (COLOR_BG_BRIGHT_CYAN) "B"
+		    SGR_SEQ (COLOR_BG_BRIGHT_WHITE) "B"));
+  ASSERT_EQ (s.size (), 33);
+  for (size_t i = 0; i < s.size (); i++)
+    ASSERT_EQ (s[i].get_style_id (), i);
+  for (size_t i = 0; i < 17; i++)
+    ASSERT_EQ (s[i].get_code (), 'F');
+  for (size_t i = 17; i < 33; i++)
+    ASSERT_EQ (s[i].get_code (), 'B');
+}
+
+static void
+test_from_str_with_8_bit_color ()
+{
+  {
+    style_manager sm;
+    styled_string s (sm,
+		     ("F"));
+    ASSERT_EQ (s.size (), 1);
+    ASSERT_EQ (s[0].get_code (), 'F');
+    ASSERT_EQ (s[0].get_style_id (), 1);
+    ASSERT_EQ (sm.get_style (1).m_fg_color, style::color (232));
+  }
+  {
+    style_manager sm;
+    styled_string s (sm,
+		     ("B"));
+    ASSERT_EQ (s.size (), 1);
+    ASSERT_EQ (s[0].get_code (), 'B');
+    ASSERT_EQ (s[0].get_style_id (), 1);
+    ASSERT_EQ (sm.get_style (1).m_bg_color, style::color (231));
+  }
+}
+
+static void
+test_from_str_with_24_bit_color ()
+{
+  {
+    style_manager sm;
+    styled_string s (sm,
+		     ("F"));
+    ASSERT_EQ (s.size (), 1);
+    ASSERT_EQ (s[0].get_code (), 'F');
+    ASSERT_EQ (s[0].get_style_id (), 1);
+    ASSERT_EQ (sm.get_style (1).m_fg_color, style::color (243, 250, 242));
+  }
+  {
+    style_manager sm;
+    styled_string s (sm,
+		     ("B"));
+    ASSERT_EQ (s.size (), 1);
+    ASSERT_EQ (s[0].get_code (), 'B');
+    ASSERT_EQ (s[0].get_style_id (), 1);
+    ASSERT_EQ (sm.get_style (1).m_bg_color, style::color (253, 247, 231));
+  }
+}
+
+static void
+test_from_str_combining_characters ()
+{
+  style_manager sm;
+  styled_string s (sm,
+		   /* CYRILLIC CAPITAL LETTER U (U+0423).  */
+		   "\xD0\xA3"
+		   /* COMBINING BREVE (U+0306).  */
+		   "\xCC\x86");
+  ASSERT_EQ (s.size (), 1);
+  ASSERT_EQ (s[0].get_code (), 0x423);
+  ASSERT_EQ (s[0].get_combining_chars ().size (), 1);
+  ASSERT_EQ (s[0].get_combining_chars ()[0], 0x306);
+}
+
+/* Run all selftests in this file.  */
+
+void
+text_art_styled_string_cc_tests ()
+{
+  test_combining_chars ();
+  test_empty ();
+  test_simple ();
+  test_pi_from_utf8 ();
+  test_emoji_from_utf8 ();
+  test_emoji_variant_from_utf8 ();
+  test_emoji_from_codepoint ();
+  test_from_mixed_width_utf8 ();
+  test_url ();
+  test_from_fmt ();
+  test_from_fmt_qs ();
+  test_from_str_with_bold ();
+  test_from_str_with_underscore ();
+  test_from_str_with_blink ();
+  test_from_str_with_color ();
+  test_from_str_with_named_color ();
+  test_from_str_with_8_bit_color ();
+  test_from_str_with_24_bit_color ();
+  test_from_str_combining_characters ();
+}
+
+} // namespace selftest
+
+
+#endif /* #if CHECKING_P */
diff --git a/gcc/text-art/table.cc b/gcc/text-art/table.cc
new file mode 100644
index 0000000..71a1024
--- /dev/null
+++ b/gcc/text-art/table.cc
@@ -0,0 +1,1273 @@
+/* Support for tabular/grid-based content.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#define INCLUDE_MEMORY
+#define INCLUDE_VECTOR
+#include "system.h"
+#include "coretypes.h"
+#include "make-unique.h"
+#include "pretty-print.h"
+#include "diagnostic.h"
+#include "selftest.h"
+#include "text-art/selftests.h"
+#include "text-art/table.h"
+
+using namespace text_art;
+
+/* class text_art::table_cell_content.  */
+
+table_cell_content::table_cell_content (styled_string &&s)
+: m_str (std::move (s)),
+  /* We assume here that the content occupies a single canvas row.  */
+  m_size (m_str.calc_canvas_width (), 1)
+{
+}
+
+void
+table_cell_content::paint_to_canvas (canvas &canvas,
+				     canvas::coord_t top_left) const
+{
+  canvas.paint_text (top_left, m_str);
+}
+
+/* struct text_art::table_dimension_sizes.  */
+
+table_dimension_sizes::table_dimension_sizes (unsigned num)
+: m_requirements (num, 0)
+{
+}
+
+/* class text_art::table::cell_placement.  */
+
+void
+table::cell_placement::paint_cell_contents_to_canvas(canvas &canvas,
+						     canvas::coord_t offset,
+						     const table_geometry &tg) const
+{
+  const canvas::size_t req_canvas_size = get_min_canvas_size ();
+  const canvas::size_t alloc_canvas_size = tg.get_canvas_size (m_rect);
+  gcc_assert (req_canvas_size.w <= alloc_canvas_size.w);
+  gcc_assert (req_canvas_size.h <= alloc_canvas_size.h);
+  const int x_padding = alloc_canvas_size.w - req_canvas_size.w;
+  const int y_padding = alloc_canvas_size.h - req_canvas_size.h;
+  const table::coord_t table_top_left = m_rect.m_top_left;
+  const canvas::coord_t canvas_top_left = tg.table_to_canvas (table_top_left);
+
+  gcc_assert (x_padding >= 0);
+  int x_align_offset;
+  switch (m_x_align)
+    {
+    default:
+      gcc_unreachable ();
+    case x_align::LEFT:
+      x_align_offset = 0;
+      break;
+    case x_align::CENTER:
+      x_align_offset = x_padding / 2;
+      break;
+    case x_align::RIGHT:
+      x_align_offset = x_padding;
+      break;
+    }
+
+  gcc_assert (y_padding >= 0);
+  int y_align_offset;
+  switch (m_y_align)
+    {
+    default:
+      gcc_unreachable ();
+    case y_align::TOP:
+      y_align_offset = 0;
+      break;
+    case y_align::CENTER:
+      y_align_offset = y_padding / 2;
+      break;
+    case y_align::BOTTOM:
+      y_align_offset = y_padding;
+      break;
+    }
+  const canvas::coord_t content_rel_coord
+    (canvas_top_left.x + 1 + x_align_offset,
+     canvas_top_left.y + 1 + y_align_offset);
+  m_content.paint_to_canvas (canvas, offset + content_rel_coord);
+}
+
+/* class text_art::table.  */
+
+
+table::table (size_t size)
+: m_size (size),
+  m_placements (),
+  m_occupancy (size)
+{
+  m_occupancy.fill (-1);
+}
+
+void
+table::set_cell (coord_t coord,
+		 table_cell_content &&content,
+		 enum x_align x_align,
+		 enum y_align y_align)
+{
+  set_cell_span (rect_t (coord, table::size_t (1, 1)),
+		 std::move (content), x_align, y_align);
+}
+
+void
+table::set_cell_span (rect_t span,
+		      table_cell_content &&content,
+		      enum x_align x_align,
+		      enum y_align y_align)
+{
+  gcc_assert (span.m_size.w > 0);
+  gcc_assert (span.m_size.h > 0);
+  int placement_idx = m_placements.size ();
+  m_placements.emplace_back (cell_placement (span, std::move (content),
+					     x_align, y_align));
+  for (int y = span.get_min_y (); y < span.get_next_y (); y++)
+    for (int x = span.get_min_x (); x < span.get_next_x (); x++)
+      {
+	gcc_assert (m_occupancy.get (coord_t (x, y)) == -1);
+	m_occupancy.set (coord_t (x, y), placement_idx);
+      }
+}
+
+canvas
+table::to_canvas (const theme &theme, const style_manager &sm) const
+{
+  table_dimension_sizes col_widths (m_size.w);
+  table_dimension_sizes row_heights (m_size.h);
+  table_cell_sizes cell_sizes (col_widths, row_heights);
+  cell_sizes.pass_1 (*this);
+  cell_sizes.pass_2 (*this);
+  table_geometry tg (*this, cell_sizes);
+  canvas canvas (tg.get_canvas_size (), sm);
+  paint_to_canvas (canvas, canvas::coord_t (0, 0), tg, theme);
+  return canvas;
+}
+
+void
+table::paint_to_canvas (canvas &canvas,
+			canvas::coord_t offset,
+			const table_geometry &tg,
+			const theme &theme) const
+{
+  canvas.fill (canvas::rect_t (offset, tg.get_canvas_size ()),
+	       styled_unichar (' '));
+  paint_cell_borders_to_canvas (canvas, offset, tg, theme);
+  paint_cell_contents_to_canvas (canvas, offset, tg);
+}
+
+/* Print this table to stderr.  */
+
+DEBUG_FUNCTION void
+table::debug () const
+{
+  /* Use a temporary style manager.
+     Styles in the table will be meaningless, so
+     print the canvas with styling disabled.  */
+  style_manager sm;
+  canvas canvas (to_canvas (unicode_theme (), sm));
+  canvas.debug (false);
+}
+
+const table::cell_placement *
+table::get_placement_at (coord_t coord) const
+{
+  const int placement_idx = m_occupancy.get (coord);
+  if (placement_idx == -1)
+    return nullptr;
+  return &m_placements[placement_idx];
+}
+
+int
+table::get_occupancy_safe (coord_t coord) const
+{
+  if (coord.x < 0)
+    return -1;
+  if (coord.x >= m_size.w)
+    return -1;
+  if (coord.y < 0)
+    return -1;
+  if (coord.y >= m_size.h)
+    return -1;
+  return m_occupancy.get (coord);
+}
+
+/* Determine if the "?" edges need borders for table cell D
+   in the following, for the directions relative to "X", based
+   on whether each of table cell boundaries AB, CD, AC, and BD
+   are boundaries between cell spans:
+
+   #            up?
+   #      +-----+-----+
+   #      |           |
+   #      |     ?     |
+   #      |  A  ?  B  |
+   #      |     ?     |
+   #      |           |
+   # left?+ ??? X ??? + right?
+   #      |           |
+   #      |     ?     |
+   #      |  C  ?  D  |
+   #      |     ?     |
+   #      |           |
+   #      +-----+-----+
+   #          down?
+*/
+
+directions
+table::get_connections (int table_x, int table_y) const
+{
+  int cell_a = get_occupancy_safe (coord_t (table_x - 1, table_y - 1));
+  int cell_b = get_occupancy_safe (coord_t (table_x, table_y - 1));
+  int cell_c = get_occupancy_safe (coord_t (table_x - 1, table_y));
+  int cell_d = get_occupancy_safe (coord_t (table_x, table_y));
+  const bool up = (cell_a != cell_b);
+  const bool down = (cell_c != cell_d);
+  const bool left = (cell_a != cell_c);
+  const bool right = (cell_b != cell_d);
+  return directions (up, down, left, right);
+}
+
+/* Paint the grid lines.
+
+   Consider painting
+   - a grid of cells,
+   - plus a right-hand border
+   - and a bottom border
+
+   Then we need to paint to the canvas like this:
+
+   #         PER-TABLE-COLUMN     R BORDER
+   #      +-------------------+   +-----+
+   #
+   #             TABLE CELL WIDTH (in canvas units)
+   #            +-------------+
+   #      .     .     . .     .   .     .
+   #   ...+-----+-----+.+-----+...+-----+ +
+   #      |  U  |     |.|     |   |  U  | |
+   #      |  U  |     |.|     |   |  U  | |
+   #      |LL+RR|RRRRR|.|RRRRR|   |LL+  | |
+   #      |  D  |     |.|     |   |  D  | |
+   #      |  D  |     |.|     |   |  D  | |
+   #   ...+-----+-----+.+-----+...+-----+ |
+   #      .....................   ......  +-- PER-TABLE-ROW
+   #   ...+-----+-----+.+-----+...+-----+ | +
+   #      |  D  |     |.|     |   |  D  | | |
+   #      |  D  |     |.|     |   |  D  | | |
+   #      |  D  |     |.|     |   |  D  | | +---- TABLE CELL HEIGHT (in canvas units)
+   #      |  D  |     |.|     |   |  D  | | |
+   #      |  D  |     |.|     |   |  D  | | |
+   #   ...+-----+-----+.+-----+...+-----+ + +
+   #      .     .     .     .   .     .
+   #   ...+-----+-----+.+-----+...+-----+  +
+   #      |  D  |     |.|     |   |  U  |  |
+   #      |  D  |     |.|     |   |  U  |  |
+   #      |LL+RR|RRRRR|.|RRRRR|   |LL+  |  | BOTTOM BORDER
+   #      |     |     |.|     |   |     |  |
+   #      |     |     |.|     |   |     |  |
+   #   ...+-----+-----+.+-----+...+-----+  +
+
+   where each:
+
+   #    +-----+
+   #    |     |
+   #    |     |
+   #    |     |
+   #    |     |
+   #    |     |
+   #    +-----+
+
+   is a canvas cell, and the U, L, R, D express the connections
+   that are present with neighboring table cells.  These affect
+   the kinds of borders that we draw for a particular table cell.  */
+
+void
+table::paint_cell_borders_to_canvas (canvas &canvas,
+				     canvas::coord_t offset,
+				     const table_geometry &tg,
+				     const theme &theme) const
+{
+  /* The per-table-cell left and top borders are either drawn or not,
+     but if they are, they aren't affected by per-table-cell connections.  */
+  const canvas::cell_t left_border
+    = theme.get_line_art (directions (true, /* up */
+				      true, /* down */
+				      false, /* left */
+				      false /* right */));
+  const canvas::cell_t top_border
+    = theme.get_line_art (directions (false, /* up */
+				      false, /* down */
+				      true, /* left */
+				      true)); /* right */
+  for (int table_y = 0; table_y < m_size.h; table_y++)
+    {
+      const int canvas_y = tg.table_y_to_canvas_y (table_y);
+      for (int table_x = 0; table_x < m_size.w; table_x++)
+	{
+	  canvas::coord_t canvas_top_left
+	    = tg.table_to_canvas(table::coord_t (table_x, table_y));
+
+	  const directions c (get_connections (table_x, table_y));
+
+	  /* Paint top-left corner of border, if any.  */
+	  canvas.paint (offset + canvas_top_left,
+			theme.get_line_art (c));
+
+	  /* Paint remainder of left border of cell, if any.
+	     We assume here that the content occupies a single canvas row.  */
+	  if (c.m_down)
+	    canvas.paint (offset + canvas::coord_t (canvas_top_left.x,
+						    canvas_y + 1),
+			  left_border);
+
+	  /* Paint remainder of top border of cell, if any.  */
+	  if (c.m_right)
+	    {
+	      const int col_width = tg.get_col_width (table_x);
+	      for (int x_offset = 0; x_offset < col_width; x_offset++)
+		{
+		  const int canvas_x = canvas_top_left.x + 1 + x_offset;
+		  canvas.paint (offset + canvas::coord_t (canvas_x, canvas_y),
+				top_border);
+		}
+	    }
+	}
+
+      /* Paint right-hand border of row.  */
+      const int table_x = m_size.w;
+      const int canvas_x = tg.table_x_to_canvas_x (table_x);
+      const directions c (get_connections (m_size.w, table_y));
+      canvas.paint(offset + canvas::coord_t (canvas_x, canvas_y),
+		   theme.get_line_art (directions (c.m_up,
+						   c.m_down,
+						   c.m_left,
+						   false))); /* right */
+      /* We assume here that the content occupies a single canvas row.  */
+      canvas.paint(offset + canvas::coord_t (canvas_x, canvas_y + 1),
+		   theme.get_line_art (directions (c.m_down, /* up */
+						   c.m_down, /* down */
+						   false, /* left */
+						   false))); /* right */
+    }
+
+  /* Draw bottom border of table.  */
+  {
+    const int canvas_y = tg.get_canvas_size ().h - 1;
+    for (int table_x = 0; table_x < m_size.w; table_x++)
+      {
+	const directions c (get_connections (table_x, m_size.h));
+	const int left_canvas_x = tg.table_x_to_canvas_x (table_x);
+	canvas.paint (offset + canvas::coord_t (left_canvas_x, canvas_y),
+		      theme.get_line_art (directions (c.m_up,
+						      false, /* down */
+						      c.m_left,
+						      c.m_right)));
+	const int col_width = tg.get_col_width (table_x);
+	for (int x_offset = 0; x_offset < col_width; x_offset++)
+	  {
+	    const int canvas_x = left_canvas_x + 1 + x_offset;
+	    canvas.paint(offset + canvas::coord_t (canvas_x, canvas_y),
+			 theme.get_line_art (directions (false, // up
+							 false, // down
+							 c.m_right, // left
+							 c.m_right))); // right
+	  }
+      }
+
+    /* Bottom-right corner of table.  */
+    const int table_x = m_size.w;
+    const int canvas_x = tg.table_x_to_canvas_x (table_x);
+    const directions c (get_connections (m_size.w, m_size.h));
+    canvas.paint (offset + canvas::coord_t (canvas_x, canvas_y),
+		  theme.get_line_art (directions (c.m_up, // up
+						  false, // down
+						  c.m_left, // left
+						  false))); // right
+  }
+}
+
+void
+table::paint_cell_contents_to_canvas(canvas &canvas,
+				     canvas::coord_t offset,
+				     const table_geometry &tg) const
+{
+  for (auto &placement : m_placements)
+    placement.paint_cell_contents_to_canvas (canvas, offset, tg);
+}
+
+/* class table_cell_sizes.  */
+
+/* Consider 1x1 cells.  */
+
+void
+table_cell_sizes::pass_1 (const table &table)
+{
+  for (auto &placement : table.m_placements)
+    if (placement.one_by_one_p ())
+      {
+	canvas::size_t canvas_size (placement.get_min_canvas_size ());
+	table::coord_t table_coord (placement.m_rect.m_top_left);
+	m_col_widths.require (table_coord.x, canvas_size.w);
+	m_row_heights.require (table_coord.y, canvas_size.h);
+      }
+}
+
+/* Consider cells that span more than one row or column.  */
+
+void
+table_cell_sizes::pass_2 (const table &table)
+{
+  for (auto &placement : table.m_placements)
+    if (!placement.one_by_one_p ())
+      {
+	const canvas::size_t req_canvas_size (placement.get_min_canvas_size ());
+	const canvas::size_t current_canvas_size
+	  = get_canvas_size (placement.m_rect);
+	/* Grow columns as necessary.  */
+	if (req_canvas_size.w > current_canvas_size.w)
+	  {
+	    /* Spread the deficit amongst the columns.  */
+	    int deficit = req_canvas_size.w - current_canvas_size.w;
+	    const int per_col = deficit / placement.m_rect.m_size.w;
+	    for (int table_x = placement.m_rect.get_min_x ();
+		 table_x < placement.m_rect.get_next_x ();
+		 table_x++)
+	    {
+	      m_col_widths.m_requirements[table_x] += per_col;
+	      deficit -= per_col;
+	    }
+	    /* Make sure we allocate all of the deficit.  */
+	    if (deficit > 0)
+	      {
+		const int table_x = placement.m_rect.get_max_x ();
+		m_col_widths.m_requirements[table_x] += deficit;
+	      }
+	  }
+	/* Grow rows as necessary.  */
+	if (req_canvas_size.h > current_canvas_size.h)
+	  {
+	    /* Spread the deficit amongst the rows.  */
+	    int deficit = req_canvas_size.h - current_canvas_size.h;
+	    const int per_row = deficit / placement.m_rect.m_size.h;
+	    for (int table_y = placement.m_rect.get_min_y ();
+		 table_y < placement.m_rect.get_next_y ();
+		 table_y++)
+	    {
+	      m_row_heights.m_requirements[table_y] += per_row;
+	      deficit -= per_row;
+	    }
+	    /* Make sure we allocate all of the deficit.  */
+	    if (deficit > 0)
+	      {
+		const int table_y = placement.m_rect.get_max_y ();
+		m_row_heights.m_requirements[table_y] += deficit;
+	      }
+	  }
+      }
+}
+
+canvas::size_t
+table_cell_sizes::get_canvas_size (const table::rect_t &rect) const
+{
+  canvas::size_t result (0, 0);
+  for (int table_x = rect.get_min_x ();
+       table_x < rect.get_next_x ();
+       table_x ++)
+    result.w += m_col_widths.m_requirements[table_x];
+  for (int table_y = rect.get_min_y ();
+       table_y < rect.get_next_y ();
+       table_y ++)
+    result.h += m_row_heights.m_requirements[table_y];
+  /* Allow space for the borders.  */
+  result.w += rect.m_size.w - 1;
+  result.h += rect.m_size.h - 1;
+  return result;
+}
+
+/* class text_art::table_geometry.  */
+
+table_geometry::table_geometry (const table &table, table_cell_sizes &cell_sizes)
+: m_table (table),
+  m_cell_sizes (cell_sizes),
+  m_canvas_size (canvas::size_t (0, 0)),
+  m_col_start_x (table.get_size ().w),
+  m_row_start_y (table.get_size ().h)
+{
+  recalc_coords ();
+}
+
+void
+table_geometry::recalc_coords ()
+{
+  /* Start canvas column of table cell, including leading border.  */
+  m_col_start_x.clear ();
+  int iter_canvas_x = 0;
+  for (auto w : m_cell_sizes.m_col_widths.m_requirements)
+    {
+      m_col_start_x.push_back (iter_canvas_x);
+      iter_canvas_x += w + 1;
+    }
+
+  /* Start canvas row of table cell, including leading border.  */
+  m_row_start_y.clear ();
+  int iter_canvas_y = 0;
+  for (auto h : m_cell_sizes.m_row_heights.m_requirements)
+    {
+      m_row_start_y.push_back (iter_canvas_y);
+      iter_canvas_y += h + 1;
+    }
+
+  m_canvas_size = canvas::size_t (iter_canvas_x + 1,
+				  iter_canvas_y + 1);
+}
+
+/* Get the TL corner of the table cell at TABLE_COORD
+   in canvas coords (including the border).  */
+
+canvas::coord_t
+table_geometry::table_to_canvas (table::coord_t table_coord) const
+{
+  return canvas::coord_t (table_x_to_canvas_x (table_coord.x),
+			  table_y_to_canvas_y (table_coord.y));
+}
+
+/* Get the left border of the table cell at column TABLE_X
+   in canvas coords (including the border).  */
+
+int
+table_geometry::table_x_to_canvas_x (int table_x) const
+{
+  /* Allow one beyond the end, for the right-hand border of the table.  */
+  if (table_x == m_col_start_x.size ())
+    return m_canvas_size.w - 1;
+  return m_col_start_x[table_x];
+}
+
+/* Get the top border of the table cell at column TABLE_Y
+   in canvas coords (including the border).  */
+
+int
+table_geometry::table_y_to_canvas_y (int table_y) const
+{
+  /* Allow one beyond the end, for the right-hand border of the table.  */
+  if (table_y == m_row_start_y.size ())
+    return m_canvas_size.h - 1;
+  return m_row_start_y[table_y];
+}
+
+/* class text_art::simple_table_geometry.  */
+
+simple_table_geometry::simple_table_geometry (const table &table)
+: m_col_widths (table.get_size ().w),
+  m_row_heights (table.get_size ().h),
+  m_cell_sizes (m_col_widths, m_row_heights),
+  m_tg (table, m_cell_sizes)
+{
+  m_cell_sizes.pass_1 (table);
+  m_cell_sizes.pass_2 (table);
+  m_tg.recalc_coords ();
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+static void
+test_tic_tac_toe ()
+{
+  style_manager sm;
+  table t (table::size_t (3, 3));
+  t.set_cell (table::coord_t (0, 0), styled_string (sm, "X"));
+  t.set_cell (table::coord_t (1, 0), styled_string (sm, ""));
+  t.set_cell (table::coord_t (2, 0), styled_string (sm, ""));
+  t.set_cell (table::coord_t (0, 1), styled_string (sm, "O"));
+  t.set_cell (table::coord_t (1, 1), styled_string (sm, "O"));
+  t.set_cell (table::coord_t (2, 1), styled_string (sm, ""));
+  t.set_cell (table::coord_t (0, 2), styled_string (sm, "X"));
+  t.set_cell (table::coord_t (1, 2), styled_string (sm, ""));
+  t.set_cell (table::coord_t (2, 2), styled_string (sm, "O"));
+
+  {
+    canvas canvas (t.to_canvas (ascii_theme (), sm));
+    ASSERT_CANVAS_STREQ
+      (canvas, false,
+       ("+-+-+-+\n"
+	"|X| | |\n"
+	"+-+-+-+\n"
+	"|O|O| |\n"
+	"+-+-+-+\n"
+	"|X| |O|\n"
+	"+-+-+-+\n"));
+  }
+
+  {
+    canvas canvas (t.to_canvas (unicode_theme (), sm));
+    ASSERT_CANVAS_STREQ
+      (canvas, false,
+       // FIXME: are we allowed unicode chars in string literals in our source?
+       ("┌─┬─┬─┐\n"
+	"│X│ │ │\n"
+	"├─┼─┼─┤\n"
+	"│O│O│ │\n"
+	"├─┼─┼─┤\n"
+	"│X│ │O│\n"
+	"└─┴─┴─┘\n"));
+  }
+}
+
+static table
+make_text_table ()
+{
+  style_manager sm;
+  table t (table::size_t (3, 3));
+  t.set_cell (table::coord_t (0, 0), styled_string (sm, "top left"));
+  t.set_cell (table::coord_t (1, 0), styled_string (sm, "top middle"));
+  t.set_cell (table::coord_t (2, 0), styled_string (sm, "top right"));
+  t.set_cell (table::coord_t (0, 1), styled_string (sm, "middle left"));
+  t.set_cell (table::coord_t (1, 1), styled_string (sm, "middle middle"));
+  t.set_cell (table::coord_t (2, 1), styled_string (sm, "middle right"));
+  t.set_cell (table::coord_t (0, 2), styled_string (sm, "bottom left"));
+  t.set_cell (table::coord_t (1, 2), styled_string (sm, "bottom middle"));
+  t.set_cell (table::coord_t (2, 2), styled_string (sm, "bottom right"));
+  return t;
+}
+
+static void
+test_text_table ()
+{
+  style_manager sm;
+  table table = make_text_table ();
+  {
+    canvas canvas (table.to_canvas (ascii_theme(), sm));
+    ASSERT_CANVAS_STREQ
+      (canvas, false,
+       ("+-----------+-------------+------------+\n"
+	"| top left  | top middle  | top right  |\n"
+	"+-----------+-------------+------------+\n"
+	"|middle left|middle middle|middle right|\n"
+	"+-----------+-------------+------------+\n"
+	"|bottom left|bottom middle|bottom right|\n"
+	"+-----------+-------------+------------+\n"));
+  }
+  {
+    canvas canvas (table.to_canvas (unicode_theme(), sm));
+    ASSERT_CANVAS_STREQ
+      (canvas, false,
+       // FIXME: are we allowed unicode chars in string literals in our source?
+       ("┌───────────┬─────────────┬────────────┐\n"
+	"│ top left  │ top middle  │ top right  │\n"
+	"├───────────┼─────────────┼────────────┤\n"
+	"│middle left│middle middle│middle right│\n"
+	"├───────────┼─────────────┼────────────┤\n"
+	"│bottom left│bottom middle│bottom right│\n"
+	"└───────────┴─────────────┴────────────┘\n"));
+  }
+}
+
+static void
+test_offset_table ()
+{
+  style_manager sm;
+  table table = make_text_table ();
+  simple_table_geometry stg (table);
+  const canvas::size_t tcs = stg.m_tg.get_canvas_size();
+  {
+    canvas canvas (canvas::size_t (tcs.w + 5, tcs.h + 5), sm);
+    canvas.debug_fill ();
+    table.paint_to_canvas (canvas, canvas::coord_t (3, 3),
+			   stg.m_tg,
+			   ascii_theme());
+    ASSERT_CANVAS_STREQ
+      (canvas, false,
+       ("*********************************************\n"
+	"*********************************************\n"
+	"*********************************************\n"
+	"***+-----------+-------------+------------+**\n"
+	"***| top left  | top middle  | top right  |**\n"
+	"***+-----------+-------------+------------+**\n"
+	"***|middle left|middle middle|middle right|**\n"
+	"***+-----------+-------------+------------+**\n"
+	"***|bottom left|bottom middle|bottom right|**\n"
+	"***+-----------+-------------+------------+**\n"
+	"*********************************************\n"
+	"*********************************************\n"));
+  }
+  {
+    canvas canvas (canvas::size_t (tcs.w + 5, tcs.h + 5), sm);
+    canvas.debug_fill ();
+    table.paint_to_canvas (canvas, canvas::coord_t (3, 3),
+			   stg.m_tg,
+			   unicode_theme());
+    ASSERT_CANVAS_STREQ
+      (canvas, false,
+       // FIXME: are we allowed unicode chars in string literals in our source?
+       ("*********************************************\n"
+	"*********************************************\n"
+	"*********************************************\n"
+	"***┌───────────┬─────────────┬────────────┐**\n"
+	"***│ top left  │ top middle  │ top right  │**\n"
+	"***├───────────┼─────────────┼────────────┤**\n"
+	"***│middle left│middle middle│middle right│**\n"
+	"***├───────────┼─────────────┼────────────┤**\n"
+	"***│bottom left│bottom middle│bottom right│**\n"
+	"***└───────────┴─────────────┴────────────┘**\n"
+	"*********************************************\n"
+	"*********************************************\n"));
+  }
+}
+
+#define ASSERT_TABLE_CELL_STREQ(TABLE, TABLE_X, TABLE_Y, EXPECTED_STR)	\
+  SELFTEST_BEGIN_STMT							\
+    table::coord_t coord ((TABLE_X), (TABLE_Y));			\
+    const table::cell_placement *cp = (TABLE).get_placement_at (coord);	\
+    ASSERT_NE (cp, nullptr);						\
+    ASSERT_EQ (cp->get_content (), styled_string (sm, EXPECTED_STR)); \
+  SELFTEST_END_STMT
+
+#define ASSERT_TABLE_NULL_CELL(TABLE, TABLE_X, TABLE_Y)			\
+  SELFTEST_BEGIN_STMT							\
+    table::coord_t coord ((TABLE_X), (TABLE_Y));			\
+    const table::cell_placement *cp = (TABLE).get_placement_at (coord);	\
+    ASSERT_EQ (cp, nullptr);						\
+  SELFTEST_END_STMT
+
+static void
+test_spans ()
+{
+  style_manager sm;
+  table table (table::size_t (3, 3));
+  table.set_cell_span (table::rect_t (table::coord_t (0, 0),
+				      table::size_t (3, 1)),
+		       styled_string (sm, "ABC"));
+  table.set_cell_span (table::rect_t (table::coord_t (0, 1),
+				      table::size_t (2, 1)),
+		       styled_string (sm, "DE"));
+  table.set_cell_span (table::rect_t (table::coord_t (2, 1),
+				      table::size_t (1, 1)),
+		       styled_string (sm, "F"));
+  table.set_cell (table::coord_t (0, 2), styled_string (sm, "G"));
+  table.set_cell (table::coord_t (1, 2), styled_string (sm, "H"));
+  table.set_cell (table::coord_t (2, 2), styled_string (sm, "I"));
+  {
+    canvas canvas (table.to_canvas (ascii_theme(), sm));
+    ASSERT_CANVAS_STREQ
+      (canvas, false,
+       ("+-----+\n"
+	"| ABC |\n"
+	"+---+-+\n"
+	"|DE |F|\n"
+	"+-+-+-+\n"
+	"|G|H|I|\n"
+	"+-+-+-+\n"));
+  }
+  {
+    canvas canvas (table.to_canvas (unicode_theme(), sm));
+    ASSERT_CANVAS_STREQ
+      (canvas, false,
+       // FIXME: are we allowed unicode chars in string literals in our source?
+       ("┌─────┐\n"
+	"│ ABC │\n"
+	"├───┬─┤\n"
+	"│DE │F│\n"
+	"├─┬─┼─┤\n"
+	"│G│H│I│\n"
+	"└─┴─┴─┘\n"));
+  }
+}
+
+/* Verify building this 5x5 table with spans:
+     |0|1|2|3|4|
+     +-+-+-+-+-+
+    0|A A A|B|C|0
+     +     +-+ +
+    1|A A A|D|C|1
+     +     +-+-+
+    2|A A A|E|F|2
+     +-+-+-+-+-+
+    3|G G|H|I I|3
+     |   | +-+-+
+    4|G G|H|J J|4
+     +-+-+-+-+-+
+     |0|1|2|3|4|
+*/
+
+static void
+test_spans_2 ()
+{
+  style_manager sm;
+  table table (table::size_t (5, 5));
+  table.set_cell_span (table::rect_t (table::coord_t (0, 0),
+				      table::size_t (3, 3)),
+		       styled_string (sm, "A"));
+  table.set_cell_span (table::rect_t (table::coord_t (3, 0),
+				      table::size_t (1, 1)),
+		       styled_string (sm, "B"));
+  table.set_cell_span (table::rect_t (table::coord_t (4, 0),
+				      table::size_t (1, 2)),
+		       styled_string (sm, "C"));
+  table.set_cell_span (table::rect_t (table::coord_t (3, 1),
+				      table::size_t (1, 1)),
+		       styled_string (sm, "D"));
+  table.set_cell_span (table::rect_t (table::coord_t (3, 2),
+				      table::size_t (1, 1)),
+		       styled_string (sm, "E"));
+  table.set_cell_span (table::rect_t (table::coord_t (4, 2),
+				      table::size_t (1, 1)),
+		       styled_string (sm, "F"));
+  table.set_cell_span (table::rect_t (table::coord_t (0, 3),
+				      table::size_t (2, 2)),
+		       styled_string (sm, "G"));
+  table.set_cell_span (table::rect_t (table::coord_t (2, 3),
+				      table::size_t (1, 2)),
+		       styled_string (sm, "H"));
+  table.set_cell_span (table::rect_t (table::coord_t (3, 3),
+				      table::size_t (2, 1)),
+		       styled_string (sm, "I"));
+  table.set_cell_span (table::rect_t (table::coord_t (3, 4),
+				      table::size_t (2, 1)),
+		       styled_string (sm, "J"));
+
+  /* Check occupancy at each table coordinate.  */
+  ASSERT_TABLE_CELL_STREQ (table, 0, 0, "A");
+  ASSERT_TABLE_CELL_STREQ (table, 1, 0, "A");
+  ASSERT_TABLE_CELL_STREQ (table, 2, 0, "A");
+  ASSERT_TABLE_CELL_STREQ (table, 3, 0, "B");
+  ASSERT_TABLE_CELL_STREQ (table, 4, 0, "C");
+
+  ASSERT_TABLE_CELL_STREQ (table, 0, 1, "A");
+  ASSERT_TABLE_CELL_STREQ (table, 1, 1, "A");
+  ASSERT_TABLE_CELL_STREQ (table, 2, 1, "A");
+  ASSERT_TABLE_CELL_STREQ (table, 3, 1, "D");
+  ASSERT_TABLE_CELL_STREQ (table, 4, 1, "C");
+
+  ASSERT_TABLE_CELL_STREQ (table, 0, 2, "A");
+  ASSERT_TABLE_CELL_STREQ (table, 1, 2, "A");
+  ASSERT_TABLE_CELL_STREQ (table, 2, 2, "A");
+  ASSERT_TABLE_CELL_STREQ (table, 3, 2, "E");
+  ASSERT_TABLE_CELL_STREQ (table, 4, 2, "F");
+
+  ASSERT_TABLE_CELL_STREQ (table, 0, 3, "G");
+  ASSERT_TABLE_CELL_STREQ (table, 1, 3, "G");
+  ASSERT_TABLE_CELL_STREQ (table, 2, 3, "H");
+  ASSERT_TABLE_CELL_STREQ (table, 3, 3, "I");
+  ASSERT_TABLE_CELL_STREQ (table, 4, 3, "I");
+
+  ASSERT_TABLE_CELL_STREQ (table, 0, 4, "G");
+  ASSERT_TABLE_CELL_STREQ (table, 1, 4, "G");
+  ASSERT_TABLE_CELL_STREQ (table, 2, 4, "H");
+  ASSERT_TABLE_CELL_STREQ (table, 3, 4, "J");
+  ASSERT_TABLE_CELL_STREQ (table, 4, 4, "J");
+
+  {
+    canvas canvas (table.to_canvas (ascii_theme(), sm));
+    ASSERT_CANVAS_STREQ
+      (canvas, false,
+       ("+---+-+-+\n"
+	"|   |B| |\n"
+	"|   +-+C|\n"
+	"| A |D| |\n"
+	"|   +-+-+\n"
+	"|   |E|F|\n"
+	"+-+-+-+-+\n"
+	"| | | I |\n"
+	"|G|H+---+\n"
+	"| | | J |\n"
+	"+-+-+---+\n"));
+  }
+  {
+    canvas canvas (table.to_canvas (unicode_theme(), sm));
+    ASSERT_CANVAS_STREQ
+      (canvas, false,
+       // FIXME: are we allowed unicode chars in string literals in our source?
+       ("┌───┬─┬─┐\n"
+	"│   │B│ │\n"
+	"│   ├─┤C│\n"
+	"│ A │D│ │\n"
+	"│   ├─┼─┤\n"
+	"│   │E│F│\n"
+	"├─┬─┼─┴─┤\n"
+	"│ │ │ I │\n"
+	"│G│H├───┤\n"
+	"│ │ │ J │\n"
+	"└─┴─┴───┘\n"));
+  }
+}
+
+/* Experiment with adding a 1-table-column gap at the boundary between
+   valid vs invalid for visualizing a buffer overflow.  */
+static void
+test_spans_3 ()
+{
+  const char * const str = "hello world!";
+  const size_t buf_size = 10;
+  const size_t str_size = strlen (str) + 1;
+
+  style_manager sm;
+  table table (table::size_t (str_size + 1, 3));
+
+  table.set_cell_span (table::rect_t (table::coord_t (0, 0),
+				      table::size_t (str_size + 1, 1)),
+		       styled_string (sm, "String literal"));
+
+  for (size_t i = 0; i < str_size; i++)
+    {
+      table::coord_t c (i, 1);
+      if (i >= buf_size)
+	c.x++;
+      if (str[i] == '\0')
+	table.set_cell (c, styled_string (sm, "NUL"));
+      else
+	table.set_cell (c, styled_string ((cppchar_t)str[i]));
+    }
+
+  table.set_cell_span (table::rect_t (table::coord_t (0, 2),
+				      table::size_t (buf_size, 1)),
+		       styled_string::from_fmt (sm,
+						     nullptr,
+						     "'buf' (char[%i])",
+						     (int)buf_size));
+  table.set_cell_span (table::rect_t (table::coord_t (buf_size + 1, 2),
+				      table::size_t (str_size - buf_size, 1)),
+		       styled_string (sm, "overflow"));
+
+  {
+    canvas canvas (table.to_canvas (ascii_theme (), sm));
+    ASSERT_CANVAS_STREQ
+      (canvas, false,
+       "+-----------------------------+\n"
+       "|       String literal        |\n"
+       "+-+-+-+-+-+-+-+-+-+-++-+-+----+\n"
+       "|h|e|l|l|o| |w|o|r|l||d|!|NUL |\n"
+       "+-+-+-+-+-+-+-+-+-+-++-+-+----+\n"
+       "| 'buf' (char[10])  ||overflow|\n"
+       "+-------------------++--------+\n");
+  }
+  {
+    canvas canvas (table.to_canvas (unicode_theme (), sm));
+    ASSERT_CANVAS_STREQ
+      (canvas, false,
+       // FIXME: are we allowed unicode chars in string literals in our source?
+       ("┌─────────────────────────────┐\n"
+	"│       String literal        │\n"
+	"├─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬┬─┬─┬────┤\n"
+	"│h│e│l│l│o│ │w│o│r│l││d│!│NUL │\n"
+	"├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┤├─┴─┴────┤\n"
+	"│ 'buf' (char[10])  ││overflow│\n"
+	"└───────────────────┘└────────┘\n"));
+  }
+}
+
+static void
+test_double_width_chars ()
+{
+  table_cell_content tcc (styled_string ((cppchar_t)0x1f642));
+  ASSERT_EQ (tcc.get_canvas_size ().w, 2);
+  ASSERT_EQ (tcc.get_canvas_size ().h, 1);
+
+  style_manager sm;
+  table table (table::size_t (1, 1));
+  table.set_cell (table::coord_t (0,0),
+		  styled_string ((cppchar_t)0x1f642));
+
+  canvas canvas (table.to_canvas (unicode_theme(), sm));
+  ASSERT_CANVAS_STREQ
+    (canvas, false,
+     // FIXME: are we allowed unicode chars in string literals in our source?
+     ("┌──┐\n"
+      "│🙂│\n"
+      "└──┘\n"));
+}
+
+static void
+test_ipv4_header ()
+{
+  style_manager sm;
+  table table (table::size_t (34, 10));
+  table.set_cell (table::coord_t (0, 0), styled_string (sm, "Offsets"));
+  table.set_cell (table::coord_t (1, 0), styled_string (sm, "Octet"));
+  table.set_cell (table::coord_t (0, 1), styled_string (sm, "Octet"));
+  for (int octet = 0; octet < 4; octet++)
+    table.set_cell_span (table::rect_t (table::coord_t (2 + (octet * 8), 0),
+					table::size_t (8, 1)),
+			 styled_string::from_fmt (sm, nullptr, "%i", octet));
+  table.set_cell (table::coord_t (1, 1), styled_string (sm, "Bit"));
+  for (int bit = 0; bit < 32; bit++)
+    table.set_cell (table::coord_t (bit + 2, 1),
+		    styled_string::from_fmt (sm, nullptr, "%i", bit));
+  for (int word = 0; word < 6; word++)
+    {
+      table.set_cell (table::coord_t (0, word + 2),
+		      styled_string::from_fmt (sm, nullptr, "%i", word * 4));
+      table.set_cell (table::coord_t (1, word + 2),
+		      styled_string::from_fmt (sm, nullptr, "%i", word * 32));
+    }
+
+  table.set_cell (table::coord_t (0, 8), styled_string (sm, "..."));
+  table.set_cell (table::coord_t (1, 8), styled_string (sm, "..."));
+  table.set_cell (table::coord_t (0, 9), styled_string (sm, "56"));
+  table.set_cell (table::coord_t (1, 9), styled_string (sm, "448"));
+
+#define SET_BITS(FIRST, LAST, NAME)					\
+  do {									\
+    const int first = (FIRST);						\
+    const int last = (LAST);						\
+    const char *name = (NAME);						\
+    const int row = first / 32;						\
+    gcc_assert (last / 32 == row);					\
+    table::rect_t rect (table::coord_t ((first % 32) + 2, row + 2),	\
+			table::size_t (last + 1 - first , 1));		\
+    table.set_cell_span (rect, styled_string (sm, name));		\
+  } while (0)
+
+  SET_BITS (0, 3, "Version");
+  SET_BITS (4, 7, "IHL");
+  SET_BITS (8, 13, "DSCP");
+  SET_BITS (14, 15, "ECN");
+  SET_BITS (16, 31, "Total Length");
+
+  SET_BITS (32 +  0, 32 + 15, "Identification");
+  SET_BITS (32 + 16, 32 + 18, "Flags");
+  SET_BITS (32 + 19, 32 + 31, "Fragment Offset");
+
+  SET_BITS (64 +  0, 64 +  7, "Time To Live");
+  SET_BITS (64 +  8, 64 + 15, "Protocol");
+  SET_BITS (64 + 16, 64 + 31, "Header Checksum");
+
+  SET_BITS (96 +  0, 96 + 31, "Source IP Address");
+  SET_BITS (128 +  0, 128 + 31, "Destination IP Address");
+
+  table.set_cell_span(table::rect_t (table::coord_t (2, 7),
+				     table::size_t (32, 3)),
+		      styled_string (sm, "Options"));
+  {
+    canvas canvas (table.to_canvas (ascii_theme(), sm));
+    ASSERT_CANVAS_STREQ
+      (canvas, false,
+       ("+-------+-----+---------------+---------------------+-----------------------+-----------------------+\n"
+	"|Offsets|Octet|       0       |          1          |           2           |           3           |\n"
+	"+-------+-----+-+-+-+-+-+-+-+-+-+-+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\n"
+	"| Octet | Bit |0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|\n"
+	"+-------+-----+-+-+-+-+-+-+-+-+-+-+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\n"
+	"|   0   |  0  |Version|  IHL  |     DSCP      | ECN |                 Total Length                  |\n"
+	"+-------+-----+-------+-------+---------------+-----+--------+--------------------------------------+\n"
+	"|   4   | 32  |           Identification            | Flags  |           Fragment Offset            |\n"
+	"+-------+-----+---------------+---------------------+--------+--------------------------------------+\n"
+	"|   8   | 64  | Time To Live  |      Protocol       |                Header Checksum                |\n"
+	"+-------+-----+---------------+---------------------+-----------------------------------------------+\n"
+	"|  12   | 96  |                                  Source IP Address                                  |\n"
+	"+-------+-----+-------------------------------------------------------------------------------------+\n"
+	"|  16   | 128 |                               Destination IP Address                                |\n"
+	"+-------+-----+-------------------------------------------------------------------------------------+\n"
+	"|  20   | 160 |                                                                                     |\n"
+	"+-------+-----+                                                                                     |\n"
+	"|  ...  | ... |                                       Options                                       |\n"
+	"+-------+-----+                                                                                     |\n"
+	"|  56   | 448 |                                                                                     |\n"
+	"+-------+-----+-------------------------------------------------------------------------------------+\n"));
+  }
+  {
+    canvas canvas (table.to_canvas (unicode_theme(), sm));
+    ASSERT_CANVAS_STREQ
+      (canvas, false,
+       // FIXME: are we allowed unicode chars in string literals in our source?
+       ("┌───────┬─────┬───────────────┬─────────────────────┬───────────────────────┬───────────────────────┐\n"
+	"│Offsets│Octet│       0       │          1          │           2           │           3           │\n"
+	"├───────┼─────┼─┬─┬─┬─┬─┬─┬─┬─┼─┬─┬──┬──┬──┬──┬──┬──┼──┬──┬──┬──┬──┬──┬──┬──┼──┬──┬──┬──┬──┬──┬──┬──┤\n"
+	"│ Octet │ Bit │0│1│2│3│4│5│6│7│8│9│10│11│12│13│14│15│16│17│18│19│20│21│22│23│24│25│26│27│28│29│30│31│\n"
+	"├───────┼─────┼─┴─┴─┴─┼─┴─┴─┴─┼─┴─┴──┴──┴──┴──┼──┴──┼──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┤\n"
+	"│   0   │  0  │Version│  IHL  │     DSCP      │ ECN │                 Total Length                  │\n"
+	"├───────┼─────┼───────┴───────┴───────────────┴─────┼────────┬──────────────────────────────────────┤\n"
+	"│   4   │ 32  │           Identification            │ Flags  │           Fragment Offset            │\n"
+	"├───────┼─────┼───────────────┬─────────────────────┼────────┴──────────────────────────────────────┤\n"
+	"│   8   │ 64  │ Time To Live  │      Protocol       │                Header Checksum                │\n"
+	"├───────┼─────┼───────────────┴─────────────────────┴───────────────────────────────────────────────┤\n"
+	"│  12   │ 96  │                                  Source IP Address                                  │\n"
+	"├───────┼─────┼─────────────────────────────────────────────────────────────────────────────────────┤\n"
+	"│  16   │ 128 │                               Destination IP Address                                │\n"
+	"├───────┼─────┼─────────────────────────────────────────────────────────────────────────────────────┤\n"
+	"│  20   │ 160 │                                                                                     │\n"
+	"├───────┼─────┤                                                                                     │\n"
+	"│  ...  │ ... │                                       Options                                       │\n"
+	"├───────┼─────┤                                                                                     │\n"
+	"│  56   │ 448 │                                                                                     │\n"
+	"└───────┴─────┴─────────────────────────────────────────────────────────────────────────────────────┘\n"));
+  }
+}
+
+static void
+test_missing_cells ()
+{
+  style_manager sm;
+  table table (table::size_t (3, 3));
+  table.set_cell (table::coord_t (1, 0), styled_string (sm, "A"));
+  table.set_cell (table::coord_t (0, 1), styled_string (sm, "B"));
+  table.set_cell (table::coord_t (1, 1), styled_string (sm, "C"));
+  table.set_cell (table::coord_t (2, 1), styled_string (sm, "D"));
+  table.set_cell (table::coord_t (1, 2), styled_string (sm, "E"));
+
+  ASSERT_TABLE_NULL_CELL (table, 0, 0);
+  ASSERT_TABLE_CELL_STREQ (table, 1, 0, "A");
+  ASSERT_TABLE_NULL_CELL (table, 2, 0);
+
+  ASSERT_TABLE_CELL_STREQ (table, 0, 1, "B");
+  ASSERT_TABLE_CELL_STREQ (table, 1, 1, "C");
+  ASSERT_TABLE_CELL_STREQ (table, 2, 1, "D");
+
+  ASSERT_TABLE_NULL_CELL (table, 0, 2);
+  ASSERT_TABLE_CELL_STREQ (table, 1, 2, "E");
+  ASSERT_TABLE_NULL_CELL (table, 2, 2);
+
+  {
+    canvas canvas (table.to_canvas (ascii_theme(), sm));
+    ASSERT_CANVAS_STREQ
+      (canvas, false,
+       ("  +-+\n"
+	"  |A|\n"
+	"+-+-+-+\n"
+	"|B|C|D|\n"
+	"+-+-+-+\n"
+	"  |E|\n"
+	"  +-+\n"));
+  }
+  {
+    canvas canvas (table.to_canvas (unicode_theme(), sm));
+    ASSERT_CANVAS_STREQ
+      (canvas, false,
+       ("  ┌─┐\n"
+	"  │A│\n"
+	"┌─┼─┼─┐\n"
+	"│B│C│D│\n"
+	"└─┼─┼─┘\n"
+	"  │E│\n"
+	"  └─┘\n"));
+  }
+}
+
+static void
+test_add_row ()
+{
+  style_manager sm;
+  table table (table::size_t (3, 0));
+  for (int i = 0; i < 5; i++)
+    {
+      const int y = table.add_row ();
+      for (int x = 0; x < 3; x++)
+	table.set_cell (table::coord_t (x, y),
+			styled_string::from_fmt (sm, nullptr,
+						 "%i, %i", x, y));
+    }
+  canvas canvas (table.to_canvas (ascii_theme(), sm));
+  ASSERT_CANVAS_STREQ
+    (canvas, false,
+     ("+----+----+----+\n"
+      "|0, 0|1, 0|2, 0|\n"
+      "+----+----+----+\n"
+      "|0, 1|1, 1|2, 1|\n"
+      "+----+----+----+\n"
+      "|0, 2|1, 2|2, 2|\n"
+      "+----+----+----+\n"
+      "|0, 3|1, 3|2, 3|\n"
+      "+----+----+----+\n"
+      "|0, 4|1, 4|2, 4|\n"
+      "+----+----+----+\n"));
+}
+
+static void
+test_alignment ()
+{
+  style_manager sm;
+  table table (table::size_t (9, 9));
+  table.set_cell_span (table::rect_t (table::coord_t (0, 0),
+				      table::size_t (3, 3)),
+		       styled_string (sm, "left top"),
+		      x_align::LEFT, y_align::TOP);
+  table.set_cell_span (table::rect_t (table::coord_t (3, 0),
+				      table::size_t (3, 3)),
+		       styled_string (sm, "center top"),
+		       x_align::CENTER, y_align::TOP);
+  table.set_cell_span (table::rect_t (table::coord_t (6, 0),
+				      table::size_t (3, 3)),
+		       styled_string (sm, "right top"),
+		       x_align::RIGHT, y_align::TOP);
+  table.set_cell_span (table::rect_t (table::coord_t (0, 3),
+				      table::size_t (3, 3)),
+		       styled_string (sm, "left center"),
+		       x_align::LEFT, y_align::CENTER);
+  table.set_cell_span (table::rect_t (table::coord_t (3, 3),
+				      table::size_t (3, 3)),
+		       styled_string (sm, "center center"),
+		       x_align::CENTER, y_align::CENTER);
+  table.set_cell_span (table::rect_t (table::coord_t (6, 3),
+				      table::size_t (3, 3)),
+		       styled_string (sm, "right center"),
+		       x_align::RIGHT, y_align::CENTER);
+  table.set_cell_span (table::rect_t (table::coord_t (0, 6),
+				      table::size_t (3, 3)),
+		       styled_string (sm, "left bottom"),
+		       x_align::LEFT, y_align::BOTTOM);
+  table.set_cell_span (table::rect_t (table::coord_t (3, 6),
+				      table::size_t (3, 3)),
+		       styled_string (sm, "center bottom"),
+		       x_align::CENTER, y_align::BOTTOM);
+  table.set_cell_span (table::rect_t (table::coord_t (6, 6),
+				      table::size_t (3, 3)),
+		       styled_string (sm, "right bottom"),
+		       x_align::RIGHT, y_align::BOTTOM);
+
+  canvas canvas (table.to_canvas (ascii_theme(), sm));
+  ASSERT_CANVAS_STREQ
+    (canvas, false,
+     ("+-----------+-------------+------------+\n"
+      "|left top   | center top  |   right top|\n"
+      "|           |             |            |\n"
+      "+-----------+-------------+------------+\n"
+      "|left center|center center|right center|\n"
+      "|           |             |            |\n"
+      "+-----------+-------------+------------+\n"
+      "|           |             |            |\n"
+      "|left bottom|center bottom|right bottom|\n"
+      "+-----------+-------------+------------+\n"));
+}
+
+/* Run all selftests in this file.  */
+
+void
+text_art_table_cc_tests ()
+{
+  test_tic_tac_toe ();
+  test_text_table ();
+  test_offset_table ();
+  test_spans ();
+  test_spans_2 ();
+  test_spans_3 ();
+  test_double_width_chars ();
+  test_ipv4_header ();
+  test_missing_cells ();
+  test_add_row ();
+  test_alignment ();
+}
+
+} // namespace selftest
+
+
+#endif /* #if CHECKING_P */
diff --git a/gcc/text-art/table.h b/gcc/text-art/table.h
new file mode 100644
index 0000000..2dc5c3c
--- /dev/null
+++ b/gcc/text-art/table.h
@@ -0,0 +1,261 @@
+/* Support for tabular/grid-based content.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_TEXT_ART_TABLE_H
+#define GCC_TEXT_ART_TABLE_H
+
+#include "text-art/canvas.h"
+#include "text-art/theme.h"
+
+namespace text_art {
+
+class table;
+class table_geometry;
+
+/* A class representing the content of a particular table cell,
+   or of a span of table cells.  */
+
+class table_cell_content
+{
+ public:
+  table_cell_content () : m_str (), m_size (0, 0) {}
+  table_cell_content (styled_string &&s);
+
+  bool operator== (const table_cell_content &other) const
+  {
+    return m_str == other.m_str;
+  }
+
+  canvas::size_t get_canvas_size () const { return m_size; }
+
+  void paint_to_canvas (canvas &canvas,
+			canvas::coord_t top_left) const;
+
+ private:
+  styled_string m_str;
+  canvas::size_t m_size;
+};
+
+/* A list of required sizes of table rows or columns
+   in canvas units (row heights or column widths).  */
+
+struct table_dimension_sizes
+{
+  table_dimension_sizes (unsigned num);
+
+  void require (unsigned idx, int amount)
+  {
+    m_requirements[idx] = std::max (m_requirements[idx], amount);
+  }
+
+  std::vector<int> m_requirements;
+};
+
+/* A 2D grid of cells.  Instances of table_cell_content can be assigned
+   to individual table cells, and to rectangular spans of cells.  Such
+   assignments do not have to fully cover the 2D grid, but they must not
+   overlap.  */
+
+class table
+{
+ public:
+  typedef size<class table> size_t;
+  typedef coord<class table> coord_t;
+  typedef range<class table> range_t;
+  typedef rect<class table> rect_t;
+
+  /* A record of how a table_cell_content was placed at a table::rect_t
+     with a certain alignment.  */
+  class cell_placement
+  {
+  public:
+    cell_placement (rect_t rect,
+		    table_cell_content &&content,
+		    x_align x_align,
+		    y_align y_align)
+    : m_rect (rect),
+      m_content (std::move (content)),
+      m_x_align (x_align),
+      m_y_align (y_align)
+    {
+    }
+
+    bool one_by_one_p () const
+    {
+      return m_rect.m_size.w == 1 && m_rect.m_size.h == 1;
+    }
+
+    canvas::size_t get_min_canvas_size () const
+    {
+      // Doesn't include border
+      return m_content.get_canvas_size ();
+    }
+
+    void paint_cell_contents_to_canvas(canvas &canvas,
+				       canvas::coord_t offset,
+				       const table_geometry &tg) const;
+
+    const table_cell_content &get_content () const { return m_content; }
+
+  private:
+    friend class table_cell_sizes;
+    rect_t m_rect;
+    table_cell_content m_content;
+    x_align m_x_align;
+    y_align m_y_align;
+  };
+
+  table (size_t size);
+  ~table () = default;
+  table (table &&) = default;
+  table (const table &) = delete;
+  table &operator= (const table &) = delete;
+
+  const size_t &get_size () const { return m_size; }
+
+  int add_row ()
+  {
+    m_size.h++;
+    m_occupancy.add_row (-1);
+    return m_size.h - 1; // return the table_y of the newly-added row
+  }
+
+  void set_cell (coord_t coord,
+		 table_cell_content &&content,
+		 enum x_align x_align = x_align::CENTER,
+		 enum y_align y_align = y_align::CENTER);
+
+  void set_cell_span (rect_t span,
+		      table_cell_content &&content,
+		      enum x_align x_align = x_align::CENTER,
+		      enum y_align y_align = y_align::CENTER);
+
+  canvas to_canvas (const theme &theme, const style_manager &sm) const;
+
+  void paint_to_canvas(canvas &canvas,
+		       canvas::coord_t offset,
+		       const table_geometry &tg,
+		       const theme &theme) const;
+
+  void debug () const;
+
+  /* Self-test support.  */
+  const cell_placement *get_placement_at (coord_t coord) const;
+
+ private:
+  int get_occupancy_safe (coord_t coord) const;
+  directions get_connections (int table_x, int table_y) const;
+  void paint_cell_borders_to_canvas(canvas &canvas,
+				    canvas::coord_t offset,
+				    const table_geometry &tg,
+				    const theme &theme) const;
+  void paint_cell_contents_to_canvas(canvas &canvas,
+				     canvas::coord_t offset,
+				     const table_geometry &tg) const;
+
+  friend class table_cell_sizes;
+
+  size_t m_size;
+  std::vector<cell_placement> m_placements;
+  array2<int, size_t, coord_t> m_occupancy; /* indices into the m_placements vec.  */
+};
+
+/* A workspace for computing the row heights and column widths
+   of a table (in canvas units).
+   The col_widths and row_heights could be shared between multiple
+   instances, for aligning multiple tables vertically or horizontally.  */
+
+class table_cell_sizes
+{
+ public:
+  table_cell_sizes (table_dimension_sizes &col_widths,
+		    table_dimension_sizes &row_heights)
+  : m_col_widths (col_widths),
+    m_row_heights (row_heights)
+  {
+  }
+
+  void pass_1 (const table &table);
+  void pass_2 (const table &table);
+
+  canvas::size_t get_canvas_size (const table::rect_t &rect) const;
+
+  table_dimension_sizes &m_col_widths;
+  table_dimension_sizes &m_row_heights;
+};
+
+/* A class responsible for mapping from table cell coords
+   to canvas coords, handling column widths.
+   It's the result of solving "how big are all the table cells and where
+   do they go?"
+   The cell_sizes are passed in, for handling aligning multiple tables,
+   sharing column widths or row heights.  */
+
+class table_geometry
+{
+ public:
+  table_geometry (const table &table, table_cell_sizes &cell_sizes);
+
+  void recalc_coords ();
+
+  const canvas::size_t get_canvas_size () const { return m_canvas_size; }
+
+  canvas::coord_t table_to_canvas (table::coord_t table_coord) const;
+  int table_x_to_canvas_x (int table_x) const;
+  int table_y_to_canvas_y (int table_y) const;
+
+  int get_col_width (int table_x) const
+  {
+    return m_cell_sizes.m_col_widths.m_requirements[table_x];
+  }
+
+  canvas::size_t get_canvas_size (const table::rect_t &rect) const
+  {
+    return m_cell_sizes.get_canvas_size (rect);
+  }
+
+ private:
+  const table &m_table;
+  table_cell_sizes &m_cell_sizes;
+  canvas::size_t m_canvas_size;
+
+  /* Start canvas column of table cell, including leading border.  */
+  std::vector<int> m_col_start_x;
+
+  /* Start canvas row of table cell, including leading border.  */
+  std::vector<int> m_row_start_y;
+};
+
+/* Helper class for handling the simple case of a single table
+   that doesn't need to be aligned with respect to anything else.  */
+
+struct simple_table_geometry
+{
+  simple_table_geometry (const table &table);
+
+  table_dimension_sizes m_col_widths;
+  table_dimension_sizes m_row_heights;
+  table_cell_sizes m_cell_sizes;
+  table_geometry m_tg;
+};
+
+} // namespace text_art
+
+#endif /* GCC_TEXT_ART_TABLE_H */
diff --git a/gcc/text-art/theme.cc b/gcc/text-art/theme.cc
new file mode 100644
index 0000000..19c39fa
--- /dev/null
+++ b/gcc/text-art/theme.cc
@@ -0,0 +1,184 @@
+/* Classes for abstracting ascii vs unicode output.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#define INCLUDE_VECTOR
+#include "system.h"
+#include "coretypes.h"
+#include "pretty-print.h"
+#include "selftest.h"
+#include "text-art/selftests.h"
+#include "text-art/ruler.h"
+#include "text-art/theme.h"
+
+using namespace text_art;
+
+/* class theme.  */
+
+void
+theme::paint_y_arrow (canvas &canvas,
+		      int canvas_x,
+		      canvas::range_t y_range,
+		      y_arrow_dir dir,
+		      style::id_t style_id) const
+{
+  int canvas_y;
+  int delta_y;
+  const canvas::cell_t head (get_cppchar (dir == y_arrow_dir::UP
+					  ? cell_kind::Y_ARROW_UP_HEAD
+					  : cell_kind::Y_ARROW_DOWN_HEAD),
+			     false, style_id);
+  const canvas::cell_t tail (get_cppchar (dir == y_arrow_dir::UP
+					  ? cell_kind::Y_ARROW_UP_TAIL
+					  : cell_kind::Y_ARROW_DOWN_TAIL),
+			     false, style_id);
+  if (dir == y_arrow_dir::UP)
+    {
+      canvas_y = y_range.get_max ();
+      delta_y = -1;
+    }
+  else
+    {
+      canvas_y = y_range.get_min ();
+      delta_y = 1;
+    }
+  for (int len = y_range.get_size (); len; len--)
+    {
+      const canvas::cell_t cell = (len > 1) ? tail : head;
+      canvas.paint (canvas::coord_t (canvas_x, canvas_y), cell);
+      canvas_y += delta_y;
+    }
+}
+
+/* class ascii_theme : public theme.  */
+
+canvas::cell_t
+ascii_theme::get_line_art (directions line_dirs) const
+{
+  if (line_dirs.m_up
+      && line_dirs.m_down
+      && !(line_dirs.m_left || line_dirs.m_right))
+    return canvas::cell_t ('|');
+  if (line_dirs.m_left
+      && line_dirs.m_right
+      && !(line_dirs.m_up || line_dirs.m_down))
+    return canvas::cell_t ('-');
+  if (line_dirs.m_up
+      || line_dirs.m_down
+      || line_dirs.m_left
+      || line_dirs.m_right)
+    return canvas::cell_t ('+');
+  return canvas::cell_t (' ');
+}
+
+cppchar_t
+ascii_theme::get_cppchar (enum cell_kind kind) const
+{
+  switch (kind)
+    {
+    default:
+      gcc_unreachable ();
+    case cell_kind::X_RULER_LEFT_EDGE:
+      return '|';
+    case cell_kind::X_RULER_MIDDLE:
+      return '~';
+    case cell_kind::X_RULER_INTERNAL_EDGE:
+      return '|';
+    case cell_kind::X_RULER_CONNECTOR_TO_LABEL_BELOW:
+    case cell_kind::X_RULER_CONNECTOR_TO_LABEL_ABOVE:
+      return '+';
+    case cell_kind::X_RULER_RIGHT_EDGE:
+      return '|';
+    case cell_kind::X_RULER_VERTICAL_CONNECTOR:
+      return '|';
+
+    case cell_kind::TEXT_BORDER_HORIZONTAL:
+      return '-';
+    case cell_kind::TEXT_BORDER_VERTICAL:
+      return '|';
+    case cell_kind::TEXT_BORDER_TOP_LEFT:
+    case cell_kind::TEXT_BORDER_TOP_RIGHT:
+    case cell_kind::TEXT_BORDER_BOTTOM_LEFT:
+    case cell_kind::TEXT_BORDER_BOTTOM_RIGHT:
+      return '+';
+
+    case cell_kind::Y_ARROW_UP_HEAD: return '^';
+    case cell_kind::Y_ARROW_DOWN_HEAD: return 'v';
+
+    case cell_kind::Y_ARROW_UP_TAIL:
+    case cell_kind::Y_ARROW_DOWN_TAIL:
+      return '|';
+    }
+}
+
+/* class unicode_theme : public theme.  */
+
+canvas::cell_t
+unicode_theme::get_line_art (directions line_dirs) const
+{
+  return canvas::cell_t (get_box_drawing_char (line_dirs));
+}
+
+cppchar_t
+unicode_theme::get_cppchar (enum cell_kind kind) const
+{
+  switch (kind)
+    {
+    default:
+      gcc_unreachable ();
+    case cell_kind::X_RULER_LEFT_EDGE:
+      return 0x251C; /* "├": U+251C: BOX DRAWINGS LIGHT VERTICAL AND RIGHT */
+    case cell_kind::X_RULER_MIDDLE:
+      return 0x2500; /* "─": U+2500: BOX DRAWINGS LIGHT HORIZONTAL */
+    case cell_kind::X_RULER_INTERNAL_EDGE:
+      return 0x253C; /* "┼": U+253C: BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */
+    case cell_kind::X_RULER_CONNECTOR_TO_LABEL_BELOW:
+      return 0x252C; /* "┬": U+252C: BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */
+    case cell_kind::X_RULER_CONNECTOR_TO_LABEL_ABOVE:
+      return 0x2534; /* "┴": U+2534: BOX DRAWINGS LIGHT UP AND HORIZONTAL */
+    case cell_kind::X_RULER_RIGHT_EDGE:
+      return 0x2524; /* "┤": U+2524: BOX DRAWINGS LIGHT VERTICAL AND LEFT */
+    case cell_kind::X_RULER_VERTICAL_CONNECTOR:
+      return 0x2502; /* "│": U+2502: BOX DRAWINGS LIGHT VERTICAL */
+
+    case cell_kind::TEXT_BORDER_HORIZONTAL:
+      return 0x2500; /* "─": U+2500: BOX DRAWINGS LIGHT HORIZONTAL */
+    case cell_kind::TEXT_BORDER_VERTICAL:
+      return 0x2502; /* "│": U+2502: BOX DRAWINGS LIGHT VERTICAL */
+
+    /* Round corners.  */
+    case cell_kind::TEXT_BORDER_TOP_LEFT:
+      return 0x256D; /* "╭": U+256D BOX DRAWINGS LIGHT ARC DOWN AND RIGHT.  */
+    case cell_kind::TEXT_BORDER_TOP_RIGHT:
+      return 0x256E; /* "╮": U+256E BOX DRAWINGS LIGHT ARC DOWN AND LEFT.  */
+    case cell_kind::TEXT_BORDER_BOTTOM_LEFT:
+      return 0x2570; /* "╰": U+2570 BOX DRAWINGS LIGHT ARC UP AND RIGHT.  */
+    case cell_kind::TEXT_BORDER_BOTTOM_RIGHT:
+      return 0x256F; /* "╯": U+256F BOX DRAWINGS LIGHT ARC UP AND LEFT.  */
+
+    case cell_kind::Y_ARROW_UP_HEAD:
+      return '^';
+    case cell_kind::Y_ARROW_DOWN_HEAD:
+      return 'v';
+    case cell_kind::Y_ARROW_UP_TAIL:
+    case cell_kind::Y_ARROW_DOWN_TAIL:
+      return 0x2502; /* "│": U+2502: BOX DRAWINGS LIGHT VERTICAL */
+    }
+}
diff --git a/gcc/text-art/theme.h b/gcc/text-art/theme.h
new file mode 100644
index 0000000..8edbc6e
--- /dev/null
+++ b/gcc/text-art/theme.h
@@ -0,0 +1,123 @@
+/* Classes for abstracting ascii vs unicode output.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_TEXT_ART_THEME_H
+#define GCC_TEXT_ART_THEME_H
+
+#include "text-art/canvas.h"
+#include "text-art/box-drawing.h"
+
+namespace text_art {
+
+class theme
+{
+ public:
+  enum class cell_kind
+  {
+    /* A left-hand edge of a range e.g. "├".  */
+    X_RULER_LEFT_EDGE,
+
+    /* Within a range e.g. "─".  */
+    X_RULER_MIDDLE,
+
+    /* A border between two neighboring ranges e.g. "┼".  */
+    X_RULER_INTERNAL_EDGE,
+
+    /* The connector with the text label within a range e.g. "┬".  */
+    X_RULER_CONNECTOR_TO_LABEL_BELOW,
+
+    /* As above, but when the text label is above the ruler.  */
+    X_RULER_CONNECTOR_TO_LABEL_ABOVE,
+
+    /* The vertical connection to a text label.  */
+    X_RULER_VERTICAL_CONNECTOR,
+
+    /* A right-hand edge of a range e.g. "┤".  */
+    X_RULER_RIGHT_EDGE,
+
+    TEXT_BORDER_HORIZONTAL,
+    TEXT_BORDER_VERTICAL,
+    TEXT_BORDER_TOP_LEFT,
+    TEXT_BORDER_TOP_RIGHT,
+    TEXT_BORDER_BOTTOM_LEFT,
+    TEXT_BORDER_BOTTOM_RIGHT,
+
+    Y_ARROW_UP_HEAD,
+    Y_ARROW_UP_TAIL,
+    Y_ARROW_DOWN_HEAD,
+    Y_ARROW_DOWN_TAIL,
+  };
+
+  virtual ~theme () = default;
+
+  virtual bool unicode_p () const = 0;
+  virtual bool emojis_p () const = 0;
+
+  virtual canvas::cell_t
+  get_line_art (directions line_dirs) const = 0;
+
+  canvas::cell_t get_cell (enum cell_kind kind, unsigned style_idx) const
+  {
+    return canvas::cell_t (get_cppchar (kind), false, style_idx);
+  }
+
+  virtual cppchar_t get_cppchar (enum cell_kind kind) const = 0;
+
+  enum class y_arrow_dir { UP, DOWN };
+  void paint_y_arrow (canvas &canvas,
+		      int x,
+		      canvas::range_t y_range,
+		      y_arrow_dir dir,
+		      style::id_t style_id) const;
+};
+
+class ascii_theme : public theme
+{
+ public:
+  bool unicode_p () const final override { return false; }
+  bool emojis_p () const final override { return false; }
+
+  canvas::cell_t
+  get_line_art (directions line_dirs) const final override;
+
+  cppchar_t get_cppchar (enum cell_kind kind) const final override;
+};
+
+class unicode_theme : public theme
+{
+ public:
+  bool unicode_p () const final override { return true; }
+  bool emojis_p () const override { return false; }
+
+  canvas::cell_t
+  get_line_art (directions line_dirs) const final override;
+
+  cppchar_t get_cppchar (enum cell_kind kind) const final override;
+};
+
+class emoji_theme : public unicode_theme
+{
+public:
+  bool emojis_p () const final override { return true; }
+};
+
+} // namespace text_art
+
+#endif /* GCC_TEXT_ART_THEME_H */
diff --git a/gcc/text-art/types.h b/gcc/text-art/types.h
new file mode 100644
index 0000000..ea4ff4b
--- /dev/null
+++ b/gcc/text-art/types.h
@@ -0,0 +1,510 @@
+/* Types for drawing 2d "text art".
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_TEXT_ART_TYPES_H
+#define GCC_TEXT_ART_TYPES_H
+
+/* This header uses std::vector, but <vector> can't be directly
+   included due to issues with macros.  Hence it must be included from
+   system.h by defining INCLUDE_MEMORY in any source file using it.  */
+
+#ifndef INCLUDE_VECTOR
+# error "You must define INCLUDE_VECTOR before including system.h to use text-art/types.h"
+#endif
+
+#include "cpplib.h"
+#include "pretty-print.h"
+
+namespace text_art {
+
+/* Forward decls.  */
+
+class canvas;
+class table;
+class theme;
+
+/* Classes for geometry.
+   We use templates to avoid mixing up e.g. canvas coordinates
+   with table coordinates.  */
+
+template <typename CoordinateSystem>
+struct size
+{
+  size (int w_, int h_) : w (w_), h (h_) {}
+  int w;
+  int h;
+};
+
+template <typename CoordinateSystem>
+struct coord
+{
+  coord (int x_, int y_) : x (x_), y (y_) {}
+  int x;
+  int y;
+};
+
+template <typename CoordinateSystem>
+coord<CoordinateSystem> operator+ (coord<CoordinateSystem> a,
+				   coord<CoordinateSystem> b)
+{
+  return coord<CoordinateSystem> (a.x + b.x, a.y + b.y);
+}
+
+/* A half-open range [start, next) of int.  */
+
+template <typename CoordinateSystem>
+struct range
+{
+  range (int start_, int next_)
+  : start (start_), next (next_)
+  {}
+
+  int get_min () const { return start; }
+  int get_max () const { return next - 1; }
+  int get_next () const { return next; }
+  int get_size () const { return next - start; }
+
+  int get_midpoint () const { return get_min () + get_size () / 2; }
+
+  int start;
+  int next;
+};
+
+/* A rectangle area within CoordinateSystem.  */
+
+template <typename CoordinateSystem>
+struct rect
+{
+  rect (coord<CoordinateSystem> top_left,
+	size<CoordinateSystem> size)
+  : m_top_left (top_left),
+    m_size (size)
+  {
+  }
+
+  rect (range<CoordinateSystem> x_range,
+	range<CoordinateSystem> y_range)
+  : m_top_left (x_range.get_min (), y_range.get_min ()),
+    m_size (x_range.get_size (), y_range.get_size ())
+  {
+  }
+
+  int get_min_x () const { return m_top_left.x; }
+  int get_min_y () const { return m_top_left.y; }
+  int get_max_x () const { return m_top_left.x + m_size.w - 1; }
+  int get_max_y () const { return m_top_left.y + m_size.h - 1; }
+  int get_next_x () const { return m_top_left.x + m_size.w; }
+  int get_next_y () const { return m_top_left.y + m_size.h; }
+
+  range<CoordinateSystem> get_x_range () const
+  {
+    return range<CoordinateSystem> (get_min_x (), get_next_x ());
+  }
+  range<CoordinateSystem> get_y_range () const
+  {
+    return range<CoordinateSystem> (get_min_y (), get_next_y ());
+  }
+
+  int get_width () const { return m_size.w; }
+  int get_height () const { return m_size.h; }
+
+  coord<CoordinateSystem> m_top_left;
+  size<CoordinateSystem> m_size;
+};
+
+template <typename ElementType, typename SizeType, typename CoordType>
+class array2
+{
+ public:
+  typedef ElementType element_t;
+  typedef SizeType size_t;
+  typedef CoordType coord_t;
+
+  array2 (size_t sz)
+  : m_size (sz),
+    m_elements (sz.w * sz.h)
+  {
+  }
+  array2 (array2 &&other)
+  : m_size (other.m_size),
+    m_elements (std::move (other.m_elements))
+  {
+  }
+
+  /* Move assignment not implemented or used.  */
+  array2 &operator== (array2 &&other) = delete;
+
+  /* No copy ctor or assignment op.  */
+  array2 (const array2 &other) = delete;
+  array2 &operator= (const array2 &other) = delete;
+
+
+  const size_t &get_size () const { return m_size; }
+
+  void add_row (const element_t &element)
+  {
+    m_size.h++;
+    m_elements.insert (m_elements.end (), m_size.w, element);
+  }
+
+  const element_t &get (const coord_t &coord) const
+  {
+    ::size_t idx = get_idx (coord);
+    return m_elements[idx];
+  }
+
+  void set (const coord_t &coord, const element_t &element)
+  {
+    ::size_t idx = get_idx (coord);
+    m_elements[idx] = element;
+  }
+
+  void fill (element_t element)
+  {
+    for (int y = 0; y < m_size.h; y++)
+      for (int x = 0; x < m_size.w; x++)
+	set (coord_t (x, y), element);
+  }
+
+ private:
+  ::size_t get_idx (const coord_t &coord) const
+  {
+    gcc_assert (coord.x >= 0);
+    gcc_assert (coord.x < m_size.w);
+    gcc_assert (coord.y >= 0);
+    gcc_assert (coord.y < m_size.h);
+    return (coord.y * m_size.w) + coord.x;
+  }
+
+  size_t m_size;
+  std::vector<element_t> m_elements;
+};
+
+/* A combination of attributes describing how to style a text cell.
+   We only support those attributes mentioned in invoke.texi:
+   - bold,
+   - underscore,
+   - blink,
+   - inverse,
+   - colors for foreground and background:
+     - default color
+     - named colors
+     - 16-color mode colors (the "bright" variants)
+     - 88-color mode
+     - 256-color mode
+   plus URLs. */
+
+struct style
+{
+  typedef unsigned char id_t;
+  static const id_t id_plain = 0;
+
+  /* Colors.  */
+  enum class named_color
+  {
+   DEFAULT,
+   // ANSI order
+   BLACK,
+   RED,
+   GREEN,
+   YELLOW,
+   BLUE,
+   MAGENTA,
+   CYAN,
+   WHITE
+  };
+
+
+  struct color
+  {
+    enum class kind
+    {
+      NAMED,
+      BITS_8,
+      BITS_24,
+    } m_kind;
+
+    union
+    {
+      struct {
+	enum named_color m_name;
+	bool m_bright;
+      } m_named;
+      uint8_t m_8bit;
+      struct {
+	uint8_t r;
+	uint8_t g;
+	uint8_t b;
+      } m_24bit;
+    } u;
+
+    /* Constructor for named colors.  */
+    color (enum named_color name = named_color::DEFAULT,
+	   bool bright = false)
+    : m_kind (kind::NAMED)
+    {
+      u.m_named.m_name = name;
+      u.m_named.m_bright = bright;
+    }
+
+    /* Constructor for 8-bit colors.  */
+    color (uint8_t col_val)
+    : m_kind (kind::BITS_8)
+    {
+      u.m_8bit = col_val;
+    }
+
+    /* Constructor for 24-bit colors.  */
+    color (uint8_t r, uint8_t g, uint8_t b)
+    : m_kind (kind::BITS_24)
+    {
+      u.m_24bit.r = r;
+      u.m_24bit.g = g;
+      u.m_24bit.b = b;
+    }
+
+    bool operator== (const color &other) const;
+    bool operator!= (const color &other) const
+    {
+      return !(*this == other);
+    }
+
+    void print_sgr (pretty_printer *pp, bool fg, bool &need_separator) const;
+  };
+
+  style ()
+  : m_bold (false),
+    m_underscore (false),
+    m_blink (false),
+    m_reverse (false),
+    m_fg_color (named_color::DEFAULT),
+    m_bg_color (named_color::DEFAULT),
+    m_url ()
+  {}
+
+  bool operator== (const style &other) const
+  {
+    return (m_bold == other.m_bold
+	    && m_underscore == other.m_underscore
+	    && m_blink == other.m_blink
+	    && m_reverse == other.m_reverse
+	    && m_fg_color == other.m_fg_color
+	    && m_bg_color == other.m_bg_color
+	    && m_url == other.m_url);
+  }
+
+  style &set_style_url (const char *url);
+
+  static void print_changes (pretty_printer *pp,
+			     const style &old_style,
+			     const style &new_style);
+
+  bool m_bold;
+  bool m_underscore;
+  bool m_blink;
+  bool m_reverse;
+  color m_fg_color;
+  color m_bg_color;
+  std::vector<cppchar_t> m_url; // empty = no URL
+};
+
+/* A class to keep track of all the styles in use in a drawing, so that
+   we can refer to them via the compact style::id_t type, rather than
+   via e.g. pointers.  */
+
+class style_manager
+{
+ public:
+  style_manager ();
+  style::id_t get_or_create_id (const style &style);
+  const style &get_style (style::id_t id) const
+  {
+    return m_styles[id];
+  }
+  void print_any_style_changes (pretty_printer *pp,
+				style::id_t old_id,
+				style::id_t new_id) const;
+  unsigned get_num_styles () const { return m_styles.size (); }
+
+private:
+  std::vector<style> m_styles;
+};
+
+class styled_unichar
+{
+ public:
+  friend class styled_string;
+
+  explicit styled_unichar ()
+  : m_code (0),
+    m_style_id (style::id_plain)
+  {}
+  explicit styled_unichar (cppchar_t ch)
+  : m_code (ch),
+    m_emoji_variant_p (false),
+    m_style_id (style::id_plain)
+  {}
+  explicit styled_unichar (cppchar_t ch, bool emoji, style::id_t style_id)
+  : m_code (ch),
+    m_emoji_variant_p (emoji),
+    m_style_id (style_id)
+  {
+    gcc_assert (style_id <= 0x7f);
+  }
+
+  cppchar_t get_code () const { return m_code; }
+  bool emoji_variant_p () const { return m_emoji_variant_p; }
+  style::id_t get_style_id () const { return m_style_id; }
+
+  bool double_width_p () const
+  {
+    int width = cpp_wcwidth (get_code ());
+    gcc_assert (width == 1 || width == 2);
+    return width == 2;
+  }
+
+  bool operator== (const styled_unichar &other) const
+  {
+    return (m_code == other.m_code
+	    && m_emoji_variant_p == other.m_emoji_variant_p
+	    && m_style_id == other.m_style_id);
+  }
+
+  void set_emoji_variant () { m_emoji_variant_p = true; }
+
+  int get_canvas_width () const
+  {
+      return cpp_wcwidth (m_code);
+  }
+
+  void add_combining_char (cppchar_t ch)
+  {
+    m_combining_chars.push_back (ch);
+  }
+
+  const std::vector<cppchar_t> get_combining_chars () const
+  {
+    return m_combining_chars;
+  }
+
+private:
+  cppchar_t m_code : 24;
+  bool m_emoji_variant_p : 1;
+  style::id_t m_style_id : 7;
+  std::vector<cppchar_t> m_combining_chars;
+};
+
+class styled_string
+{
+ public:
+  explicit styled_string () = default;
+  explicit styled_string (style_manager &sm, const char *str);
+  explicit styled_string (cppchar_t cppchar, bool emoji = false);
+
+  styled_string (styled_string &&) = default;
+  styled_string &operator= (styled_string &&) = default;
+
+  /* No copy ctor or assignment op.  */
+  styled_string (const styled_string &) = delete;
+  styled_string &operator= (const styled_string &) = delete;
+
+  /* For the few cases where copying is required, spell it out explicitly.  */
+  styled_string copy () const
+  {
+    styled_string result;
+    result.m_chars = m_chars;
+    return result;
+  }
+
+  bool operator== (const styled_string &other) const
+  {
+    return m_chars == other.m_chars;
+  }
+
+  static styled_string from_fmt (style_manager &sm,
+				 printer_fn format_decoder,
+				 const char *fmt, ...)
+    ATTRIBUTE_GCC_PPDIAG(3, 4);
+  static styled_string from_fmt_va (style_manager &sm,
+				    printer_fn format_decoder,
+				    const char *fmt,
+				    va_list *args)
+    ATTRIBUTE_GCC_PPDIAG(3, 0);
+
+  size_t size () const { return m_chars.size (); }
+  styled_unichar operator[] (size_t idx) const { return m_chars[idx]; }
+
+  std::vector<styled_unichar>::const_iterator begin () const
+  {
+    return m_chars.begin ();
+  }
+  std::vector<styled_unichar>::const_iterator end () const
+  {
+    return m_chars.end ();
+  }
+
+  int calc_canvas_width () const;
+
+  void append (const styled_string &suffix);
+
+  void set_url (style_manager &sm, const char *url);
+
+private:
+  std::vector<styled_unichar> m_chars;
+};
+
+enum class x_align
+{
+  LEFT,
+  CENTER,
+  RIGHT
+};
+
+enum class y_align
+{
+  TOP,
+  CENTER,
+  BOTTOM
+};
+
+/* A set of cardinal directions within a canvas or table.  */
+
+struct directions
+{
+public:
+  directions (bool up, bool down, bool left, bool right)
+  : m_up (up), m_down (down), m_left (left), m_right (right)
+  {
+  }
+
+  size_t as_index () const
+  {
+    return (m_up << 3) | (m_down << 2) | (m_left << 1) | m_right;
+  }
+
+  bool m_up: 1;
+  bool m_down: 1;
+  bool m_left: 1;
+  bool m_right: 1;
+};
+
+} // namespace text_art
+
+#endif /* GCC_TEXT_ART_TYPES_H */
diff --git a/gcc/text-art/widget.cc b/gcc/text-art/widget.cc
new file mode 100644
index 0000000..b64a623
--- /dev/null
+++ b/gcc/text-art/widget.cc
@@ -0,0 +1,276 @@
+/* Hierarchical diagram elements.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#define INCLUDE_MEMORY
+#define INCLUDE_VECTOR
+#include "system.h"
+#include "coretypes.h"
+#include "pretty-print.h"
+#include "selftest.h"
+#include "make-unique.h"
+#include "text-art/selftests.h"
+#include "text-art/widget.h"
+
+using namespace text_art;
+
+/* class text_art::widget.  */
+
+canvas
+widget::to_canvas (const style_manager &style_mgr)
+{
+  const canvas::size_t req_size = get_req_size ();
+
+  /* For now we don't constrain the allocation; we give
+     the widget the full size it requested, and widgets
+     assume they got their full size request.  */
+  const canvas::size_t alloc_size = req_size;
+
+  set_alloc_rect (canvas::rect_t (canvas::coord_t (0, 0), alloc_size));
+  canvas c (alloc_size, style_mgr);
+  paint_to_canvas (c);
+  return c;
+}
+
+/* class text_art::vbox_widget : public text_art::container_widget.  */
+
+const char *
+vbox_widget::get_desc () const
+{
+  return "vbox_widget";
+}
+
+canvas::size_t
+vbox_widget::calc_req_size ()
+{
+  canvas::size_t result (0, 0);
+  for (auto &child : m_children)
+    {
+      canvas::size_t child_req_size = child->get_req_size();
+      result.h += child_req_size.h;
+      result.w = std::max (result.w, child_req_size.w);
+    }
+  return result;
+}
+
+void
+vbox_widget::update_child_alloc_rects ()
+{
+  const int x = get_min_x ();
+  int y = get_min_y ();
+  for (auto &child : m_children)
+    {
+      child->set_alloc_rect
+	(canvas::rect_t (canvas::coord_t (x, y),
+			 canvas::size_t (get_alloc_w (),
+					 child->get_req_h ())));
+      y += child->get_req_h ();
+    }
+}
+
+/* class text_art::text_widget : public text_art::leaf_widget.  */
+
+const char *
+text_widget::get_desc () const
+{
+  return "text_widget";
+}
+
+canvas::size_t
+text_widget::calc_req_size ()
+{
+  return canvas::size_t (m_str.size (), 1);
+}
+
+void
+text_widget::paint_to_canvas (canvas &canvas)
+{
+  canvas.paint_text (get_top_left (), m_str);
+}
+
+/* class text_art::canvas_widget : public text_art::leaf_widget.  */
+
+const char *
+canvas_widget::get_desc () const
+{
+  return "canvas_widget";
+}
+
+canvas::size_t
+canvas_widget::calc_req_size ()
+{
+  return m_canvas.get_size ();
+}
+
+void
+canvas_widget::paint_to_canvas (canvas &canvas)
+{
+  for (int y = 0; y < m_canvas.get_size ().h; y++)
+    for (int x = 0; x < m_canvas.get_size ().w; x++)
+      {
+	canvas::coord_t rel_xy (x, y);
+	canvas.paint (get_top_left () + rel_xy,
+		      m_canvas.get (rel_xy));
+      }
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Concrete widget subclass for writing selftests.
+   Requests a hard-coded size, and fills its allocated rectangle
+   with a specific character.  */
+
+class test_widget : public leaf_widget
+{
+public:
+  test_widget (canvas::size_t size, char ch)
+  : m_test_size (size), m_ch (ch)
+  {}
+
+  const char *get_desc () const final override
+  {
+    return "test_widget";
+  }
+  canvas::size_t calc_req_size () final override
+  {
+    return m_test_size;
+  }
+  void paint_to_canvas (canvas &canvas) final override
+  {
+    canvas.fill (get_alloc_rect (), canvas::cell_t (m_ch));
+  }
+
+private:
+  canvas::size_t m_test_size;
+  char m_ch;
+};
+
+static void
+test_test_widget ()
+{
+  style_manager sm;
+  test_widget w (canvas::size_t (3, 3), 'A');
+  canvas c (w.to_canvas (sm));
+  ASSERT_CANVAS_STREQ
+    (c, false,
+     ("AAA\n"
+      "AAA\n"
+      "AAA\n"));
+}
+
+static void
+test_text_widget ()
+{
+  style_manager sm;
+  text_widget w (styled_string (sm, "hello world"));
+  canvas c (w.to_canvas (sm));
+  ASSERT_CANVAS_STREQ
+    (c, false,
+     ("hello world\n"));
+}
+
+static void
+test_wrapper_widget ()
+{
+  style_manager sm;
+  wrapper_widget w (::make_unique<test_widget> (canvas::size_t (3, 3), 'B'));
+  canvas c (w.to_canvas (sm));
+  ASSERT_CANVAS_STREQ
+    (c, false,
+     ("BBB\n"
+      "BBB\n"
+      "BBB\n"));
+}
+
+static void
+test_vbox_1 ()
+{
+  style_manager sm;
+  vbox_widget w;
+  for (int i = 0; i < 5; i++)
+    w.add_child
+      (::make_unique <text_widget>
+       (styled_string::from_fmt (sm, nullptr,
+				 "this is line %i", i)));
+  canvas c (w.to_canvas (sm));
+  ASSERT_CANVAS_STREQ
+    (c, false,
+     ("this is line 0\n"
+      "this is line 1\n"
+      "this is line 2\n"
+      "this is line 3\n"
+      "this is line 4\n"));
+}
+
+static void
+test_vbox_2 ()
+{
+  style_manager sm;
+  vbox_widget w;
+  w.add_child (::make_unique<test_widget> (canvas::size_t (1, 3), 'A'));
+  w.add_child (::make_unique<test_widget> (canvas::size_t (4, 1), 'B'));
+  w.add_child (::make_unique<test_widget> (canvas::size_t (1, 2), 'C'));
+  canvas c (w.to_canvas (sm));
+  ASSERT_CANVAS_STREQ
+    (c, false,
+     ("AAAA\n"
+      "AAAA\n"
+      "AAAA\n"
+      "BBBB\n"
+      "CCCC\n"
+      "CCCC\n"));
+}
+
+static void
+test_canvas_widget ()
+{
+  style_manager sm;
+  canvas inner_canvas (canvas::size_t (5, 3), sm);
+  inner_canvas.fill (canvas::rect_t (canvas::coord_t (0, 0),
+				     canvas::size_t (5, 3)),
+		     canvas::cell_t ('a'));
+  canvas_widget cw (std::move (inner_canvas));
+  canvas c (cw.to_canvas (sm));
+  ASSERT_CANVAS_STREQ
+    (c, false,
+     ("aaaaa\n"
+      "aaaaa\n"
+      "aaaaa\n"));
+}
+
+/* Run all selftests in this file.  */
+
+void
+text_art_widget_cc_tests ()
+{
+  test_test_widget ();
+  test_text_widget ();
+  test_wrapper_widget ();
+  test_vbox_1 ();
+  test_vbox_2 ();
+  test_canvas_widget ();
+}
+
+} // namespace selftest
+
+
+#endif /* #if CHECKING_P */
diff --git a/gcc/text-art/widget.h b/gcc/text-art/widget.h
new file mode 100644
index 0000000..8798e43
--- /dev/null
+++ b/gcc/text-art/widget.h
@@ -0,0 +1,245 @@
+/* Hierarchical diagram elements.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_TEXT_ART_WIDGET_H
+#define GCC_TEXT_ART_WIDGET_H
+
+#include "text-art/canvas.h"
+#include "text-art/table.h"
+
+namespace text_art {
+
+/* Abstract base class: something that knows how to size itself and
+   how to paint itself to a canvas, potentially with children, with
+   support for hierarchical sizing and positioning.
+
+   Widgets have a two-phase sizing/positioning algorithm.
+
+   Step 1: size requests: the root widget is asked for its size request i.e
+   how big it wants to be.  This is handled by recursively asking child
+   widgets for their requested sizes.  Each widget subclass can implement
+   their own logic for this in the "calc_req_size" vfunc, and the result
+   is cached in m_req_size.
+
+   Step 2: rect allocation: the root widget is set a canvas::rect_t as
+   its "allocated" rectangle.  Each widget subclass can then place its
+   children recursively using the "update_child_alloc_rects" vfunc.
+   For simplicity, all coordinates in the hierarchy are within the same
+   coordinate system (rather than attempting to store per-child offsets).
+
+   Widget subclasses are responsible for managing their own children.  */
+
+/* Subclasses in this header, with indentation indicating inheritance.  */
+
+class widget;  /* Abstract base class.  */
+  class wrapper_widget;  /* Concrete subclass: a widget with a single child.  */
+  class container_widget; /* Abstract subclass: widgets with an arbitrary
+			     number of children.  */
+    class vbox_widget; /* Concrete widget subclass: lay out children
+			  vertically.  */
+  class leaf_widget; /* Abstract subclass: a widget with no children.  */
+    class text_widget; /* Concrete subclass: a text string.  */
+    class canvas_widget; /* Concrete subclass: a pre-rendered canvas.  */
+
+class widget
+{
+ public:
+  /* This can be very useful for debugging when implementing new
+     widget subclasses.  */
+  static const bool DEBUG_GEOMETRY = false;
+
+  virtual ~widget () {}
+
+  canvas to_canvas (const style_manager &style_mgr);
+
+  canvas::size_t get_req_size ()
+  {
+    m_req_size = calc_req_size();
+    if (DEBUG_GEOMETRY)
+      fprintf (stderr, "calc_req_size (%s) -> (w:%i, h:%i)\n",
+	       get_desc (),
+	       m_req_size.w, m_req_size.h);
+    return m_req_size;
+  }
+
+  void set_alloc_rect (const canvas::rect_t &rect)
+  {
+    if (DEBUG_GEOMETRY)
+      fprintf (stderr, "set_alloc_rect (%s): ((x:%i, y:%i), (w:%i, h:%i))\n",
+	       get_desc (),
+	       rect.m_top_left.x, rect.m_top_left.y,
+	       rect.m_size.w, rect.m_size.h);
+    m_alloc_rect = rect;
+    update_child_alloc_rects ();
+  }
+
+  virtual const char *get_desc () const = 0;
+  virtual canvas::size_t calc_req_size () = 0;
+  virtual void update_child_alloc_rects () = 0;
+  virtual void paint_to_canvas (canvas &canvas) = 0;
+
+  /* Access to the cached size request of this widget.  */
+  const canvas::size_t get_req_size () const { return m_req_size; }
+  int get_req_w () const { return m_req_size.w; }
+  int get_req_h () const { return m_req_size.h; }
+
+  /* Access to the allocated canvas coordinates of this widget.  */
+  const canvas::rect_t &get_alloc_rect () const { return m_alloc_rect; }
+  int get_alloc_w () const { return m_alloc_rect.get_width (); }
+  int get_alloc_h () const { return m_alloc_rect.get_height (); }
+  int get_min_x () const { return m_alloc_rect.get_min_x (); }
+  int get_max_x () const { return m_alloc_rect.get_max_x (); }
+  int get_next_x () const { return m_alloc_rect.get_next_x (); }
+  int get_min_y () const { return m_alloc_rect.get_min_y (); }
+  int get_max_y () const { return m_alloc_rect.get_max_y (); }
+  int get_next_y () const { return m_alloc_rect.get_max_y (); }
+  canvas::range_t get_x_range () const { return m_alloc_rect.get_x_range (); }
+  canvas::range_t get_y_range () const { return m_alloc_rect.get_y_range (); }
+  const canvas::coord_t &get_top_left () const
+  {
+    return m_alloc_rect.m_top_left;
+  }
+
+ protected:
+  widget ()
+  : m_req_size (0, 0),
+    m_alloc_rect (canvas::coord_t (0, 0),
+		  canvas::size_t (0, 0))
+  {}
+
+private:
+  /* How much size this widget requested.  */
+  canvas::size_t m_req_size;
+  /* Where (and how big) this widget was allocated.  */
+  canvas::rect_t m_alloc_rect;
+};
+
+/* Concrete subclass for a widget with a single child.  */
+
+class wrapper_widget : public widget
+{
+ public:
+  wrapper_widget (std::unique_ptr<widget> child)
+  : m_child (std::move (child))
+  {}
+
+  const char *get_desc () const override
+  {
+    return "wrapper_widget";
+  }
+  canvas::size_t calc_req_size () override
+  {
+    return m_child->get_req_size ();
+  }
+  void update_child_alloc_rects ()
+  {
+    m_child->set_alloc_rect (get_alloc_rect ());
+  }
+  void paint_to_canvas (canvas &canvas) override
+  {
+    m_child->paint_to_canvas (canvas);
+  }
+ private:
+  std::unique_ptr<widget> m_child;
+};
+
+/* Abstract subclass for widgets with an arbitrary number of children.  */
+
+class container_widget : public widget
+{
+ public:
+  void add_child (std::unique_ptr<widget> child)
+  {
+    m_children.push_back (std::move (child));
+  }
+
+  void paint_to_canvas (canvas &canvas) final override
+  {
+    for (auto &child : m_children)
+      child->paint_to_canvas (canvas);
+  }
+
+ protected:
+  std::vector<std::unique_ptr<widget>> m_children;
+};
+
+/* Concrete widget subclass: lay out children vertically.  */
+
+class vbox_widget : public container_widget
+{
+ public:
+  const char *get_desc () const override;
+  canvas::size_t calc_req_size () override;
+  void update_child_alloc_rects () final override;
+};
+
+/* Abstract subclass for widgets with no children.  */
+
+class leaf_widget : public widget
+{
+ public:
+  void update_child_alloc_rects () final override
+  {
+    /* no-op.  */
+  }
+
+ protected:
+  leaf_widget () : widget () {}
+};
+
+/* Concrete widget subclass for a text string.  */
+
+class text_widget : public leaf_widget
+{
+ public:
+  text_widget (styled_string str)
+  : leaf_widget (), m_str (std::move (str))
+  {
+  }
+
+  const char *get_desc () const override;
+  canvas::size_t calc_req_size () final override;
+  void paint_to_canvas (canvas &canvas) final override;
+
+private:
+  styled_string m_str;
+};
+
+/* Concrete widget subclass for a pre-rendered canvas.  */
+
+class canvas_widget : public leaf_widget
+{
+ public:
+  canvas_widget (canvas &&c)
+  : leaf_widget (), m_canvas (std::move (c))
+  {
+  }
+
+  const char *get_desc () const override;
+  canvas::size_t calc_req_size () final override;
+  void paint_to_canvas (canvas &canvas) final override;
+
+private:
+  canvas m_canvas;
+};
+
+} // namespace text_art
+
+#endif /* GCC_TEXT_ART_WIDGET_H */
diff --git a/gcc/trans-mem.cc b/gcc/trans-mem.cc
index d036e43..9c3d112 100644
--- a/gcc/trans-mem.cc
+++ b/gcc/trans-mem.cc
@@ -637,6 +637,9 @@
     {
     case GIMPLE_CALL:
       {
+	if (gimple_call_internal_p (stmt))
+	  break;
+
 	tree fn = gimple_call_fn (stmt);
 
 	if ((d->summary_flags & DIAG_TM_OUTER) == 0
diff --git a/gcc/tree-ssa-alias.cc b/gcc/tree-ssa-alias.cc
index e1bc04b8..f31fd04 100644
--- a/gcc/tree-ssa-alias.cc
+++ b/gcc/tree-ssa-alias.cc
@@ -2815,11 +2815,13 @@
       case IFN_SCATTER_STORE:
       case IFN_MASK_SCATTER_STORE:
       case IFN_LEN_STORE:
+      case IFN_LEN_MASK_STORE:
 	return false;
       case IFN_MASK_STORE_LANES:
 	goto process_args;
       case IFN_MASK_LOAD:
       case IFN_LEN_LOAD:
+      case IFN_LEN_MASK_LOAD:
       case IFN_MASK_LOAD_LANES:
 	{
 	  ao_ref rhs_ref;
@@ -3068,6 +3070,7 @@
 	return false;
       case IFN_MASK_STORE:
       case IFN_LEN_STORE:
+      case IFN_LEN_MASK_STORE:
       case IFN_MASK_STORE_LANES:
 	{
 	  tree rhs = gimple_call_arg (call,
diff --git a/gcc/tree-ssa-dce.cc b/gcc/tree-ssa-dce.cc
index 2949957..f0b0245 100644
--- a/gcc/tree-ssa-dce.cc
+++ b/gcc/tree-ssa-dce.cc
@@ -1865,12 +1865,15 @@
 		    }
 		  free_dominance_info (fn, CDI_DOMINATORS);
 		  basic_block forwarder = split_edge (args[start].first);
+		  profile_count count = profile_count::zero ();
 		  for (unsigned j = start + 1; j < i; ++j)
 		    {
 		      edge e = args[j].first;
 		      redirect_edge_and_branch_force (e, forwarder);
 		      redirect_edge_var_map_clear (e);
+		      count += e->count ();
 		    }
+		  forwarder->count = count;
 		  if (vphi)
 		    {
 		      tree def = copy_ssa_name (vphi_args[0]);
diff --git a/gcc/tree-ssa-dse.cc b/gcc/tree-ssa-dse.cc
index 3c7a2e9..f833803 100644
--- a/gcc/tree-ssa-dse.cc
+++ b/gcc/tree-ssa-dse.cc
@@ -48,6 +48,7 @@
 #include "tree-ssa-loop-niter.h"
 #include "cfgloop.h"
 #include "tree-data-ref.h"
+#include "internal-fn.h"
 
 /* This file implements dead store elimination.
 
@@ -157,23 +158,36 @@
       switch (gimple_call_internal_fn (stmt))
 	{
 	case IFN_LEN_STORE:
-	  ao_ref_init_from_ptr_and_size
-	      (write, gimple_call_arg (stmt, 0),
-	       int_const_binop (MINUS_EXPR,
-				gimple_call_arg (stmt, 2),
-				gimple_call_arg (stmt, 4)));
-	  return true;
 	case IFN_MASK_STORE:
-	  /* We cannot initialize a must-def ao_ref (in all cases) but we
-	     can provide a may-def variant.  */
-	  if (may_def_ok)
-	    {
-	      ao_ref_init_from_ptr_and_size
-		  (write, gimple_call_arg (stmt, 0),
-		   TYPE_SIZE_UNIT (TREE_TYPE (gimple_call_arg (stmt, 3))));
-	      return true;
-	    }
-	  break;
+	case IFN_LEN_MASK_STORE:
+	  {
+	    int stored_value_index
+	      = internal_fn_stored_value_index (gimple_call_internal_fn (stmt));
+	    if (gimple_call_internal_fn (stmt) == IFN_LEN_STORE)
+	      {
+		tree len = gimple_call_arg (stmt, 2);
+		tree bias = gimple_call_arg (stmt, 4);
+		if (tree_fits_uhwi_p (len))
+		  {
+		    ao_ref_init_from_ptr_and_size (write,
+						   gimple_call_arg (stmt, 0),
+						   int_const_binop (MINUS_EXPR,
+								    len, bias));
+		    return true;
+		  }
+	      }
+	    /* We cannot initialize a must-def ao_ref (in all cases) but we
+	       can provide a may-def variant.  */
+	    if (may_def_ok)
+	      {
+		ao_ref_init_from_ptr_and_size (
+		  write, gimple_call_arg (stmt, 0),
+		  TYPE_SIZE_UNIT (
+		    TREE_TYPE (gimple_call_arg (stmt, stored_value_index))));
+		return true;
+	      }
+	    break;
+	  }
 	default:;
 	}
     }
@@ -1502,6 +1516,7 @@
 	{
 	case IFN_LEN_STORE:
 	case IFN_MASK_STORE:
+	case IFN_LEN_MASK_STORE:
 	  {
 	    enum dse_store_status store_status;
 	    store_status = dse_classify_store (&ref, stmt, false, live_bytes);
diff --git a/gcc/tree-ssa-loop-ivopts.cc b/gcc/tree-ssa-loop-ivopts.cc
index 6671ff6..243ce86 100644
--- a/gcc/tree-ssa-loop-ivopts.cc
+++ b/gcc/tree-ssa-loop-ivopts.cc
@@ -2442,6 +2442,7 @@
     case IFN_MASK_LOAD:
     case IFN_MASK_LOAD_LANES:
     case IFN_LEN_LOAD:
+    case IFN_LEN_MASK_LOAD:
       if (op_p == gimple_call_arg_ptr (call, 0))
 	return TREE_TYPE (gimple_call_lhs (call));
       return NULL_TREE;
@@ -2449,9 +2450,16 @@
     case IFN_MASK_STORE:
     case IFN_MASK_STORE_LANES:
     case IFN_LEN_STORE:
-      if (op_p == gimple_call_arg_ptr (call, 0))
-	return TREE_TYPE (gimple_call_arg (call, 3));
-      return NULL_TREE;
+    case IFN_LEN_MASK_STORE:
+      {
+	if (op_p == gimple_call_arg_ptr (call, 0))
+	  {
+	    internal_fn ifn = gimple_call_internal_fn (call);
+	    int index = internal_fn_stored_value_index (ifn);
+	    return TREE_TYPE (gimple_call_arg (call, index));
+	  }
+	return NULL_TREE;
+      }
 
     default:
       return NULL_TREE;
@@ -7559,6 +7567,8 @@
     case IFN_MASK_STORE_LANES:
     case IFN_LEN_LOAD:
     case IFN_LEN_STORE:
+    case IFN_LEN_MASK_LOAD:
+    case IFN_LEN_MASK_STORE:
       /* The second argument contains the correct alias type.  */
       gcc_assert (use->op_p = gimple_call_arg_ptr (call, 0));
       return TREE_TYPE (gimple_call_arg (call, 1));
diff --git a/gcc/tree-ssa-phiprop.cc b/gcc/tree-ssa-phiprop.cc
index 21a349a..b01ef44 100644
--- a/gcc/tree-ssa-phiprop.cc
+++ b/gcc/tree-ssa-phiprop.cc
@@ -340,6 +340,9 @@
       gimple *def_stmt;
       tree vuse;
 
+      if (!dom_info_available_p (cfun, CDI_POST_DOMINATORS))
+	calculate_dominance_info (CDI_POST_DOMINATORS);
+
       /* Only replace loads in blocks that post-dominate the PHI node.  That
          makes sure we don't end up speculating loads.  */
       if (!dominated_by_p (CDI_POST_DOMINATORS,
@@ -399,14 +402,18 @@
 	     there are no statements that could read from memory
 	     aliasing the lhs in between the start of bb and use_stmt.
 	     As we require use_stmt to have a VDEF above, loads after
-	     use_stmt will use a different virtual SSA_NAME.  */
+	     use_stmt will use a different virtual SSA_NAME.  When
+	     we reach an edge inserted load the constraints we place
+	     on processing guarantees that program order is preserved
+	     so we can avoid checking those.  */
 	  FOR_EACH_IMM_USE_FAST (vuse_p, vui, vuse)
 	    {
 	      vuse_stmt = USE_STMT (vuse_p);
 	      if (vuse_stmt == use_stmt)
 		continue;
-	      if (!dominated_by_p (CDI_DOMINATORS,
-				   gimple_bb (vuse_stmt), bb))
+	      if (!gimple_bb (vuse_stmt)
+		  || !dominated_by_p (CDI_DOMINATORS,
+				      gimple_bb (vuse_stmt), bb))
 		continue;
 	      if (ref_maybe_used_by_stmt_p (vuse_stmt,
 					    gimple_assign_lhs (use_stmt)))
@@ -481,7 +488,7 @@
   0, /* properties_provided */
   0, /* properties_destroyed */
   0, /* todo_flags_start */
-  TODO_update_ssa, /* todo_flags_finish */
+  0, /* todo_flags_finish */
 };
 
 class pass_phiprop : public gimple_opt_pass
@@ -509,7 +516,6 @@
   size_t n;
 
   calculate_dominance_info (CDI_DOMINATORS);
-  calculate_dominance_info (CDI_POST_DOMINATORS);
 
   n = num_ssa_names;
   phivn = XCNEWVEC (struct phiprop_d, n);
@@ -535,7 +541,7 @@
 
   free_dominance_info (CDI_POST_DOMINATORS);
 
-  return 0;
+  return did_something ? TODO_update_ssa_only_virtuals : 0;
 }
 
 } // anon namespace
diff --git a/gcc/tree-ssa-sccvn.cc b/gcc/tree-ssa-sccvn.cc
index 27c84e78..11061a3 100644
--- a/gcc/tree-ssa-sccvn.cc
+++ b/gcc/tree-ssa-sccvn.cc
@@ -3346,17 +3346,17 @@
 		= tree_to_uhwi (TYPE_SIZE (TREE_TYPE (vectype)));
 	      if (mask)
 		{
-		  HOST_WIDE_INT start = 0, len = 0;
+		  HOST_WIDE_INT start = 0, length = 0;
 		  unsigned mask_idx = 0;
 		  do
 		    {
 		      if (integer_zerop (VECTOR_CST_ELT (mask, mask_idx)))
 			{
-			  if (len != 0)
+			  if (length != 0)
 			    {
 			      pd.rhs_off = start;
 			      pd.offset = offset2i + start;
-			      pd.size = len;
+			      pd.size = length;
 			      if (ranges_known_overlap_p
 				    (offset, maxsize, pd.offset, pd.size))
 				{
@@ -3367,18 +3367,18 @@
 				}
 			    }
 			  start = (mask_idx + 1) * elsz;
-			  len = 0;
+			  length = 0;
 			}
 		      else
-			len += elsz;
+			length += elsz;
 		      mask_idx++;
 		    }
 		  while (known_lt (mask_idx, TYPE_VECTOR_SUBPARTS (vectype)));
-		  if (len != 0)
+		  if (length != 0)
 		    {
 		      pd.rhs_off = start;
 		      pd.offset = offset2i + start;
-		      pd.size = len;
+		      pd.size = length;
 		      if (ranges_known_overlap_p (offset, maxsize,
 						  pd.offset, pd.size))
 			return data->push_partial_def (pd, set, set,
diff --git a/gcc/tree-ssa-scopedtables.cc b/gcc/tree-ssa-scopedtables.cc
index 528ddf2..e698ef9 100644
--- a/gcc/tree-ssa-scopedtables.cc
+++ b/gcc/tree-ssa-scopedtables.cc
@@ -574,7 +574,7 @@
       && (TREE_CODE (type0) == ERROR_MARK
 	  || TREE_CODE (type1) == ERROR_MARK
 	  || TYPE_UNSIGNED (type0) != TYPE_UNSIGNED (type1)
-	  || TYPE_PRECISION (type0) != TYPE_PRECISION (type1)
+	  || element_precision (type0) != element_precision (type1)
 	  || TYPE_MODE (type0) != TYPE_MODE (type1)))
     return false;
 
diff --git a/gcc/tree-vect-patterns.cc b/gcc/tree-vect-patterns.cc
index 60bc9be..a04accf 100644
--- a/gcc/tree-vect-patterns.cc
+++ b/gcc/tree-vect-patterns.cc
@@ -398,8 +398,11 @@
 				      vect_unpromoted_value *unprom,
 				      bool *single_use_p = NULL)
 {
-  tree res = NULL_TREE;
   tree op_type = TREE_TYPE (op);
+  if (!INTEGRAL_TYPE_P (op_type))
+    return NULL_TREE;
+
+  tree res = NULL_TREE;
   unsigned int orig_precision = TYPE_PRECISION (op_type);
   unsigned int min_precision = orig_precision;
   stmt_vec_info caster = NULL;
@@ -3881,6 +3884,7 @@
   if (TREE_CODE (oprnd0) != SSA_NAME
       || TREE_CODE (oprnd1) != SSA_NAME
       || TYPE_MODE (TREE_TYPE (oprnd0)) == TYPE_MODE (TREE_TYPE (oprnd1))
+      || !INTEGRAL_TYPE_P (TREE_TYPE (oprnd0))
       || !type_has_mode_precision_p (TREE_TYPE (oprnd1))
       || TYPE_PRECISION (TREE_TYPE (lhs))
 	 != TYPE_PRECISION (TREE_TYPE (oprnd0)))
diff --git a/gcc/tree-vect-slp.cc b/gcc/tree-vect-slp.cc
index ab89a82..fee992d 100644
--- a/gcc/tree-vect-slp.cc
+++ b/gcc/tree-vect-slp.cc
@@ -4673,14 +4673,28 @@
   m_partition_layout_costs.safe_grow_cleared (m_partitions.length ()
 					      * m_perms.length ());
 
-  /* We have to mark outgoing permutations facing non-reduction graph
-     entries that are not represented as to be materialized.  */
+  /* We have to mark outgoing permutations facing non-associating-reduction
+     graph entries that are not represented as to be materialized.
+     slp_inst_kind_bb_reduc currently only covers associatable reductions.  */
   for (slp_instance instance : m_vinfo->slp_instances)
     if (SLP_INSTANCE_KIND (instance) == slp_inst_kind_ctor)
       {
 	unsigned int node_i = SLP_INSTANCE_TREE (instance)->vertex;
 	m_partitions[m_vertices[node_i].partition].layout = 0;
       }
+    else if (SLP_INSTANCE_KIND (instance) == slp_inst_kind_reduc_chain)
+      {
+	stmt_vec_info stmt_info
+	  = SLP_TREE_REPRESENTATIVE (SLP_INSTANCE_TREE (instance));
+	stmt_vec_info reduc_info = info_for_reduction (m_vinfo, stmt_info);
+	if (needs_fold_left_reduction_p (TREE_TYPE
+					   (gimple_get_lhs (stmt_info->stmt)),
+					 STMT_VINFO_REDUC_CODE (reduc_info)))
+	  {
+	    unsigned int node_i = SLP_INSTANCE_TREE (instance)->vertex;
+	    m_partitions[m_vertices[node_i].partition].layout = 0;
+	  }
+      }
 
   /* Check which layouts each node and partition can handle.  Calculate the
      weights associated with inserting layout changes on edges.  */
diff --git a/gcc/tree-vect-stmts.cc b/gcc/tree-vect-stmts.cc
index ae24f3e..b31971e 100644
--- a/gcc/tree-vect-stmts.cc
+++ b/gcc/tree-vect-stmts.cc
@@ -1819,16 +1819,8 @@
   poly_uint64 nunits = TYPE_VECTOR_SUBPARTS (vectype);
   poly_uint64 vf = LOOP_VINFO_VECT_FACTOR (loop_vinfo);
   machine_mode mask_mode;
-  bool using_partial_vectors_p = false;
-  if (targetm.vectorize.get_mask_mode (vecmode).exists (&mask_mode)
-      && can_vec_mask_load_store_p (vecmode, mask_mode, is_load))
-    {
-      nvectors = group_memory_nvectors (group_size * vf, nunits);
-      vect_record_loop_mask (loop_vinfo, masks, nvectors, vectype, scalar_mask);
-      using_partial_vectors_p = true;
-    }
-
   machine_mode vmode;
+  bool using_partial_vectors_p = false;
   if (get_len_load_store_mode (vecmode, is_load).exists (&vmode))
     {
       nvectors = group_memory_nvectors (group_size * vf, nunits);
@@ -1837,6 +1829,13 @@
       vect_record_loop_len (loop_vinfo, lens, nvectors, vectype, factor);
       using_partial_vectors_p = true;
     }
+  else if (targetm.vectorize.get_mask_mode (vecmode).exists (&mask_mode)
+	   && can_vec_mask_load_store_p (vecmode, mask_mode, is_load))
+    {
+      nvectors = group_memory_nvectors (group_size * vf, nunits);
+      vect_record_loop_mask (loop_vinfo, masks, nvectors, vectype, scalar_mask);
+      using_partial_vectors_p = true;
+    }
 
   if (!using_partial_vectors_p)
     {
@@ -2143,6 +2142,14 @@
   if (!VECTOR_MODE_P (vmode))
     return NULL_TREE;
 
+  /* When we are asked to compose the vector from its components let
+     that happen directly.  */
+  if (known_eq (TYPE_VECTOR_SUBPARTS (vtype), nelts))
+    {
+      *ptype = TREE_TYPE (vtype);
+      return vtype;
+    }
+
   poly_uint64 vbsize = GET_MODE_BITSIZE (vmode);
   unsigned int pbsize;
   if (constant_multiple_p (vbsize, nelts, &pbsize))
@@ -5037,7 +5044,7 @@
 			 gimple **vec_stmt, slp_tree slp_node,
 			 stmt_vector_for_cost *cost_vec)
 {
-  tree vec_dest;
+  tree vec_dest, cvt_op = NULL_TREE;
   tree scalar_dest;
   tree op0, op1 = NULL_TREE;
   loop_vec_info loop_vinfo = dyn_cast <loop_vec_info> (vinfo);
@@ -5256,7 +5263,8 @@
       if ((code == FLOAT_EXPR
 	   && GET_MODE_SIZE (lhs_mode) > GET_MODE_SIZE (rhs_mode))
 	  || (code == FIX_TRUNC_EXPR
-	      && GET_MODE_SIZE (rhs_mode) > GET_MODE_SIZE (lhs_mode)))
+	      && GET_MODE_SIZE (rhs_mode) > GET_MODE_SIZE (lhs_mode)
+	      && !flag_trapping_math))
 	{
 	  bool float_expr_p = code == FLOAT_EXPR;
 	  scalar_mode imode = float_expr_p ? rhs_mode : lhs_mode;
@@ -5470,8 +5478,9 @@
       if (modifier == NONE)
         {
 	  STMT_VINFO_TYPE (stmt_info) = type_conversion_vec_info_type;
-	  vect_model_simple_cost (vinfo, stmt_info, ncopies, dt, ndts, slp_node,
-				  cost_vec);
+	  vect_model_simple_cost (vinfo, stmt_info,
+				  ncopies * (1 + multi_step_cvt),
+				  dt, ndts, slp_node, cost_vec);
 	}
       else if (modifier == NARROW_SRC || modifier == NARROW_DST)
 	{
@@ -5561,6 +5570,13 @@
     case NONE:
       vect_get_vec_defs (vinfo, stmt_info, slp_node, ncopies,
 			 op0, &vec_oprnds0);
+      /* vec_dest is intermediate type operand when multi_step_cvt.  */
+      if (multi_step_cvt)
+	{
+	  cvt_op = vec_dest;
+	  vec_dest = vec_dsts[0];
+	}
+
       FOR_EACH_VEC_ELT (vec_oprnds0, i, vop0)
 	{
 	  /* Arguments are ready, create the new vector stmt.  */
@@ -5568,12 +5584,11 @@
 	  if (multi_step_cvt)
 	    {
 	      gcc_assert (multi_step_cvt == 1);
-	      new_stmt = vect_gimple_build (vec_dest, codecvt1, vop0);
-	      new_temp = make_ssa_name (vec_dest, new_stmt);
+	      new_stmt = vect_gimple_build (cvt_op, codecvt1, vop0);
+	      new_temp = make_ssa_name (cvt_op, new_stmt);
 	      gimple_assign_set_lhs (new_stmt, new_temp);
 	      vect_finish_stmt_generation (vinfo, stmt_info, new_stmt, gsi);
 	      vop0 = new_temp;
-	      vec_dest = vec_dsts[0];
 	    }
 	  new_stmt = vect_gimple_build (vec_dest, code1, vop0);
 	  new_temp = make_ssa_name (vec_dest, new_stmt);
@@ -5825,12 +5840,15 @@
   /* We do not handle bit-precision changes.  */
   if ((CONVERT_EXPR_CODE_P (code)
        || code == VIEW_CONVERT_EXPR)
-      && INTEGRAL_TYPE_P (TREE_TYPE (scalar_dest))
-      && (!type_has_mode_precision_p (TREE_TYPE (scalar_dest))
-	  || !type_has_mode_precision_p (TREE_TYPE (op)))
+      && ((INTEGRAL_TYPE_P (TREE_TYPE (scalar_dest))
+	   && !type_has_mode_precision_p (TREE_TYPE (scalar_dest)))
+	  || (INTEGRAL_TYPE_P (TREE_TYPE (op))
+	      && !type_has_mode_precision_p (TREE_TYPE (op))))
       /* But a conversion that does not change the bit-pattern is ok.  */
-      && !((TYPE_PRECISION (TREE_TYPE (scalar_dest))
-	    > TYPE_PRECISION (TREE_TYPE (op)))
+      && !(INTEGRAL_TYPE_P (TREE_TYPE (scalar_dest))
+	   && INTEGRAL_TYPE_P (TREE_TYPE (op))
+	   && (TYPE_PRECISION (TREE_TYPE (scalar_dest))
+	       > TYPE_PRECISION (TREE_TYPE (op)))
 	   && TYPE_UNSIGNED (TREE_TYPE (op))))
     {
       if (dump_enabled_p ())
@@ -9006,30 +9024,63 @@
 		  vec_oprnd = new_temp;
 		}
 
-	      /* Arguments are ready.  Create the new vector stmt.  */
-	      if (final_mask)
+	      /* Compute IFN when LOOP_LENS or final_mask valid.  */
+	      machine_mode vmode = TYPE_MODE (vectype);
+	      machine_mode new_vmode = vmode;
+	      internal_fn partial_ifn = IFN_LAST;
+	      /* Produce 'len' and 'bias' argument.  */
+	      tree final_len = NULL_TREE;
+	      tree bias = NULL_TREE;
+	      if (loop_lens)
 		{
-		  tree ptr = build_int_cst (ref_type, align * BITS_PER_UNIT);
-		  gcall *call
-		    = gimple_build_call_internal (IFN_MASK_STORE, 4,
-						  dataref_ptr, ptr,
-						  final_mask, vec_oprnd);
-		  gimple_call_set_nothrow (call, true);
-		  vect_finish_stmt_generation (vinfo, stmt_info, call, gsi);
-		  new_stmt = call;
-		}
-	      else if (loop_lens)
-		{
-		  machine_mode vmode = TYPE_MODE (vectype);
 		  opt_machine_mode new_ovmode
-		    = get_len_load_store_mode (vmode, false);
-		  machine_mode new_vmode = new_ovmode.require ();
+		    = get_len_load_store_mode (vmode, false, &partial_ifn);
+		  new_vmode = new_ovmode.require ();
 		  unsigned factor
 		    = (new_ovmode == vmode) ? 1 : GET_MODE_UNIT_SIZE (vmode);
-		  tree final_len
-		    = vect_get_loop_len (loop_vinfo, gsi, loop_lens,
-					 vec_num * ncopies, vectype,
-					 vec_num * j + i, factor);
+		  final_len = vect_get_loop_len (loop_vinfo, gsi, loop_lens,
+						 vec_num * ncopies, vectype,
+						 vec_num * j + i, factor);
+		}
+	      else if (final_mask)
+		{
+		  if (!can_vec_mask_load_store_p (vmode,
+						  TYPE_MODE (TREE_TYPE (final_mask)),
+						  false, &partial_ifn))
+		    gcc_unreachable ();
+		}
+
+	      if (partial_ifn == IFN_LEN_MASK_STORE)
+		{
+		  if (!final_len)
+		    {
+		      /* Pass VF value to 'len' argument of
+		         LEN_MASK_STORE if LOOP_LENS is invalid.  */
+		      tree iv_type = LOOP_VINFO_RGROUP_IV_TYPE (loop_vinfo);
+		      final_len
+			= build_int_cst (iv_type,
+					 TYPE_VECTOR_SUBPARTS (vectype));
+		    }
+		  if (!final_mask)
+		    {
+		      /* Pass all ones value to 'mask' argument of
+			 LEN_MASK_STORE if final_mask is invalid.  */
+		      mask_vectype = truth_type_for (vectype);
+		      final_mask = build_minus_one_cst (mask_vectype);
+		    }
+		}
+	      if (final_len)
+		{
+		  signed char biasval
+		    = LOOP_VINFO_PARTIAL_LOAD_STORE_BIAS (loop_vinfo);
+
+		  bias = build_int_cst (intQI_type_node, biasval);
+		}
+
+	      /* Arguments are ready.  Create the new vector stmt.  */
+	      if (final_len)
+		{
+		  gcall *call;
 		  tree ptr = build_int_cst (ref_type, align * BITS_PER_UNIT);
 		  /* Need conversion if it's wrapped with VnQI.  */
 		  if (vmode != new_vmode)
@@ -9049,14 +9100,27 @@
 		      vec_oprnd = var;
 		    }
 
-		  signed char biasval =
-		    LOOP_VINFO_PARTIAL_LOAD_STORE_BIAS (loop_vinfo);
-
-		  tree bias = build_int_cst (intQI_type_node, biasval);
+		  if (partial_ifn == IFN_LEN_MASK_STORE)
+		    call = gimple_build_call_internal (IFN_LEN_MASK_STORE, 6,
+						       dataref_ptr, ptr,
+						       final_len, final_mask,
+						       vec_oprnd, bias);
+		  else
+		    call
+		      = gimple_build_call_internal (IFN_LEN_STORE, 5,
+						    dataref_ptr, ptr, final_len,
+						    vec_oprnd, bias);
+		  gimple_call_set_nothrow (call, true);
+		  vect_finish_stmt_generation (vinfo, stmt_info, call, gsi);
+		  new_stmt = call;
+		}
+	      else if (final_mask)
+		{
+		  tree ptr = build_int_cst (ref_type, align * BITS_PER_UNIT);
 		  gcall *call
-		    = gimple_build_call_internal (IFN_LEN_STORE, 5, dataref_ptr,
-						  ptr, final_len, vec_oprnd,
-						  bias);
+		    = gimple_build_call_internal (IFN_MASK_STORE, 4,
+						  dataref_ptr, ptr,
+						  final_mask, vec_oprnd);
 		  gimple_call_set_nothrow (call, true);
 		  vect_finish_stmt_generation (vinfo, stmt_info, call, gsi);
 		  new_stmt = call;
@@ -9585,27 +9649,26 @@
 	  gimple_set_vuse (new_stmt, vuse);
 	  gsi_insert_on_edge_immediate (pe, new_stmt);
 	}
-      /* These copies are all equivalent, but currently the representation
-	 requires a separate STMT_VINFO_VEC_STMT for each one.  */
-      gimple_stmt_iterator gsi2 = *gsi;
-      gsi_next (&gsi2);
-      for (j = 0; j < ncopies; j++)
+      /* These copies are all equivalent.  */
+      if (hoist_p)
+	new_temp = vect_init_vector (vinfo, stmt_info, scalar_dest,
+				     vectype, NULL);
+      else
 	{
-	  if (hoist_p)
-	    new_temp = vect_init_vector (vinfo, stmt_info, scalar_dest,
-					 vectype, NULL);
-	  else
-	    new_temp = vect_init_vector (vinfo, stmt_info, scalar_dest,
-					 vectype, &gsi2);
-	  gimple *new_stmt = SSA_NAME_DEF_STMT (new_temp);
-	  if (slp)
-	    SLP_TREE_VEC_STMTS (slp_node).quick_push (new_stmt);
-	  else
-	    {
-	      if (j == 0)
-		*vec_stmt = new_stmt;
-	      STMT_VINFO_VEC_STMTS (stmt_info).safe_push (new_stmt);
-	    }
+	  gimple_stmt_iterator gsi2 = *gsi;
+	  gsi_next (&gsi2);
+	  new_temp = vect_init_vector (vinfo, stmt_info, scalar_dest,
+				       vectype, &gsi2);
+	}
+      gimple *new_stmt = SSA_NAME_DEF_STMT (new_temp);
+      if (slp)
+	for (j = 0; j < (int) SLP_TREE_NUMBER_OF_VEC_STMTS (slp_node); ++j)
+	  SLP_TREE_VEC_STMTS (slp_node).quick_push (new_stmt);
+      else
+	{
+	  for (j = 0; j < ncopies; ++j)
+	    STMT_VINFO_VEC_STMTS (stmt_info).safe_push (new_stmt);
+	  *vec_stmt = new_stmt;
 	}
       return true;
     }
@@ -10366,45 +10429,77 @@
 					      align, misalign);
 		    align = least_bit_hwi (misalign | align);
 
-		    if (final_mask)
+		    /* Compute IFN when LOOP_LENS or final_mask valid.  */
+		    machine_mode vmode = TYPE_MODE (vectype);
+		    machine_mode new_vmode = vmode;
+		    internal_fn partial_ifn = IFN_LAST;
+		    /* Produce 'len' and 'bias' argument.  */
+		    tree final_len = NULL_TREE;
+		    tree bias = NULL_TREE;
+		    if (loop_lens)
 		      {
-			tree ptr = build_int_cst (ref_type,
-						  align * BITS_PER_UNIT);
-			gcall *call
-			  = gimple_build_call_internal (IFN_MASK_LOAD, 3,
-							dataref_ptr, ptr,
-							final_mask);
-			gimple_call_set_nothrow (call, true);
-			new_stmt = call;
-			data_ref = NULL_TREE;
-		      }
-		    else if (loop_lens && memory_access_type != VMAT_INVARIANT)
-		      {
-			machine_mode vmode = TYPE_MODE (vectype);
 			opt_machine_mode new_ovmode
-			  = get_len_load_store_mode (vmode, true);
-			machine_mode new_vmode = new_ovmode.require ();
+			  = get_len_load_store_mode (vmode, true,
+						     &partial_ifn);
+			new_vmode = new_ovmode.require ();
 			unsigned factor = (new_ovmode == vmode)
 					    ? 1
 					    : GET_MODE_UNIT_SIZE (vmode);
-			tree final_len
+			final_len
 			  = vect_get_loop_len (loop_vinfo, gsi, loop_lens,
 					       vec_num * ncopies, vectype,
 					       vec_num * j + i, factor);
+		      }
+		    else if (final_mask)
+		      {
+			if (!can_vec_mask_load_store_p (
+			      vmode, TYPE_MODE (TREE_TYPE (final_mask)), true,
+			      &partial_ifn))
+			  gcc_unreachable ();
+		      }
+
+		    if (partial_ifn == IFN_LEN_MASK_LOAD)
+		      {
+			if (!final_len)
+			  {
+			    /* Pass VF value to 'len' argument of
+			       LEN_MASK_LOAD if LOOP_LENS is invalid.  */
+			    tree iv_type
+			      = LOOP_VINFO_RGROUP_IV_TYPE (loop_vinfo);
+			    final_len
+			      = build_int_cst (iv_type,
+					       TYPE_VECTOR_SUBPARTS (vectype));
+			  }
+			if (!final_mask)
+			  {
+			    /* Pass all ones value to 'mask' argument of
+			       LEN_MASK_LOAD if final_mask is invalid.  */
+			    mask_vectype = truth_type_for (vectype);
+			    final_mask = build_minus_one_cst (mask_vectype);
+			  }
+		      }
+		    if (final_len)
+		      {
+			signed char biasval
+			  = LOOP_VINFO_PARTIAL_LOAD_STORE_BIAS (loop_vinfo);
+
+			bias = build_int_cst (intQI_type_node, biasval);
+		      }
+
+		    if (final_len && memory_access_type != VMAT_INVARIANT)
+		      {
 			tree ptr
 			  = build_int_cst (ref_type, align * BITS_PER_UNIT);
-
-			tree qi_type = unsigned_intQI_type_node;
-
-			signed char biasval =
-			  LOOP_VINFO_PARTIAL_LOAD_STORE_BIAS (loop_vinfo);
-
-			tree bias = build_int_cst (intQI_type_node, biasval);
-
-			gcall *call
-			  = gimple_build_call_internal (IFN_LEN_LOAD, 4,
-							dataref_ptr, ptr,
-							final_len, bias);
+			gcall *call;
+			if (partial_ifn == IFN_LEN_MASK_LOAD)
+			  call = gimple_build_call_internal (IFN_LEN_MASK_LOAD,
+							     5, dataref_ptr,
+							     ptr, final_len,
+							     final_mask, bias);
+			else
+			  call = gimple_build_call_internal (IFN_LEN_LOAD, 4,
+							     dataref_ptr, ptr,
+							     final_len, bias);
 			gimple_call_set_nothrow (call, true);
 			new_stmt = call;
 			data_ref = NULL_TREE;
@@ -10412,8 +10507,8 @@
 			/* Need conversion if it's wrapped with VnQI.  */
 			if (vmode != new_vmode)
 			  {
-			    tree new_vtype
-			      = build_vector_type_for_mode (qi_type, new_vmode);
+			    tree new_vtype = build_vector_type_for_mode (
+			      unsigned_intQI_type_node, new_vmode);
 			    tree var = vect_get_new_ssa_name (new_vtype,
 							      vect_simple_var);
 			    gimple_set_lhs (call, var);
@@ -10425,6 +10520,18 @@
 						     VIEW_CONVERT_EXPR, op);
 			  }
 		      }
+		    else if (final_mask)
+		      {
+			tree ptr = build_int_cst (ref_type,
+						  align * BITS_PER_UNIT);
+			gcall *call
+			  = gimple_build_call_internal (IFN_MASK_LOAD, 3,
+							dataref_ptr, ptr,
+							final_mask);
+			gimple_call_set_nothrow (call, true);
+			new_stmt = call;
+			data_ref = NULL_TREE;
+		      }
 		    else
 		      {
 			tree ltype = vectype;
diff --git a/gcc/varasm.cc b/gcc/varasm.cc
index dd84754..542315f 100644
--- a/gcc/varasm.cc
+++ b/gcc/varasm.cc
@@ -4885,7 +4885,8 @@
 	/* Allow length-preserving conversions between integer types and
 	   floating-point types.  */
 	if (((INTEGRAL_TYPE_P (dest_type) && INTEGRAL_TYPE_P (src_type))
-	     || (FLOAT_TYPE_P (dest_type) && FLOAT_TYPE_P (src_type)))
+	     || (SCALAR_FLOAT_TYPE_P (dest_type)
+		 && SCALAR_FLOAT_TYPE_P (src_type)))
 	    && (TYPE_PRECISION (dest_type) == TYPE_PRECISION (src_type)))
 	  return initializer_constant_valid_p_1 (src, endtype, cache);
 
@@ -4943,6 +4944,7 @@
       if (cache && cache[0] == value)
 	return cache[1];
       if (! INTEGRAL_TYPE_P (endtype)
+	  || ! INTEGRAL_TYPE_P (TREE_TYPE (value))
 	  || TYPE_PRECISION (endtype) >= TYPE_PRECISION (TREE_TYPE (value)))
 	{
 	  tree ncache[4] = { NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE };
@@ -4979,6 +4981,7 @@
       if (cache && cache[0] == value)
 	return cache[1];
       if (! INTEGRAL_TYPE_P (endtype)
+	  || ! INTEGRAL_TYPE_P (TREE_TYPE (value))
 	  || TYPE_PRECISION (endtype) >= TYPE_PRECISION (TREE_TYPE (value)))
 	{
 	  tree ncache[4] = { NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE };
diff --git a/libcpp/ChangeLog b/libcpp/ChangeLog
index 48acad7..390d8e1 100644
--- a/libcpp/ChangeLog
+++ b/libcpp/ChangeLog
@@ -1,3 +1,30 @@
+2023-06-23  Marek Polacek  <polacek@redhat.com>
+
+	* include/cpplib.h (c_lang): Add CXX26 and GNUCXX26.
+	* init.cc (lang_defaults): Add rows for CXX26 and GNUCXX26.
+	(cpp_init_builtins): Set __cplusplus to 202400L for C++26.
+	Set __cplusplus to 202302L for C++23.
+
+2023-06-23  Ben Boeckel  <me@benboeckel.net>
+
+	* charset.cc: Allow `UCS_LIMIT` in UTF-8 strings.
+
+2023-06-22  David Malcolm  <dmalcolm@redhat.com>
+
+	* charset.cc (get_cppchar_property): New function template, based
+	on...
+	(cpp_wcwidth): ...this function.  Rework to use the above.
+	Include "combining-chars.inc".
+	(cpp_is_combining_char): New function
+	Include "printable-chars.inc".
+	(cpp_is_printable_char): New function
+	* combining-chars.inc: New file, generated by
+	contrib/unicode/gen-combining-chars.py.
+	* include/cpplib.h (cpp_is_combining_char): New function decl.
+	(cpp_is_printable_char): New function decl.
+	* printable-chars.inc: New file, generated by
+	contrib/unicode/gen-printable-chars.py.
+
 2023-06-20  Lewis Hyatt  <lhyatt@gmail.com>
 
 	PR c++/66290
diff --git a/libcpp/charset.cc b/libcpp/charset.cc
index d4f573e..7b625c9 100644
--- a/libcpp/charset.cc
+++ b/libcpp/charset.cc
@@ -1891,7 +1891,7 @@
 	 invalid because they cannot be represented in UTF-16.
 
 	 Reject such values.*/
-      if (cp >= UCS_LIMIT)
+      if (cp > UCS_LIMIT)
 	return false;
     }
   /* No problems encountered.  */
@@ -3154,6 +3154,40 @@
   return dw.bytes_processed () + MAX (0, display_col - avail_display);
 }
 
+template <typename PropertyType>
+PropertyType
+get_cppchar_property (cppchar_t c,
+		      const cppchar_t *range_ends,
+		      const PropertyType *range_values,
+		      size_t num_ranges,
+		      PropertyType default_value)
+{
+  if (__builtin_expect (c <= range_ends[0], true))
+    return range_values[0];
+
+  /* Binary search the tables.  */
+  int begin = 1;
+  static const int end = num_ranges;
+  int len = end - begin;
+  do
+    {
+      int half = len/2;
+      int middle = begin + half;
+      if (c > range_ends[middle])
+	{
+	  begin = middle + 1;
+	  len -= half + 1;
+	}
+      else
+	len = half;
+    } while (len);
+
+  if (__builtin_expect (begin != end, true))
+    return range_values[begin];
+
+  return default_value;
+}
+
 /* Our own version of wcwidth().  We don't use the actual wcwidth() in glibc,
    because that will inspect the user's locale, and in particular in an ASCII
    locale, it will not return anything useful for extended characters.  But GCC
@@ -3167,30 +3201,43 @@
    diagnostics, they are sufficient.  */
 
 #include "generated_cpp_wcwidth.h"
-int cpp_wcwidth (cppchar_t c)
+
+int
+cpp_wcwidth (cppchar_t c)
 {
-  if (__builtin_expect (c <= wcwidth_range_ends[0], true))
-    return wcwidth_widths[0];
+  const size_t num_ranges
+    = sizeof wcwidth_range_ends / sizeof (*wcwidth_range_ends);
+  return get_cppchar_property<unsigned char > (c,
+					       &wcwidth_range_ends[0],
+					       &wcwidth_widths[0],
+					       num_ranges,
+					       1);
+}
 
-  /* Binary search the tables.  */
-  int begin = 1;
-  static const int end
-      = sizeof wcwidth_range_ends / sizeof (*wcwidth_range_ends);
-  int len = end - begin;
-  do
-    {
-      int half = len/2;
-      int middle = begin + half;
-      if (c > wcwidth_range_ends[middle])
-	{
-	  begin = middle + 1;
-	  len -= half + 1;
-	}
-      else
-	len = half;
-    } while (len);
+#include "combining-chars.inc"
 
-  if (__builtin_expect (begin != end, true))
-    return wcwidth_widths[begin];
-  return 1;
+bool
+cpp_is_combining_char (cppchar_t c)
+{
+  const size_t num_ranges
+    = sizeof combining_range_ends / sizeof (*combining_range_ends);
+  return get_cppchar_property<bool> (c,
+				     &combining_range_ends[0],
+				     &is_combining[0],
+				     num_ranges,
+				     false);
+}
+
+#include "printable-chars.inc"
+
+bool
+cpp_is_printable_char (cppchar_t c)
+{
+  const size_t num_ranges
+    = sizeof printable_range_ends / sizeof (*printable_range_ends);
+  return get_cppchar_property<bool> (c,
+				     &printable_range_ends[0],
+				     &is_printable[0],
+				     num_ranges,
+				     false);
 }
diff --git a/libcpp/combining-chars.inc b/libcpp/combining-chars.inc
new file mode 100644
index 0000000..dfec966
--- /dev/null
+++ b/libcpp/combining-chars.inc
@@ -0,0 +1,68 @@
+/* Generated by contrib/unicode/gen-combining-chars.py
+   using version 12.1.0 of the Unicode standard.  */
+
+static const cppchar_t combining_range_ends[] = {
+  0x2ff, 0x34e, 0x34f, 0x36f, 0x482, 0x487, 0x590, 0x5bd,
+  0x5be, 0x5bf, 0x5c0, 0x5c2, 0x5c3, 0x5c5, 0x5c6, 0x5c7,
+  0x60f, 0x61a, 0x64a, 0x65f, 0x66f, 0x670, 0x6d5, 0x6dc,
+  0x6de, 0x6e4, 0x6e6, 0x6e8, 0x6e9, 0x6ed, 0x710, 0x711,
+  0x72f, 0x74a, 0x7ea, 0x7f3, 0x7fc, 0x7fd, 0x815, 0x819,
+  0x81a, 0x823, 0x824, 0x827, 0x828, 0x82d, 0x858, 0x85b,
+  0x8d2, 0x8e1, 0x8e2, 0x8ff, 0x93b, 0x93c, 0x94c, 0x94d,
+  0x950, 0x954, 0x9bb, 0x9bc, 0x9cc, 0x9cd, 0x9fd, 0x9fe,
+  0xa3b, 0xa3c, 0xa4c, 0xa4d, 0xabb, 0xabc, 0xacc, 0xacd,
+  0xb3b, 0xb3c, 0xb4c, 0xb4d, 0xbcc, 0xbcd, 0xc4c, 0xc4d,
+  0xc54, 0xc56, 0xcbb, 0xcbc, 0xccc, 0xccd, 0xd3a, 0xd3c,
+  0xd4c, 0xd4d, 0xdc9, 0xdca, 0xe37, 0xe3a, 0xe47, 0xe4b,
+  0xeb7, 0xeba, 0xec7, 0xecb, 0xf17, 0xf19, 0xf34, 0xf35,
+  0xf36, 0xf37, 0xf38, 0xf39, 0xf70, 0xf72, 0xf73, 0xf74,
+  0xf79, 0xf7d, 0xf7f, 0xf80, 0xf81, 0xf84, 0xf85, 0xf87,
+  0xfc5, 0xfc6, 0x1036, 0x1037, 0x1038, 0x103a, 0x108c, 0x108d,
+  0x135c, 0x135f, 0x1713, 0x1714, 0x1733, 0x1734, 0x17d1, 0x17d2,
+  0x17dc, 0x17dd, 0x18a8, 0x18a9, 0x1938, 0x193b, 0x1a16, 0x1a18,
+  0x1a5f, 0x1a60, 0x1a74, 0x1a7c, 0x1a7e, 0x1a7f, 0x1aaf, 0x1abd,
+  0x1b33, 0x1b34, 0x1b43, 0x1b44, 0x1b6a, 0x1b73, 0x1ba9, 0x1bab,
+  0x1be5, 0x1be6, 0x1bf1, 0x1bf3, 0x1c36, 0x1c37, 0x1ccf, 0x1cd2,
+  0x1cd3, 0x1ce0, 0x1ce1, 0x1ce8, 0x1cec, 0x1ced, 0x1cf3, 0x1cf4,
+  0x1cf7, 0x1cf9, 0x1dbf, 0x1df9, 0x1dfa, 0x1dff, 0x20cf, 0x20dc,
+  0x20e0, 0x20e1, 0x20e4, 0x20f0, 0x2cee, 0x2cf1, 0x2d7e, 0x2d7f,
+  0x2ddf, 0x2dff, 0x3029, 0x302f, 0x3098, 0x309a, 0xa66e, 0xa66f,
+  0xa673, 0xa67d, 0xa69d, 0xa69f, 0xa6ef, 0xa6f1, 0xa805, 0xa806,
+  0xa8c3, 0xa8c4, 0xa8df, 0xa8f1, 0xa92a, 0xa92d, 0xa952, 0xa953,
+  0xa9b2, 0xa9b3, 0xa9bf, 0xa9c0, 0xaaaf, 0xaab0, 0xaab1, 0xaab4,
+  0xaab6, 0xaab8, 0xaabd, 0xaabf, 0xaac0, 0xaac1, 0xaaf5, 0xaaf6,
+  0xabec, 0xabed, 0xfb1d, 0xfb1e, 0xfe1f, 0xfe2f, 0x101fc, 0x101fd,
+  0x102df, 0x102e0, 0x10375, 0x1037a, 0x10a0c, 0x10a0d, 0x10a0e, 0x10a0f,
+  0x10a37, 0x10a3a, 0x10a3e, 0x10a3f, 0x10ae4, 0x10ae6, 0x10d23, 0x10d27,
+  0x10f45, 0x10f50, 0x11045, 0x11046, 0x1107e, 0x1107f, 0x110b8, 0x110ba,
+  0x110ff, 0x11102, 0x11132, 0x11134, 0x11172, 0x11173, 0x111bf, 0x111c0,
+  0x111c9, 0x111ca, 0x11234, 0x11236, 0x112e8, 0x112ea, 0x1133a, 0x1133c,
+  0x1134c, 0x1134d, 0x11365, 0x1136c, 0x1136f, 0x11374, 0x11441, 0x11442,
+  0x11445, 0x11446, 0x1145d, 0x1145e, 0x114c1, 0x114c3, 0x115be, 0x115c0,
+  0x1163e, 0x1163f, 0x116b5, 0x116b7, 0x1172a, 0x1172b, 0x11838, 0x1183a,
+  0x119df, 0x119e0, 0x11a33, 0x11a34, 0x11a46, 0x11a47, 0x11a98, 0x11a99,
+  0x11c3e, 0x11c3f, 0x11d41, 0x11d42, 0x11d43, 0x11d45, 0x11d96, 0x11d97,
+  0x16aef, 0x16af4, 0x16b2f, 0x16b36, 0x1bc9d, 0x1bc9e, 0x1d164, 0x1d169,
+  0x1d16c, 0x1d172, 0x1d17a, 0x1d182, 0x1d184, 0x1d18b, 0x1d1a9, 0x1d1ad,
+  0x1d241, 0x1d244, 0x1dfff, 0x1e006, 0x1e007, 0x1e018, 0x1e01a, 0x1e021,
+  0x1e022, 0x1e024, 0x1e025, 0x1e02a, 0x1e12f, 0x1e136, 0x1e2eb, 0x1e2ef,
+  0x1e8cf, 0x1e8d6, 0x1e943, 0x1e94a, 0x10fffe,
+};
+
+static const bool is_combining[] = {
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
+};
diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h
index a6f0abd..aef703f 100644
--- a/libcpp/include/cpplib.h
+++ b/libcpp/include/cpplib.h
@@ -174,7 +174,7 @@
 	     CLK_GNUCXX, CLK_CXX98, CLK_GNUCXX11, CLK_CXX11,
 	     CLK_GNUCXX14, CLK_CXX14, CLK_GNUCXX17, CLK_CXX17,
 	     CLK_GNUCXX20, CLK_CXX20, CLK_GNUCXX23, CLK_CXX23,
-	     CLK_ASM};
+	     CLK_GNUCXX26, CLK_CXX26, CLK_ASM};
 
 /* Payload of a NUMBER, STRING, CHAR or COMMENT token.  */
 struct GTY(()) cpp_string {
@@ -1602,4 +1602,7 @@
 int cpp_check_utf8_bom (const char *data, size_t data_length);
 bool cpp_valid_utf8_p (const char *data, size_t num_bytes);
 
+bool cpp_is_combining_char (cppchar_t c);
+bool cpp_is_printable_char (cppchar_t c);
+
 #endif /* ! LIBCPP_CPPLIB_H */
diff --git a/libcpp/init.cc b/libcpp/init.cc
index c508f06..693feaa 100644
--- a/libcpp/init.cc
+++ b/libcpp/init.cc
@@ -127,6 +127,8 @@
   /* CXX20    */  { 1,  1,  1,  1,  1,  1,    1,  1,   1,   1,   1,    1,     1,     0,   1,      1,   1,     0,   0,   0,      0,      0,    1 },
   /* GNUCXX23 */  { 1,  1,  1,  1,  1,  1,    0,  1,   1,   1,   1,    1,     1,     0,   1,      1,   1,     0,   1,   1,      1,      1,    1 },
   /* CXX23    */  { 1,  1,  1,  1,  1,  1,    1,  1,   1,   1,   1,    1,     1,     0,   1,      1,   1,     0,   1,   1,      1,      1,    1 },
+  /* GNUCXX26 */  { 1,  1,  1,  1,  1,  1,    0,  1,   1,   1,   1,    1,     1,     0,   1,      1,   1,     0,   1,   1,      1,      1,    1 },
+  /* CXX26    */  { 1,  1,  1,  1,  1,  1,    1,  1,   1,   1,   1,    1,     1,     0,   1,      1,   1,     0,   1,   1,      1,      1,    1 },
   /* ASM      */  { 0,  0,  1,  0,  0,  0,    0,  0,   0,   0,   0,    0,     0,     0,   0,      0,   0,     0,   0,   0,      0,      0,    0 }
 };
 
@@ -561,11 +563,14 @@
 
   if (CPP_OPTION (pfile, cplusplus))
     {
-      /* C++23 is not yet a standard.  For now, use an invalid
-       * year/month, 202100L, which is larger than 202002L.  */
-      if (CPP_OPTION (pfile, lang) == CLK_CXX23
+      /* C++26 is not yet a standard.  For now, use an invalid
+	 year/month, 202400L, which is larger than 202302L.  */
+      if (CPP_OPTION (pfile, lang) == CLK_CXX26
+	  || CPP_OPTION (pfile, lang) == CLK_GNUCXX26)
+	_cpp_define_builtin (pfile, "__cplusplus 202400L");
+      else if (CPP_OPTION (pfile, lang) == CLK_CXX23
 	  || CPP_OPTION (pfile, lang) == CLK_GNUCXX23)
-	_cpp_define_builtin (pfile, "__cplusplus 202100L");
+	_cpp_define_builtin (pfile, "__cplusplus 202302L");
       else if (CPP_OPTION (pfile, lang) == CLK_CXX20
 	  || CPP_OPTION (pfile, lang) == CLK_GNUCXX20)
 	_cpp_define_builtin (pfile, "__cplusplus 202002L");
diff --git a/libcpp/printable-chars.inc b/libcpp/printable-chars.inc
new file mode 100644
index 0000000..470b1ee
--- /dev/null
+++ b/libcpp/printable-chars.inc
@@ -0,0 +1,231 @@
+/* Generated by contrib/unicode/gen-printable-chars.py
+   using version 12.1.0 of the Unicode standard.  */
+
+static const cppchar_t printable_range_ends[] = {
+  0x1f, 0x7e, 0x9f, 0xac, 0xad, 0x377, 0x379, 0x37f,
+  0x383, 0x38a, 0x38b, 0x38c, 0x38d, 0x3a1, 0x3a2, 0x52f,
+  0x530, 0x556, 0x558, 0x58a, 0x58c, 0x58f, 0x590, 0x5c7,
+  0x5cf, 0x5ea, 0x5ee, 0x5f4, 0x605, 0x61b, 0x61d, 0x6dc,
+  0x6dd, 0x70d, 0x70f, 0x74a, 0x74c, 0x7b1, 0x7bf, 0x7fa,
+  0x7fc, 0x82d, 0x82f, 0x83e, 0x83f, 0x85b, 0x85d, 0x85e,
+  0x85f, 0x86a, 0x89f, 0x8b4, 0x8b5, 0x8bd, 0x8d2, 0x8e1,
+  0x8e2, 0x983, 0x984, 0x98c, 0x98e, 0x990, 0x992, 0x9a8,
+  0x9a9, 0x9b0, 0x9b1, 0x9b2, 0x9b5, 0x9b9, 0x9bb, 0x9c4,
+  0x9c6, 0x9c8, 0x9ca, 0x9ce, 0x9d6, 0x9d7, 0x9db, 0x9dd,
+  0x9de, 0x9e3, 0x9e5, 0x9fe, 0xa00, 0xa03, 0xa04, 0xa0a,
+  0xa0e, 0xa10, 0xa12, 0xa28, 0xa29, 0xa30, 0xa31, 0xa33,
+  0xa34, 0xa36, 0xa37, 0xa39, 0xa3b, 0xa3c, 0xa3d, 0xa42,
+  0xa46, 0xa48, 0xa4a, 0xa4d, 0xa50, 0xa51, 0xa58, 0xa5c,
+  0xa5d, 0xa5e, 0xa65, 0xa76, 0xa80, 0xa83, 0xa84, 0xa8d,
+  0xa8e, 0xa91, 0xa92, 0xaa8, 0xaa9, 0xab0, 0xab1, 0xab3,
+  0xab4, 0xab9, 0xabb, 0xac5, 0xac6, 0xac9, 0xaca, 0xacd,
+  0xacf, 0xad0, 0xadf, 0xae3, 0xae5, 0xaf1, 0xaf8, 0xaff,
+  0xb00, 0xb03, 0xb04, 0xb0c, 0xb0e, 0xb10, 0xb12, 0xb28,
+  0xb29, 0xb30, 0xb31, 0xb33, 0xb34, 0xb39, 0xb3b, 0xb44,
+  0xb46, 0xb48, 0xb4a, 0xb4d, 0xb55, 0xb57, 0xb5b, 0xb5d,
+  0xb5e, 0xb63, 0xb65, 0xb77, 0xb81, 0xb83, 0xb84, 0xb8a,
+  0xb8d, 0xb90, 0xb91, 0xb95, 0xb98, 0xb9a, 0xb9b, 0xb9c,
+  0xb9d, 0xb9f, 0xba2, 0xba4, 0xba7, 0xbaa, 0xbad, 0xbb9,
+  0xbbd, 0xbc2, 0xbc5, 0xbc8, 0xbc9, 0xbcd, 0xbcf, 0xbd0,
+  0xbd6, 0xbd7, 0xbe5, 0xbfa, 0xbff, 0xc0c, 0xc0d, 0xc10,
+  0xc11, 0xc28, 0xc29, 0xc39, 0xc3c, 0xc44, 0xc45, 0xc48,
+  0xc49, 0xc4d, 0xc54, 0xc56, 0xc57, 0xc5a, 0xc5f, 0xc63,
+  0xc65, 0xc6f, 0xc76, 0xc8c, 0xc8d, 0xc90, 0xc91, 0xca8,
+  0xca9, 0xcb3, 0xcb4, 0xcb9, 0xcbb, 0xcc4, 0xcc5, 0xcc8,
+  0xcc9, 0xccd, 0xcd4, 0xcd6, 0xcdd, 0xcde, 0xcdf, 0xce3,
+  0xce5, 0xcef, 0xcf0, 0xcf2, 0xcff, 0xd03, 0xd04, 0xd0c,
+  0xd0d, 0xd10, 0xd11, 0xd44, 0xd45, 0xd48, 0xd49, 0xd4f,
+  0xd53, 0xd63, 0xd65, 0xd7f, 0xd81, 0xd83, 0xd84, 0xd96,
+  0xd99, 0xdb1, 0xdb2, 0xdbb, 0xdbc, 0xdbd, 0xdbf, 0xdc6,
+  0xdc9, 0xdca, 0xdce, 0xdd4, 0xdd5, 0xdd6, 0xdd7, 0xddf,
+  0xde5, 0xdef, 0xdf1, 0xdf4, 0xe00, 0xe3a, 0xe3e, 0xe5b,
+  0xe80, 0xe82, 0xe83, 0xe84, 0xe85, 0xe8a, 0xe8b, 0xea3,
+  0xea4, 0xea5, 0xea6, 0xebd, 0xebf, 0xec4, 0xec5, 0xec6,
+  0xec7, 0xecd, 0xecf, 0xed9, 0xedb, 0xedf, 0xeff, 0xf47,
+  0xf48, 0xf6c, 0xf70, 0xf97, 0xf98, 0xfbc, 0xfbd, 0xfcc,
+  0xfcd, 0xfda, 0xfff, 0x10c5, 0x10c6, 0x10c7, 0x10cc, 0x10cd,
+  0x10cf, 0x1248, 0x1249, 0x124d, 0x124f, 0x1256, 0x1257, 0x1258,
+  0x1259, 0x125d, 0x125f, 0x1288, 0x1289, 0x128d, 0x128f, 0x12b0,
+  0x12b1, 0x12b5, 0x12b7, 0x12be, 0x12bf, 0x12c0, 0x12c1, 0x12c5,
+  0x12c7, 0x12d6, 0x12d7, 0x1310, 0x1311, 0x1315, 0x1317, 0x135a,
+  0x135c, 0x137c, 0x137f, 0x1399, 0x139f, 0x13f5, 0x13f7, 0x13fd,
+  0x13ff, 0x169c, 0x169f, 0x16f8, 0x16ff, 0x170c, 0x170d, 0x1714,
+  0x171f, 0x1736, 0x173f, 0x1753, 0x175f, 0x176c, 0x176d, 0x1770,
+  0x1771, 0x1773, 0x177f, 0x17dd, 0x17df, 0x17e9, 0x17ef, 0x17f9,
+  0x17ff, 0x180d, 0x180f, 0x1819, 0x181f, 0x1878, 0x187f, 0x18aa,
+  0x18af, 0x18f5, 0x18ff, 0x191e, 0x191f, 0x192b, 0x192f, 0x193b,
+  0x193f, 0x1940, 0x1943, 0x196d, 0x196f, 0x1974, 0x197f, 0x19ab,
+  0x19af, 0x19c9, 0x19cf, 0x19da, 0x19dd, 0x1a1b, 0x1a1d, 0x1a5e,
+  0x1a5f, 0x1a7c, 0x1a7e, 0x1a89, 0x1a8f, 0x1a99, 0x1a9f, 0x1aad,
+  0x1aaf, 0x1abe, 0x1aff, 0x1b4b, 0x1b4f, 0x1b7c, 0x1b7f, 0x1bf3,
+  0x1bfb, 0x1c37, 0x1c3a, 0x1c49, 0x1c4c, 0x1c88, 0x1c8f, 0x1cba,
+  0x1cbc, 0x1cc7, 0x1ccf, 0x1cfa, 0x1cff, 0x1df9, 0x1dfa, 0x1f15,
+  0x1f17, 0x1f1d, 0x1f1f, 0x1f45, 0x1f47, 0x1f4d, 0x1f4f, 0x1f57,
+  0x1f58, 0x1f59, 0x1f5a, 0x1f5b, 0x1f5c, 0x1f5d, 0x1f5e, 0x1f7d,
+  0x1f7f, 0x1fb4, 0x1fb5, 0x1fc4, 0x1fc5, 0x1fd3, 0x1fd5, 0x1fdb,
+  0x1fdc, 0x1fef, 0x1ff1, 0x1ff4, 0x1ff5, 0x1ffe, 0x1fff, 0x200a,
+  0x200f, 0x2029, 0x202e, 0x205f, 0x206f, 0x2071, 0x2073, 0x208e,
+  0x208f, 0x209c, 0x209f, 0x20bf, 0x20cf, 0x20f0, 0x20ff, 0x218b,
+  0x218f, 0x2426, 0x243f, 0x244a, 0x245f, 0x2b73, 0x2b75, 0x2b95,
+  0x2b97, 0x2c2e, 0x2c2f, 0x2c5e, 0x2c5f, 0x2cf3, 0x2cf8, 0x2d25,
+  0x2d26, 0x2d27, 0x2d2c, 0x2d2d, 0x2d2f, 0x2d67, 0x2d6e, 0x2d70,
+  0x2d7e, 0x2d96, 0x2d9f, 0x2da6, 0x2da7, 0x2dae, 0x2daf, 0x2db6,
+  0x2db7, 0x2dbe, 0x2dbf, 0x2dc6, 0x2dc7, 0x2dce, 0x2dcf, 0x2dd6,
+  0x2dd7, 0x2dde, 0x2ddf, 0x2e4f, 0x2e7f, 0x2e99, 0x2e9a, 0x2ef3,
+  0x2eff, 0x2fd5, 0x2fef, 0x2ffb, 0x2fff, 0x303f, 0x3040, 0x3096,
+  0x3098, 0x30ff, 0x3104, 0x312f, 0x3130, 0x318e, 0x318f, 0x31ba,
+  0x31bf, 0x31e3, 0x31ef, 0x321e, 0x321f, 0x4db5, 0x4dbf, 0x9fef,
+  0x9fff, 0xa48c, 0xa48f, 0xa4c6, 0xa4cf, 0xa62b, 0xa63f, 0xa6f7,
+  0xa6ff, 0xa7bf, 0xa7c1, 0xa7c6, 0xa7f6, 0xa82b, 0xa82f, 0xa839,
+  0xa83f, 0xa877, 0xa87f, 0xa8c5, 0xa8cd, 0xa8d9, 0xa8df, 0xa953,
+  0xa95e, 0xa97c, 0xa97f, 0xa9cd, 0xa9ce, 0xa9d9, 0xa9dd, 0xa9fe,
+  0xa9ff, 0xaa36, 0xaa3f, 0xaa4d, 0xaa4f, 0xaa59, 0xaa5b, 0xaac2,
+  0xaada, 0xaaf6, 0xab00, 0xab06, 0xab08, 0xab0e, 0xab10, 0xab16,
+  0xab1f, 0xab26, 0xab27, 0xab2e, 0xab2f, 0xab67, 0xab6f, 0xabed,
+  0xabef, 0xabf9, 0xabff, 0xd7a3, 0xd7af, 0xd7c6, 0xd7ca, 0xd7fb,
+  0xf8ff, 0xfa6d, 0xfa6f, 0xfad9, 0xfaff, 0xfb06, 0xfb12, 0xfb17,
+  0xfb1c, 0xfb36, 0xfb37, 0xfb3c, 0xfb3d, 0xfb3e, 0xfb3f, 0xfb41,
+  0xfb42, 0xfb44, 0xfb45, 0xfbc1, 0xfbd2, 0xfd3f, 0xfd4f, 0xfd8f,
+  0xfd91, 0xfdc7, 0xfdef, 0xfdfd, 0xfdff, 0xfe19, 0xfe1f, 0xfe52,
+  0xfe53, 0xfe66, 0xfe67, 0xfe6b, 0xfe6f, 0xfe74, 0xfe75, 0xfefc,
+  0xff00, 0xffbe, 0xffc1, 0xffc7, 0xffc9, 0xffcf, 0xffd1, 0xffd7,
+  0xffd9, 0xffdc, 0xffdf, 0xffe6, 0xffe7, 0xffee, 0xfffb, 0xfffd,
+  0xffff, 0x1000b, 0x1000c, 0x10026, 0x10027, 0x1003a, 0x1003b, 0x1003d,
+  0x1003e, 0x1004d, 0x1004f, 0x1005d, 0x1007f, 0x100fa, 0x100ff, 0x10102,
+  0x10106, 0x10133, 0x10136, 0x1018e, 0x1018f, 0x1019b, 0x1019f, 0x101a0,
+  0x101cf, 0x101fd, 0x1027f, 0x1029c, 0x1029f, 0x102d0, 0x102df, 0x102fb,
+  0x102ff, 0x10323, 0x1032c, 0x1034a, 0x1034f, 0x1037a, 0x1037f, 0x1039d,
+  0x1039e, 0x103c3, 0x103c7, 0x103d5, 0x103ff, 0x1049d, 0x1049f, 0x104a9,
+  0x104af, 0x104d3, 0x104d7, 0x104fb, 0x104ff, 0x10527, 0x1052f, 0x10563,
+  0x1056e, 0x1056f, 0x105ff, 0x10736, 0x1073f, 0x10755, 0x1075f, 0x10767,
+  0x107ff, 0x10805, 0x10807, 0x10808, 0x10809, 0x10835, 0x10836, 0x10838,
+  0x1083b, 0x1083c, 0x1083e, 0x10855, 0x10856, 0x1089e, 0x108a6, 0x108af,
+  0x108df, 0x108f2, 0x108f3, 0x108f5, 0x108fa, 0x1091b, 0x1091e, 0x10939,
+  0x1093e, 0x1093f, 0x1097f, 0x109b7, 0x109bb, 0x109cf, 0x109d1, 0x10a03,
+  0x10a04, 0x10a06, 0x10a0b, 0x10a13, 0x10a14, 0x10a17, 0x10a18, 0x10a35,
+  0x10a37, 0x10a3a, 0x10a3e, 0x10a48, 0x10a4f, 0x10a58, 0x10a5f, 0x10a9f,
+  0x10abf, 0x10ae6, 0x10aea, 0x10af6, 0x10aff, 0x10b35, 0x10b38, 0x10b55,
+  0x10b57, 0x10b72, 0x10b77, 0x10b91, 0x10b98, 0x10b9c, 0x10ba8, 0x10baf,
+  0x10bff, 0x10c48, 0x10c7f, 0x10cb2, 0x10cbf, 0x10cf2, 0x10cf9, 0x10d27,
+  0x10d2f, 0x10d39, 0x10e5f, 0x10e7e, 0x10eff, 0x10f27, 0x10f2f, 0x10f59,
+  0x10fdf, 0x10ff6, 0x10fff, 0x1104d, 0x11051, 0x1106f, 0x1107e, 0x110bc,
+  0x110bd, 0x110c1, 0x110cf, 0x110e8, 0x110ef, 0x110f9, 0x110ff, 0x11134,
+  0x11135, 0x11146, 0x1114f, 0x11176, 0x1117f, 0x111cd, 0x111cf, 0x111df,
+  0x111e0, 0x111f4, 0x111ff, 0x11211, 0x11212, 0x1123e, 0x1127f, 0x11286,
+  0x11287, 0x11288, 0x11289, 0x1128d, 0x1128e, 0x1129d, 0x1129e, 0x112a9,
+  0x112af, 0x112ea, 0x112ef, 0x112f9, 0x112ff, 0x11303, 0x11304, 0x1130c,
+  0x1130e, 0x11310, 0x11312, 0x11328, 0x11329, 0x11330, 0x11331, 0x11333,
+  0x11334, 0x11339, 0x1133a, 0x11344, 0x11346, 0x11348, 0x1134a, 0x1134d,
+  0x1134f, 0x11350, 0x11356, 0x11357, 0x1135c, 0x11363, 0x11365, 0x1136c,
+  0x1136f, 0x11374, 0x113ff, 0x11459, 0x1145a, 0x1145b, 0x1145c, 0x1145f,
+  0x1147f, 0x114c7, 0x114cf, 0x114d9, 0x1157f, 0x115b5, 0x115b7, 0x115dd,
+  0x115ff, 0x11644, 0x1164f, 0x11659, 0x1165f, 0x1166c, 0x1167f, 0x116b8,
+  0x116bf, 0x116c9, 0x116ff, 0x1171a, 0x1171c, 0x1172b, 0x1172f, 0x1173f,
+  0x117ff, 0x1183b, 0x1189f, 0x118f2, 0x118fe, 0x118ff, 0x1199f, 0x119a7,
+  0x119a9, 0x119d7, 0x119d9, 0x119e4, 0x119ff, 0x11a47, 0x11a4f, 0x11aa2,
+  0x11abf, 0x11af8, 0x11bff, 0x11c08, 0x11c09, 0x11c36, 0x11c37, 0x11c45,
+  0x11c4f, 0x11c6c, 0x11c6f, 0x11c8f, 0x11c91, 0x11ca7, 0x11ca8, 0x11cb6,
+  0x11cff, 0x11d06, 0x11d07, 0x11d09, 0x11d0a, 0x11d36, 0x11d39, 0x11d3a,
+  0x11d3b, 0x11d3d, 0x11d3e, 0x11d47, 0x11d4f, 0x11d59, 0x11d5f, 0x11d65,
+  0x11d66, 0x11d68, 0x11d69, 0x11d8e, 0x11d8f, 0x11d91, 0x11d92, 0x11d98,
+  0x11d9f, 0x11da9, 0x11edf, 0x11ef8, 0x11fbf, 0x11ff1, 0x11ffe, 0x12399,
+  0x123ff, 0x1246e, 0x1246f, 0x12474, 0x1247f, 0x12543, 0x12fff, 0x1342e,
+  0x143ff, 0x14646, 0x167ff, 0x16a38, 0x16a3f, 0x16a5e, 0x16a5f, 0x16a69,
+  0x16a6d, 0x16a6f, 0x16acf, 0x16aed, 0x16aef, 0x16af5, 0x16aff, 0x16b45,
+  0x16b4f, 0x16b59, 0x16b5a, 0x16b61, 0x16b62, 0x16b77, 0x16b7c, 0x16b8f,
+  0x16e3f, 0x16e9a, 0x16eff, 0x16f4a, 0x16f4e, 0x16f87, 0x16f8e, 0x16f9f,
+  0x16fdf, 0x16fe3, 0x16fff, 0x187f7, 0x187ff, 0x18af2, 0x1afff, 0x1b11e,
+  0x1b14f, 0x1b152, 0x1b163, 0x1b167, 0x1b16f, 0x1b2fb, 0x1bbff, 0x1bc6a,
+  0x1bc6f, 0x1bc7c, 0x1bc7f, 0x1bc88, 0x1bc8f, 0x1bc99, 0x1bc9b, 0x1bc9f,
+  0x1cfff, 0x1d0f5, 0x1d0ff, 0x1d126, 0x1d128, 0x1d172, 0x1d17a, 0x1d1e8,
+  0x1d1ff, 0x1d245, 0x1d2df, 0x1d2f3, 0x1d2ff, 0x1d356, 0x1d35f, 0x1d378,
+  0x1d3ff, 0x1d454, 0x1d455, 0x1d49c, 0x1d49d, 0x1d49f, 0x1d4a1, 0x1d4a2,
+  0x1d4a4, 0x1d4a6, 0x1d4a8, 0x1d4ac, 0x1d4ad, 0x1d4b9, 0x1d4ba, 0x1d4bb,
+  0x1d4bc, 0x1d4c3, 0x1d4c4, 0x1d505, 0x1d506, 0x1d50a, 0x1d50c, 0x1d514,
+  0x1d515, 0x1d51c, 0x1d51d, 0x1d539, 0x1d53a, 0x1d53e, 0x1d53f, 0x1d544,
+  0x1d545, 0x1d546, 0x1d549, 0x1d550, 0x1d551, 0x1d6a5, 0x1d6a7, 0x1d7cb,
+  0x1d7cd, 0x1da8b, 0x1da9a, 0x1da9f, 0x1daa0, 0x1daaf, 0x1dfff, 0x1e006,
+  0x1e007, 0x1e018, 0x1e01a, 0x1e021, 0x1e022, 0x1e024, 0x1e025, 0x1e02a,
+  0x1e0ff, 0x1e12c, 0x1e12f, 0x1e13d, 0x1e13f, 0x1e149, 0x1e14d, 0x1e14f,
+  0x1e2bf, 0x1e2f9, 0x1e2fe, 0x1e2ff, 0x1e7ff, 0x1e8c4, 0x1e8c6, 0x1e8d6,
+  0x1e8ff, 0x1e94b, 0x1e94f, 0x1e959, 0x1e95d, 0x1e95f, 0x1ec70, 0x1ecb4,
+  0x1ed00, 0x1ed3d, 0x1edff, 0x1ee03, 0x1ee04, 0x1ee1f, 0x1ee20, 0x1ee22,
+  0x1ee23, 0x1ee24, 0x1ee26, 0x1ee27, 0x1ee28, 0x1ee32, 0x1ee33, 0x1ee37,
+  0x1ee38, 0x1ee39, 0x1ee3a, 0x1ee3b, 0x1ee41, 0x1ee42, 0x1ee46, 0x1ee47,
+  0x1ee48, 0x1ee49, 0x1ee4a, 0x1ee4b, 0x1ee4c, 0x1ee4f, 0x1ee50, 0x1ee52,
+  0x1ee53, 0x1ee54, 0x1ee56, 0x1ee57, 0x1ee58, 0x1ee59, 0x1ee5a, 0x1ee5b,
+  0x1ee5c, 0x1ee5d, 0x1ee5e, 0x1ee5f, 0x1ee60, 0x1ee62, 0x1ee63, 0x1ee64,
+  0x1ee66, 0x1ee6a, 0x1ee6b, 0x1ee72, 0x1ee73, 0x1ee77, 0x1ee78, 0x1ee7c,
+  0x1ee7d, 0x1ee7e, 0x1ee7f, 0x1ee89, 0x1ee8a, 0x1ee9b, 0x1eea0, 0x1eea3,
+  0x1eea4, 0x1eea9, 0x1eeaa, 0x1eebb, 0x1eeef, 0x1eef1, 0x1efff, 0x1f02b,
+  0x1f02f, 0x1f093, 0x1f09f, 0x1f0ae, 0x1f0b0, 0x1f0bf, 0x1f0c0, 0x1f0cf,
+  0x1f0d0, 0x1f0f5, 0x1f0ff, 0x1f10c, 0x1f10f, 0x1f16c, 0x1f16f, 0x1f1ac,
+  0x1f1e5, 0x1f202, 0x1f20f, 0x1f23b, 0x1f23f, 0x1f248, 0x1f24f, 0x1f251,
+  0x1f25f, 0x1f265, 0x1f2ff, 0x1f6d5, 0x1f6df, 0x1f6ec, 0x1f6ef, 0x1f6fa,
+  0x1f6ff, 0x1f773, 0x1f77f, 0x1f7d8, 0x1f7df, 0x1f7eb, 0x1f7ff, 0x1f80b,
+  0x1f80f, 0x1f847, 0x1f84f, 0x1f859, 0x1f85f, 0x1f887, 0x1f88f, 0x1f8ad,
+  0x1f8ff, 0x1f90b, 0x1f90c, 0x1f971, 0x1f972, 0x1f976, 0x1f979, 0x1f9a2,
+  0x1f9a4, 0x1f9aa, 0x1f9ad, 0x1f9ca, 0x1f9cc, 0x1fa53, 0x1fa5f, 0x1fa6d,
+  0x1fa6f, 0x1fa73, 0x1fa77, 0x1fa7a, 0x1fa7f, 0x1fa82, 0x1fa8f, 0x1fa95,
+  0x1ffff, 0x2a6d6, 0x2a6ff, 0x2b734, 0x2b73f, 0x2b81d, 0x2b81f, 0x2cea1,
+  0x2ceaf, 0x2ebe0, 0x2f7ff, 0x2fa1d, 0xe00ff, 0xe01ef, 0x10fffe,
+};
+
+static const bool is_printable[] = {
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+  0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
+};
diff --git a/libgo/Makefile.am b/libgo/Makefile.am
index 920f8cc..c95dc21 100644
--- a/libgo/Makefile.am
+++ b/libgo/Makefile.am
@@ -417,6 +417,7 @@
 # Some internal packages are needed to bootstrap the gc toolchain.
 toolexeclibgointernaldir = $(toolexeclibgodir)/internal
 toolexeclibgointernal_DATA = \
+	internal/lazyregexp.gox \
 	internal/reflectlite.gox \
 	internal/unsafeheader.gox
 
diff --git a/libgo/Makefile.in b/libgo/Makefile.in
index 6176eb5..40340bf 100644
--- a/libgo/Makefile.in
+++ b/libgo/Makefile.in
@@ -885,6 +885,7 @@
 # Some internal packages are needed to bootstrap the gc toolchain.
 toolexeclibgointernaldir = $(toolexeclibgodir)/internal
 toolexeclibgointernal_DATA = \
+	internal/lazyregexp.gox \
 	internal/reflectlite.gox \
 	internal/unsafeheader.gox
 
diff --git a/libgo/go/internal/abi/abi.go b/libgo/go/internal/abi/abi.go
index c4a1088..6625127 100644
--- a/libgo/go/internal/abi/abi.go
+++ b/libgo/go/internal/abi/abi.go
@@ -17,10 +17,7 @@
 // compile-time error.
 //
 // Implemented as a compile intrinsic.
-func FuncPCABI0(f any) uintptr {
-	// The compiler should remove all calls.
-	panic("FuncPCABI0")
-}
+func FuncPCABI0(f any) uintptr
 
 // FuncPCABIInternal returns the entry PC of the function f. If f is a
 // direct reference of a function, it must be defined as ABIInternal.
@@ -29,7 +26,4 @@
 // the behavior is undefined.
 //
 // Implemented as a compile intrinsic.
-func FuncPCABIInternal(f any) uintptr {
-	// The compiler should remove all calls.
-	panic("FuncPCABIInternal")
-}
+func FuncPCABIInternal(f any) uintptr
diff --git a/libgomp/ChangeLog b/libgomp/ChangeLog
index 002d802..3bbcf41 100644
--- a/libgomp/ChangeLog
+++ b/libgomp/ChangeLog
@@ -1,3 +1,12 @@
+2023-06-22  Tobias Burnus  <tobias@codesourcery.com>
+
+	* libgomp.texi: Use @var for ICV vars.
+	(OpenMP Environment Variables): Mention _ALL/_DEV/_DEV_<no> variants,
+	document which ICV is set and which scope the ICV has; extend/cleanup
+	some @ref.
+	(Implementation-defined ICV Initialization): New.
+	(nvptx): Document the implementation-defined used per-warp stack size.
+
 2023-06-19  Thomas Schwinge  <thomas@codesourcery.com>
 
 	* testsuite/libgomp.c/target-51.c: Fix DejaGnu directive syntax
diff --git a/libgomp/libgomp.texi b/libgomp/libgomp.texi
index db8b1f1..7d27cc5 100644
--- a/libgomp/libgomp.texi
+++ b/libgomp/libgomp.texi
@@ -188,9 +188,9 @@
 @item @code{metadirective} directive @tab N @tab
 @item @code{declare variant} directive
       @tab P @tab @emph{simd} traits not handled correctly
-@item @emph{target-offload-var} ICV and @code{OMP_TARGET_OFFLOAD}
+@item @var{target-offload-var} ICV and @code{OMP_TARGET_OFFLOAD}
       env variable @tab Y @tab
-@item Nested-parallel changes to @emph{max-active-levels-var} ICV @tab Y @tab
+@item Nested-parallel changes to @var{max-active-levels-var} ICV @tab Y @tab
 @item @code{requires} directive @tab P
       @tab complete but no non-host devices provides @code{unified_shared_memory}
 @item @code{teams} construct outside an enclosing target region @tab Y @tab
@@ -364,7 +364,7 @@
 
 @multitable @columnfractions .60 .10 .25
 @headitem Description @tab Status @tab Comments
-@item @code{omp_in_explicit_task} routine and @emph{explicit-task-var} ICV
+@item @code{omp_in_explicit_task} routine and @var{explicit-task-var} ICV
       @tab Y @tab
 @item @code{omp}/@code{ompx}/@code{omx} sentinels and @code{omp_}/@code{ompx_}
       namespaces @tab N/A
@@ -422,7 +422,7 @@
 @item For Fortran, optional comma between directive and clause @tab N @tab
 @item Conforming device numbers and @code{omp_initial_device} and
       @code{omp_invalid_device} enum/PARAMETER @tab Y @tab
-@item Initial value of @emph{default-device-var} ICV with
+@item Initial value of @var{default-device-var} ICV with
       @code{OMP_TARGET_OFFLOAD=mandatory} @tab Y @tab
 @item @emph{interop_types} in any position of the modifier list for the @code{init} clause
       of the @code{interop} construct @tab N @tab
@@ -912,6 +912,9 @@
 regions with @code{omp_set_max_active_levels} to one to disable, or
 above one to enable.
 
+Note that the @code{omp_get_nested} API routine was deprecated
+in the OpenMP specification 5.2 in favor of @code{omp_get_max_active_levels}.
+
 @item @emph{C/C++}:
 @multitable @columnfractions .20 .80
 @item @emph{Prototype}: @tab @code{int omp_get_nested(void);}
@@ -923,7 +926,7 @@
 @end multitable
 
 @item @emph{See also}:
-@ref{omp_set_max_active_levels}, @ref{omp_set_nested},
+@ref{omp_get_max_active_levels}, @ref{omp_set_nested},
 @ref{OMP_MAX_ACTIVE_LEVELS}, @ref{OMP_NESTED}
 
 @item @emph{Reference}:
@@ -1416,6 +1419,9 @@
 active nested regions to the maximum supported.  Disabling nested parallel
 regions will set the maximum number of active nested regions to one.
 
+Note that the @code{omp_set_nested} API routine was deprecated
+in the OpenMP specification 5.2 in favor of @code{omp_set_max_active_levels}.
+
 @item @emph{C/C++}:
 @multitable @columnfractions .20 .80
 @item @emph{Prototype}: @tab @code{void omp_set_nested(int nested);}
@@ -1933,8 +1939,21 @@
 @chapter OpenMP Environment Variables
 
 The environment variables which beginning with @env{OMP_} are defined by
-section 4 of the OpenMP specification in version 4.5, while those
-beginning with @env{GOMP_} are GNU extensions.
+section 4 of the OpenMP specification in version 4.5 or in a later version
+of the specification, while those beginning with @env{GOMP_} are GNU extensions.
+Most @env{OMP_} environment variables have an associated internal control
+variable (ICV).
+
+For any OpenMP environment variable that sets an ICV and is neither
+@code{OMP_DEFAULT_DEVICE} nor has global ICV scope, associated
+device-specific environment variables exist.  For them, the environment
+variable without suffix affects the host.  The suffix @code{_DEV_} followed
+by a non-negative device number less that the number of available devices sets
+the ICV for the corresponding device.  The suffix @code{_DEV} sets the ICV
+of all non-host devices for which a device-specific corresponding environment
+variable has not been set while the @code{_ALL} suffix sets the ICV of all
+host and non-host devices for which a more specific corresponding environment
+variable is not set.
 
 @menu
 * OMP_ALLOCATOR::           Set the default allocator
@@ -1969,6 +1988,8 @@
 @section @env{OMP_ALLOCATOR} -- Set the default allocator
 @cindex Environment Variable
 @table @asis
+@item @emph{ICV:} @var{available-devices-var}
+@item @emph{Scope:} data environment
 @item @emph{Description}:
 Sets the default allocator that is used when no allocator has been specified
 in the @code{allocate} or @code{allocator} clause or if an OpenMP memory
@@ -1979,6 +2000,12 @@
 or a predefined memory space followed by a colon and a comma-separated list
 of memory trait and value pairs, separated by @code{=}.
 
+Note: The corresponding device environment variables are currently not
+supported.  Therefore, the non-host @var{def-allocator-var} ICVs are always
+initialized to @code{omp_default_mem_alloc}.  However, on all devices,
+the @code{omp_set_default_allocator} API routine can be used to change
+value.
+
 @multitable @columnfractions .45 .45
 @headitem Predefined allocators @tab Predefined memory spaces
 @item omp_default_mem_alloc     @tab omp_default_mem_space
@@ -2026,6 +2053,8 @@
 @section @env{OMP_AFFINITY_FORMAT} -- Set the format string used for affinity display
 @cindex Environment Variable
 @table @asis
+@item @emph{ICV:} @var{affinity-format-var}
+@item @emph{Scope:} device
 @item @emph{Description}:
 Sets the format string used when displaying OpenMP thread affinity information.
 Special values are output using @code{%} followed by an optional size
@@ -2088,6 +2117,8 @@
 @section @env{OMP_CANCELLATION} -- Set whether cancellation is activated
 @cindex Environment Variable
 @table @asis
+@item @emph{ICV:} @var{cancel-var}
+@item @emph{Scope:} global
 @item @emph{Description}:
 If set to @code{TRUE}, the cancellation is activated.  If set to @code{FALSE} or
 if unset, cancellation is disabled and the @code{cancel} construct is ignored.
@@ -2105,6 +2136,8 @@
 @section @env{OMP_DISPLAY_AFFINITY} -- Display thread affinity information
 @cindex Environment Variable
 @table @asis
+@item @emph{ICV:} @var{display-affinity-var}
+@item @emph{Scope:} global
 @item @emph{Description}:
 If set to @code{FALSE} or if unset, affinity displaying is disabled.
 If set to @code{TRUE}, the runtime will display affinity information about
@@ -2125,6 +2158,8 @@
 @section @env{OMP_DISPLAY_ENV} -- Show OpenMP version and environment variables
 @cindex Environment Variable
 @table @asis
+@item @emph{ICV:} none
+@item @emph{Scope:} not applicable
 @item @emph{Description}:
 If set to @code{TRUE}, the OpenMP version number and the values
 associated with the OpenMP environment variables are printed to @code{stderr}.
@@ -2143,6 +2178,8 @@
 @section @env{OMP_DEFAULT_DEVICE} -- Set the device used in target regions
 @cindex Environment Variable
 @table @asis
+@item @emph{ICV:} @var{default-device-var}
+@item @emph{Scope:} data environment
 @item @emph{Description}:
 Set to choose the device which is used in a @code{target} region, unless the
 value is overridden by @code{omp_set_default_device} or by a @code{device}
@@ -2166,6 +2203,8 @@
 @section @env{OMP_DYNAMIC} -- Dynamic adjustment of threads
 @cindex Environment Variable
 @table @asis
+@item @emph{ICV:} @var{dyn-var}
+@item @emph{Scope:} global
 @item @emph{Description}:
 Enable or disable the dynamic adjustment of the number of threads 
 within a team.  The value of this environment variable shall be 
@@ -2185,6 +2224,8 @@
 @section @env{OMP_MAX_ACTIVE_LEVELS} -- Set the maximum number of nested parallel regions
 @cindex Environment Variable
 @table @asis
+@item @emph{ICV:} @var{max-active-levels-var}
+@item @emph{Scope:} data environment
 @item @emph{Description}:
 Specifies the initial value for the maximum number of nested parallel
 regions.  The value of this variable shall be a positive integer.
@@ -2195,7 +2236,9 @@
 it will be set to one.
 
 @item @emph{See also}:
-@ref{omp_set_max_active_levels}, @ref{OMP_NESTED}
+@ref{omp_set_max_active_levels}, @ref{OMP_NESTED}, @ref{OMP_PROC_BIND},
+@ref{OMP_NUM_THREADS}
+
 
 @item @emph{Reference}: 
 @uref{https://www.openmp.org, OpenMP specification v4.5}, Section 4.9
@@ -2208,6 +2251,8 @@
 number that can be set for a task.
 @cindex Environment Variable
 @table @asis
+@item @emph{ICV:} @var{max-task-priority-var}
+@item @emph{Scope:} global
 @item @emph{Description}:
 Specifies the initial value for the maximum priority value that can be
 set for a task.  The value of this variable shall be a non-negative
@@ -2228,6 +2273,8 @@
 @cindex Environment Variable
 @cindex Implementation specific setting
 @table @asis
+@item @emph{ICV:} @var{max-active-levels-var}
+@item @emph{Scope:} data environment
 @item @emph{Description}:
 Enable or disable nested parallel regions, i.e., whether team members
 are allowed to create new teams.  The value of this environment variable 
@@ -2239,8 +2286,12 @@
 @env{OMP_NUM_THREADS} or @env{OMP_PROC_BINDS} are defined to a list with
 more than one item, otherwise they are disabled by default.
 
+Note that the @code{OMP_NESTED} environment variable was deprecated in
+the OpenMP specification 5.2 in favor of @code{OMP_MAX_ACTIVE_LEVELS}.
+
 @item @emph{See also}:
-@ref{omp_set_max_active_levels}, @ref{omp_set_nested}
+@ref{omp_set_max_active_levels}, @ref{omp_set_nested},
+@ref{OMP_MAX_ACTIVE_LEVELS}
 
 @item @emph{Reference}: 
 @uref{https://www.openmp.org, OpenMP specification v4.5}, Section 4.6
@@ -2252,6 +2303,8 @@
 @section @env{OMP_NUM_TEAMS} -- Specifies the number of teams to use by teams region
 @cindex Environment Variable
 @table @asis
+@item @emph{ICV:} @var{nteams-var}
+@item @emph{Scope:} device
 @item @emph{Description}:
 Specifies the upper bound for number of teams to use in teams regions
 without explicit @code{num_teams} clause.  The value of this variable shall
@@ -2272,6 +2325,8 @@
 @cindex Environment Variable
 @cindex Implementation specific setting
 @table @asis
+@item @emph{ICV:} @var{nthreads-var}
+@item @emph{Scope:} data environment
 @item @emph{Description}:
 Specifies the default number of threads to use in parallel regions.  The 
 value of this variable shall be a comma-separated list of positive integers;
@@ -2279,8 +2334,11 @@
 level.  Specifying more than one item in the list will automatically enable
 nesting by default.  If undefined one thread per CPU is used.
 
+When a list with more than value is specified, it also affects the
+@var{max-active-levels-var} ICV as described in @ref{OMP_MAX_ACTIVE_LEVELS}.
+
 @item @emph{See also}:
-@ref{omp_set_num_threads}, @ref{OMP_NESTED}
+@ref{omp_set_num_threads}, @ref{OMP_MAX_ACTIVE_LEVELS}
 
 @item @emph{Reference}: 
 @uref{https://www.openmp.org, OpenMP specification v4.5}, Section 4.2
@@ -2292,6 +2350,8 @@
 @section @env{OMP_PROC_BIND} -- Whether threads may be moved between CPUs
 @cindex Environment Variable
 @table @asis
+@item @emph{ICV:} @var{bind-var}
+@item @emph{Scope:} data environment
 @item @emph{Description}:
 Specifies whether threads may be moved between processors.  If set to
 @code{TRUE}, OpenMP threads should not be moved; if set to @code{FALSE}
@@ -2305,12 +2365,15 @@
 across the place partitions is used.  Specifying more than one item in the
 list will automatically enable nesting by default.
 
+When a list is specified, it also affects the @var{max-active-levels-var} ICV
+as described in @ref{OMP_MAX_ACTIVE_LEVELS}.
+
 When undefined, @env{OMP_PROC_BIND} defaults to @code{TRUE} when
 @env{OMP_PLACES} or @env{GOMP_CPU_AFFINITY} is set and @code{FALSE} otherwise.
 
 @item @emph{See also}:
-@ref{omp_get_proc_bind}, @ref{GOMP_CPU_AFFINITY},
-@ref{OMP_NESTED}, @ref{OMP_PLACES}
+@ref{omp_get_proc_bind}, @ref{GOMP_CPU_AFFINITY}, @ref{OMP_PLACES},
+@ref{OMP_MAX_ACTIVE_LEVELS}
 
 @item @emph{Reference}:
 @uref{https://www.openmp.org, OpenMP specification v4.5}, Section 4.4
@@ -2322,6 +2385,8 @@
 @section @env{OMP_PLACES} -- Specifies on which CPUs the threads should be placed
 @cindex Environment Variable
 @table @asis
+@item @emph{ICV:} @var{place-partition-var}
+@item @emph{Scope:} implicit tasks
 @item @emph{Description}:
 The thread placement can be either specified using an abstract name or by an
 explicit list of the places.  The abstract names @code{threads}, @code{cores},
@@ -2372,6 +2437,8 @@
 @section @env{OMP_STACKSIZE} -- Set default thread stack size
 @cindex Environment Variable
 @table @asis
+@item @emph{ICV:} @var{stacksize-var}
+@item @emph{Scope:} device
 @item @emph{Description}:
 Set the default thread stack size in kilobytes, unless the number
 is suffixed by @code{B}, @code{K}, @code{M} or @code{G}, in which
@@ -2382,6 +2449,9 @@
 stack size is left unchanged.  If undefined, the stack size is system
 dependent.
 
+@item @emph{See also}:
+@ref{GOMP_STACKSIZE}
+
 @item @emph{Reference}: 
 @uref{https://www.openmp.org, OpenMP specification v4.5}, Section 4.7
 @end table
@@ -2393,6 +2463,8 @@
 @cindex Environment Variable
 @cindex Implementation specific setting
 @table @asis
+@item @emph{ICV:} @var{run-sched-var}
+@item @emph{Scope:} data environment
 @item @emph{Description}:
 Allows to specify @code{schedule type} and @code{chunk size}. 
 The value of the variable shall have the form: @code{type[,chunk]} where
@@ -2414,6 +2486,8 @@
 @cindex Environment Variable
 @cindex Implementation specific setting
 @table @asis
+@item @emph{ICV:} @var{target-offload-var}
+@item @emph{Scope:} global
 @item @emph{Description}:
 Specifies the behaviour with regard to offloading code to a device.  This
 variable can be set to one of three values - @code{MANDATORY}, @code{DISABLED}
@@ -2437,6 +2511,8 @@
 @section @env{OMP_TEAMS_THREAD_LIMIT} -- Set the maximum number of threads imposed by teams
 @cindex Environment Variable
 @table @asis
+@item @emph{ICV:} @var{teams-thread-limit-var}
+@item @emph{Scope:} device
 @item @emph{Description}:
 Specifies an upper bound for the number of threads to use by each contention
 group created by a teams construct without explicit @code{thread_limit}
@@ -2457,6 +2533,8 @@
 @section @env{OMP_THREAD_LIMIT} -- Set the maximum number of threads
 @cindex Environment Variable
 @table @asis
+@item @emph{ICV:} @var{thread-limit-var}
+@item @emph{Scope:} data environment
 @item @emph{Description}:
 Specifies the number of threads to use for the whole program.  The
 value of this variable shall be a positive integer.  If undefined,
@@ -4504,10 +4582,35 @@
 @chapter OpenMP-Implementation Specifics
 
 @menu
+* Implementation-defined ICV Initialization::
 * OpenMP Context Selectors::
 * Memory allocation with libmemkind::
 @end menu
 
+@node Implementation-defined ICV Initialization
+@section Implementation-defined ICV Initialization
+@cindex Implementation specific setting
+
+@multitable @columnfractions .30 .70
+@item @var{affinity-format-var} @tab See @ref{OMP_AFFINITY_FORMAT}.
+@item @var{def-allocator-var} @tab See @ref{OMP_ALLOCATOR}.
+@item @var{max-active-levels-var} @tab See @ref{OMP_MAX_ACTIVE_LEVELS}.
+@item @var{dyn-var} @tab See @ref{OMP_DYNAMIC}.
+@item @var{nthreads-var} @tab See @code{OMP_NUM_THREADS}.
+@item @var{num-devices-var} @tab Number of non-host devices found
+by GCC's run-time library
+@item @var{num-procs-var} @tab The number of CPU cores on the
+initial device, except that affinity settings might lead to a
+smaller number.  On non-host devices, the value of the
+@var{nthreads-var} ICV.
+@item @var{place-partition-var} @tab See @ref{OMP_PLACES}.
+@item @var{run-sched-var} @tab See @ref{OMP_SCHEDULE}.
+@item @var{stacksize-var} @tab See @ref{OMP_STACKSIZE}.
+@item @var{thread-limit-var} @tab See @ref{OMP_TEAMS_THREAD_LIMIT}
+@item @var{wait-policy-var} @tab See @ref{OMP_WAIT_POLICY} and
+@ref{GOMP_SPINCOUNT}
+@end multitable
+
 @node OpenMP Context Selectors
 @section OpenMP Context Selectors
 
@@ -4668,6 +4771,8 @@
 @item OpenMP code that has a @code{requires} directive with
       @code{unified_shared_memory} will remove any nvptx device from the
       list of available devices (``host fallback'').
+@item The default per-warp stack size is 128 kiB; see also @code{-msoft-stack}
+      in the GCC manual.
 @end itemize
 
 
diff --git a/libphobos/ChangeLog b/libphobos/ChangeLog
index 70d4f55..d3ceebf 100644
--- a/libphobos/ChangeLog
+++ b/libphobos/ChangeLog
@@ -1,3 +1,8 @@
+2023-06-25  Iain Buclaw  <ibuclaw@gdcproject.org>
+
+	* libdruntime/MERGE: Merge upstream druntime a45f4e9f43.
+	* src/MERGE: Merge upstream phobos 106038f2e.
+
 2023-03-17  Iain Buclaw  <ibuclaw@gdcproject.org>
 
 	* libdruntime/MERGE: Merge upstream druntime 5f7552bb28.
diff --git a/libphobos/libdruntime/MERGE b/libphobos/libdruntime/MERGE
index 986925e..1205cd9 100644
--- a/libphobos/libdruntime/MERGE
+++ b/libphobos/libdruntime/MERGE
@@ -1,4 +1,4 @@
-5f7552bb2829b75d5e36cc767a476e1ab35147b7
+a45f4e9f43e9fdbf0b666175e5e66b1ce4f561f6
 
 The first line of this file holds the git revision number of the last
 merge done from the dlang/dmd repository.
diff --git a/libphobos/libdruntime/core/sys/windows/stacktrace.d b/libphobos/libdruntime/core/sys/windows/stacktrace.d
index c10a917..a73fc9c 100644
--- a/libphobos/libdruntime/core/sys/windows/stacktrace.d
+++ b/libphobos/libdruntime/core/sys/windows/stacktrace.d
@@ -239,6 +239,8 @@
             if (frameNum >= skip)
             {
                 buffer[nframes++] = stackframe.AddrPC.Offset;
+                if (nframes >= buffer.length)
+                    break;
             }
             frameNum++;
         }
diff --git a/libphobos/src/MERGE b/libphobos/src/MERGE
index e72db81..2b6bc3e 100644
--- a/libphobos/src/MERGE
+++ b/libphobos/src/MERGE
@@ -1,4 +1,4 @@
-67a47cf39d52b3cb3ae4117c0237415e03737f8a
+106038f2eaa70045bf25b29bb1c789304a6065f7
 
 The first line of this file holds the git revision number of the last
 merge done from the dlang/phobos repository.
diff --git a/libphobos/src/std/functional.d b/libphobos/src/std/functional.d
index 70aaee3..588a9c8 100644
--- a/libphobos/src/std/functional.d
+++ b/libphobos/src/std/functional.d
@@ -48,6 +48,9 @@
         $(TD Create a unary or binary function from a string. Most often
         used when defining algorithms on ranges.
     ))
+    $(TR $(TD $(LREF bind))
+        $(TD Passes the fields of a struct as arguments to a function.
+    ))
 ))
 
 Copyright: Copyright Andrei Alexandrescu 2008 - 2009.
diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog
index afeacbc..561c584 100644
--- a/libstdc++-v3/ChangeLog
+++ b/libstdc++-v3/ChangeLog
@@ -1,3 +1,8 @@
+2023-06-23  Jonathan Wakely  <jwakely@redhat.com>
+
+	* include/bits/vector.tcc (_M_realloc_insert): Replace try-block
+	with RAII types.
+
 2023-06-20  Jonathan Wakely  <jwakely@redhat.com>
 
 	* include/std/array (to_array(T(&)[N])): Remove redundant
diff --git a/libstdc++-v3/include/bits/iterator_concepts.h b/libstdc++-v3/include/bits/iterator_concepts.h
index 1555c37..6802582 100644
--- a/libstdc++-v3/include/bits/iterator_concepts.h
+++ b/libstdc++-v3/include/bits/iterator_concepts.h
@@ -771,19 +771,34 @@
       && invocable<_Fn, iter_reference_t<_Is>...>
     using indirect_result_t = invoke_result_t<_Fn, iter_reference_t<_Is>...>;
 
+  namespace __detail
+  {
+    template<typename _Iter, typename _Proj>
+      struct __projected
+      {
+	struct __type
+	{
+	  using value_type = remove_cvref_t<indirect_result_t<_Proj&, _Iter>>;
+	  indirect_result_t<_Proj&, _Iter> operator*() const; // not defined
+	};
+      };
+
+    template<weakly_incrementable _Iter, typename _Proj>
+      struct __projected<_Iter, _Proj>
+      {
+	struct __type
+	{
+	  using value_type = remove_cvref_t<indirect_result_t<_Proj&, _Iter>>;
+	  using difference_type = iter_difference_t<_Iter>;
+	  indirect_result_t<_Proj&, _Iter> operator*() const; // not defined
+	};
+      };
+  } // namespace __detail
+
   /// [projected], projected
   template<indirectly_readable _Iter,
 	   indirectly_regular_unary_invocable<_Iter> _Proj>
-    struct projected
-    {
-      using value_type = remove_cvref_t<indirect_result_t<_Proj&, _Iter>>;
-
-      indirect_result_t<_Proj&, _Iter> operator*() const; // not defined
-    };
-
-  template<weakly_incrementable _Iter, typename _Proj>
-    struct incrementable_traits<projected<_Iter, _Proj>>
-    { using difference_type = iter_difference_t<_Iter>; };
+    using projected = __detail::__projected<_Iter, _Proj>::__type;
 
   // [alg.req], common algorithm requirements
 
diff --git a/libstdc++-v3/include/bits/vector.tcc b/libstdc++-v3/include/bits/vector.tcc
index acd11e2..cda52fb 100644
--- a/libstdc++-v3/include/bits/vector.tcc
+++ b/libstdc++-v3/include/bits/vector.tcc
@@ -458,73 +458,109 @@
     _M_realloc_insert(iterator __position, const _Tp& __x)
 #endif
     {
-      const size_type __len =
-	_M_check_len(size_type(1), "vector::_M_realloc_insert");
+      const size_type __len = _M_check_len(1u, "vector::_M_realloc_insert");
       pointer __old_start = this->_M_impl._M_start;
       pointer __old_finish = this->_M_impl._M_finish;
       const size_type __elems_before = __position - begin();
       pointer __new_start(this->_M_allocate(__len));
       pointer __new_finish(__new_start);
-      __try
+
+      // RAII guard for allocated storage.
+      struct _Guard
+      {
+	pointer _M_storage;	    // Storage to deallocate
+	size_type _M_len;
+	_Tp_alloc_type& _M_alloc;
+
+	_GLIBCXX20_CONSTEXPR
+	_Guard(pointer __s, size_type __l, _Tp_alloc_type& __a)
+	: _M_storage(__s), _M_len(__l), _M_alloc(__a)
+	{ }
+
+	_GLIBCXX20_CONSTEXPR
+	~_Guard()
 	{
-	  // The order of the three operations is dictated by the C++11
-	  // case, where the moves could alter a new element belonging
-	  // to the existing vector.  This is an issue only for callers
-	  // taking the element by lvalue ref (see last bullet of C++11
-	  // [res.on.arguments]).
-	  _Alloc_traits::construct(this->_M_impl,
-				   __new_start + __elems_before,
+	  if (_M_storage)
+	    __gnu_cxx::__alloc_traits<_Tp_alloc_type>::
+	      deallocate(_M_alloc, _M_storage, _M_len);
+	}
+
+      private:
+	_Guard(const _Guard&);
+      };
+      _Guard __guard(__new_start, __len, _M_impl);
+
+      // The order of the three operations is dictated by the C++11
+      // case, where the moves could alter a new element belonging
+      // to the existing vector.  This is an issue only for callers
+      // taking the element by lvalue ref (see last bullet of C++11
+      // [res.on.arguments]).
+
+      // If this throws, the existing elements are unchanged.
 #if __cplusplus >= 201103L
-				   std::forward<_Args>(__args)...);
+      _Alloc_traits::construct(this->_M_impl,
+			       std::__to_address(__new_start + __elems_before),
+			       std::forward<_Args>(__args)...);
 #else
-				   __x);
+      _Alloc_traits::construct(this->_M_impl,
+			       __new_start + __elems_before,
+			       __x);
 #endif
-	  __new_finish = pointer();
 
 #if __cplusplus >= 201103L
-	  if _GLIBCXX17_CONSTEXPR (_S_use_relocate())
-	    {
-	      __new_finish = _S_relocate(__old_start, __position.base(),
-					 __new_start, _M_get_Tp_allocator());
-
-	      ++__new_finish;
-
-	      __new_finish = _S_relocate(__position.base(), __old_finish,
-					 __new_finish, _M_get_Tp_allocator());
-	    }
-	  else
-#endif
-	    {
-	      __new_finish
-		= std::__uninitialized_move_if_noexcept_a
-		(__old_start, __position.base(),
-		 __new_start, _M_get_Tp_allocator());
-
-	      ++__new_finish;
-
-	      __new_finish
-		= std::__uninitialized_move_if_noexcept_a
-		(__position.base(), __old_finish,
-		 __new_finish, _M_get_Tp_allocator());
-	    }
-	}
-      __catch(...)
+      if _GLIBCXX17_CONSTEXPR (_S_use_relocate())
 	{
-	  if (!__new_finish)
-	    _Alloc_traits::destroy(this->_M_impl,
-				   __new_start + __elems_before);
-	  else
-	    std::_Destroy(__new_start, __new_finish, _M_get_Tp_allocator());
-	  _M_deallocate(__new_start, __len);
-	  __throw_exception_again;
+	  // Relocation cannot throw.
+	  __new_finish = _S_relocate(__old_start, __position.base(),
+				     __new_start, _M_get_Tp_allocator());
+	  ++__new_finish;
+	  __new_finish = _S_relocate(__position.base(), __old_finish,
+				     __new_finish, _M_get_Tp_allocator());
 	}
-#if __cplusplus >= 201103L
-      if _GLIBCXX17_CONSTEXPR (!_S_use_relocate())
+      else
 #endif
-	std::_Destroy(__old_start, __old_finish, _M_get_Tp_allocator());
-      _GLIBCXX_ASAN_ANNOTATE_REINIT;
-      _M_deallocate(__old_start,
-		    this->_M_impl._M_end_of_storage - __old_start);
+	{
+	  // RAII type to destroy initialized elements.
+	  struct _Guard_elts
+	  {
+	    pointer _M_first, _M_last;  // Elements to destroy
+	    _Tp_alloc_type& _M_alloc;
+
+	    _GLIBCXX20_CONSTEXPR
+	    _Guard_elts(pointer __elt, _Tp_alloc_type& __a)
+	    : _M_first(__elt), _M_last(__elt + 1), _M_alloc(__a)
+	    { }
+
+	    _GLIBCXX20_CONSTEXPR
+	    ~_Guard_elts()
+	    { std::_Destroy(_M_first, _M_last, _M_alloc); }
+
+	  private:
+	    _Guard_elts(const _Guard_elts&);
+	  };
+
+	  // Guard the new element so it will be destroyed if anything throws.
+	  _Guard_elts __guard_elts(__new_start + __elems_before, _M_impl);
+
+	  __new_finish = std::__uninitialized_move_if_noexcept_a(
+			   __old_start, __position.base(),
+			   __new_start, _M_get_Tp_allocator());
+
+	  ++__new_finish;
+	  // Guard everything before the new element too.
+	  __guard_elts._M_first = __new_start;
+
+	  __new_finish = std::__uninitialized_move_if_noexcept_a(
+			    __position.base(), __old_finish,
+			    __new_finish, _M_get_Tp_allocator());
+
+	  // New storage has been fully initialized, destroy the old elements.
+	  __guard_elts._M_first = __old_start;
+	  __guard_elts._M_last = __old_finish;
+	}
+      __guard._M_storage = __old_start;
+      __guard._M_len = this->_M_impl._M_end_of_storage - __old_start;
+
       this->_M_impl._M_start = __new_start;
       this->_M_impl._M_finish = __new_finish;
       this->_M_impl._M_end_of_storage = __new_start + __len;
diff --git a/libstdc++-v3/include/debug/helper_functions.h b/libstdc++-v3/include/debug/helper_functions.h
index dccf8e9..052b36b 100644
--- a/libstdc++-v3/include/debug/helper_functions.h
+++ b/libstdc++-v3/include/debug/helper_functions.h
@@ -111,12 +111,19 @@
     _GLIBCXX_CONSTEXPR
     inline typename _Distance_traits<_Iterator>::__type
     __get_distance(_Iterator __lhs, _Iterator __rhs)
-    { return __get_distance(__lhs, __rhs, std::__iterator_category(__lhs)); }
+    {
+      return __gnu_debug::__get_distance(__lhs, __rhs,
+					 std::__iterator_category(__lhs));
+    }
 
   // An arbitrary iterator pointer is not singular.
   inline bool
   __check_singular_aux(const void*) { return false; }
 
+  // Defined in <debug/safe_base.h>
+  bool
+  __check_singular_aux(const class _Safe_iterator_base*);
+
   // We may have an iterator that derives from _Safe_iterator_base but isn't
   // a _Safe_iterator.
   template<typename _Iterator>
@@ -125,7 +132,7 @@
     __check_singular(_Iterator const& __x)
     {
       return ! std::__is_constant_evaluated()
-	       && __check_singular_aux(std::__addressof(__x));
+	       && __gnu_debug::__check_singular_aux(std::__addressof(__x));
     }
 
   /** Non-NULL pointers are nonsingular. */
@@ -163,7 +170,8 @@
 		      std::input_iterator_tag)
     {
       return __first == __last
-	|| (!__check_singular(__first) && !__check_singular(__last));
+	|| (!__gnu_debug::__check_singular(__first)
+	      && !__gnu_debug::__check_singular(__last));
     }
 
   template<typename _InputIterator>
@@ -172,8 +180,8 @@
     __valid_range_aux(_InputIterator __first, _InputIterator __last,
 		      std::random_access_iterator_tag)
     {
-      return
-	__valid_range_aux(__first, __last, std::input_iterator_tag())
+      return __gnu_debug::__valid_range_aux(__first, __last,
+					    std::input_iterator_tag())
 	&& __first <= __last;
     }
 
@@ -186,8 +194,8 @@
     __valid_range_aux(_InputIterator __first, _InputIterator __last,
 		      std::__false_type)
     {
-      return __valid_range_aux(__first, __last,
-			       std::__iterator_category(__first));
+      return __gnu_debug::__valid_range_aux(__first, __last,
+					    std::__iterator_category(__first));
     }
 
   template<typename _InputIterator>
@@ -197,10 +205,11 @@
 		      typename _Distance_traits<_InputIterator>::__type& __dist,
 		      std::__false_type)
     {
-      if (!__valid_range_aux(__first, __last, std::input_iterator_tag()))
+      if (!__gnu_debug::__valid_range_aux(__first, __last,
+					  std::input_iterator_tag()))
 	return false;
 
-      __dist = __get_distance(__first, __last);
+      __dist = __gnu_debug::__get_distance(__first, __last);
       switch (__dist.second)
 	{
 	case __dp_none:
@@ -231,7 +240,8 @@
 		  typename _Distance_traits<_InputIterator>::__type& __dist)
     {
       typedef typename std::__is_integer<_InputIterator>::__type _Integral;
-      return __valid_range_aux(__first, __last, __dist, _Integral());
+      return __gnu_debug::__valid_range_aux(__first, __last, __dist,
+					    _Integral());
     }
 
   template<typename _Iterator, typename _Sequence, typename _Category>
@@ -254,7 +264,7 @@
     __valid_range(_InputIterator __first, _InputIterator __last)
     {
       typedef typename std::__is_integer<_InputIterator>::__type _Integral;
-      return __valid_range_aux(__first, __last, _Integral());
+      return __gnu_debug::__valid_range_aux(__first, __last, _Integral());
     }
 
   template<typename _Iterator, typename _Sequence, typename _Category>
diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format
index 96a1e62..9d5981e 100644
--- a/libstdc++-v3/include/std/format
+++ b/libstdc++-v3/include/std/format
@@ -269,39 +269,26 @@
       if (__first == __last)
 	__builtin_unreachable();
 
-      // TODO: use this loop unconditionally?
-      // Most integers used for arg-id, width or precision will be small.
-      if (is_constant_evaluated())
-	{
-	  auto __next = __first;
-	  unsigned short __val = 0;
-	  while (__next != __last && '0' <= *__next && *__next <= '9')
-	    {
-	      __val = (__val * 10) + (*__next - '0'); // TODO check overflow?
-	      ++__next;
-	    }
-	  if (__next == __first)
-	    return {0, nullptr};
-	  return {__val, __next};
-	}
-
-      unsigned short __val = 0;
       if constexpr (is_same_v<_CharT, char>)
 	{
-	  auto [ptr, ec] = std::from_chars(__first, __last, __val);
-	  if (ec == errc{})
-	    return {__val, ptr};
-	  return {0, nullptr};
+	  const auto __start = __first;
+	  unsigned short __val = 0;
+	  // N.B. std::from_chars is not constexpr in C++20.
+	  if (__detail::__from_chars_alnum<true>(__first, __last, __val, 10)
+		&& __first != __start) [[likely]]
+	    return {__val, __first};
 	}
       else
 	{
+	  unsigned short __val = 0;
 	  constexpr int __n = 32;
 	  char __buf[__n]{};
-	  for (int __i = 0; __i < __n && __first != __last; ++__i)
+	  for (int __i = 0; __i < __n && (__first + __i) != __last; ++__i)
 	    __buf[__i] = __first[__i];
 	  auto [__v, __ptr] = __format::__parse_integer(__buf, __buf + __n);
 	  return {__v, __first + (__ptr - __buf)};
 	}
+      return {0, nullptr};
     }
 
   template<typename _CharT>
@@ -2118,7 +2105,7 @@
 	typename basic_format_context<_Out, _CharT>::iterator
 	format(const void* __v, basic_format_context<_Out, _CharT>& __fc) const
 	{
-	  auto __u = reinterpret_cast<__UINT64_TYPE__>(__v);
+	  auto __u = reinterpret_cast<__UINTPTR_TYPE__>(__v);
 	  char __buf[2 + sizeof(__v) * 2];
 	  auto [__ptr, __ec] = std::to_chars(__buf + 2, std::end(__buf),
 					     __u, 16);
diff --git a/libstdc++-v3/testsuite/24_iterators/indirect_callable/projected-adl.cc b/libstdc++-v3/testsuite/24_iterators/indirect_callable/projected-adl.cc
new file mode 100644
index 0000000..4c2a095
--- /dev/null
+++ b/libstdc++-v3/testsuite/24_iterators/indirect_callable/projected-adl.cc
@@ -0,0 +1,42 @@
+// { dg-options "-std=gnu++20" }
+// { dg-do compile { target c++20 } }
+
+// P2538R1 ADL-proof std::projected
+// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2538r1.html
+
+#include <iterator>
+
+template<typename T>
+  concept has_diff_type = requires { typename T::difference_type; };
+
+static_assert( has_diff_type<std::projected<int*, void(*)(int)>> );
+
+struct Indy {
+  using value_type = int;
+  int operator*() const { return 0; }
+};
+static_assert( ! std::weakly_incrementable<Indy> );
+static_assert( ! has_diff_type<std::projected<Indy, void(*)(int)>> );
+
+
+// Examples from the paper:
+
+template<class T> struct Holder { T t; };
+struct Incomplete;
+
+void test_concepts()
+{
+  using T = Holder<Incomplete>*;
+  static_assert(std::equality_comparable<T>);
+  (void) std::indirectly_comparable<T*, T*, std::equal_to<>>;
+  (void) std::sortable<T*>;
+}
+
+#include <algorithm>
+
+void test_count()
+{
+  Holder<Incomplete>* a = nullptr;
+  (void) std::count(&a, &a, nullptr);
+  (void) std::ranges::count(&a, &a, nullptr); // { dg-bogus "." }
+}
diff --git a/libstdc++-v3/testsuite/std/format/string.cc b/libstdc++-v3/testsuite/std/format/string.cc
index e421028..d28135e 100644
--- a/libstdc++-v3/testsuite/std/format/string.cc
+++ b/libstdc++-v3/testsuite/std/format/string.cc
@@ -121,6 +121,11 @@
   // Invalid presentation types for strings.
   VERIFY( ! is_format_string_for("{:S}", "str") );
   VERIFY( ! is_format_string_for("{:d}", "str") );
+
+  // Maximum integer value supported for widths and precisions is USHRT_MAX.
+  VERIFY( is_format_string_for("{:65535}", 1) );
+  VERIFY( ! is_format_string_for("{:65536}", 1) );
+  VERIFY( ! is_format_string_for("{:9999999}", 1) );
 }
 
 int main()
diff --git a/lto-plugin/ChangeLog b/lto-plugin/ChangeLog
index 8a95d1a..a40f6f6 100644
--- a/lto-plugin/ChangeLog
+++ b/lto-plugin/ChangeLog
@@ -1,3 +1,9 @@
+2023-06-22  Marek Polacek  <polacek@redhat.com>
+
+	* configure.ac (--enable-host-bind-now): New check.  Link with
+	-z,now.
+	* configure: Regenerate.
+
 2023-05-11  Joseph Myers  <joseph@codesourcery.com>
 
 	PR middle-end/109128
diff --git a/lto-plugin/configure b/lto-plugin/configure
index d522bd2..675b9cf 100755
--- a/lto-plugin/configure
+++ b/lto-plugin/configure
@@ -663,6 +663,7 @@
 gcc_build_dir
 CET_HOST_FLAGS
 ac_lto_plugin_ldflags
+enable_host_bind_now
 ac_lto_plugin_warn_cflags
 EGREP
 GREP
@@ -778,6 +779,7 @@
 with_libiberty
 enable_dependency_tracking
 enable_largefile
+enable_host_bind_now
 enable_cet
 with_gcc_major_version_only
 enable_shared
@@ -1425,6 +1427,7 @@
   --disable-dependency-tracking
                           speeds up one-time build
   --disable-largefile     omit support for large files
+  --enable-host-bind-now  link host code as BIND_NOW
   --enable-cet            enable Intel CET in host libraries [default=auto]
   --enable-shared[=PKGS]  build shared libraries [default=yes]
   --enable-static[=PKGS]  build static libraries [default=yes]
@@ -5669,6 +5672,19 @@
    ac_lto_plugin_ldflags="-Wc,-static-libgcc"
 fi
 
+# Enable --enable-host-bind-now
+# Check whether --enable-host-bind-now was given.
+if test "${enable_host_bind_now+set}" = set; then :
+  enableval=$enable_host_bind_now;
+fi
+
+
+
+if test x$enable_host_bind_now = xyes; then
+  ac_lto_plugin_ldflags="$ac_lto_plugin_ldflags -Wl,-z,now"
+fi
+
+
 
  # Check whether --enable-cet was given.
 if test "${enable_cet+set}" = set; then :
@@ -12134,7 +12150,7 @@
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12137 "configure"
+#line 12153 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -12240,7 +12256,7 @@
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12243 "configure"
+#line 12259 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
diff --git a/lto-plugin/configure.ac b/lto-plugin/configure.ac
index 0a72027..84f2a60 100644
--- a/lto-plugin/configure.ac
+++ b/lto-plugin/configure.ac
@@ -25,6 +25,17 @@
 if test "x$have_static_libgcc" = xyes; then
    ac_lto_plugin_ldflags="-Wc,-static-libgcc"
 fi
+
+# Enable --enable-host-bind-now
+AC_ARG_ENABLE(host-bind-now,
+[AS_HELP_STRING([--enable-host-bind-now],
+       [link host code as BIND_NOW])])
+AC_SUBST(enable_host_bind_now)
+
+if test x$enable_host_bind_now = xyes; then
+  ac_lto_plugin_ldflags="$ac_lto_plugin_ldflags -Wl,-z,now"
+fi
+
 AC_SUBST(ac_lto_plugin_ldflags)
 
 GCC_CET_HOST_FLAGS(CET_HOST_FLAGS)