https://gcc.gnu.org/g:9964edfb4abdec25f8be48e667afcae30ce03a37

commit r15-2039-g9964edfb4abdec25f8be48e667afcae30ce03a37
Author: Jakub Jelinek <ja...@redhat.com>
Date:   Mon Jul 15 09:48:38 2024 +0200

    varasm: Add support for emitting binary data with the new gas .base64 
directive
    
    Nick has implemented a new .base64 directive in gas (to be shipped in
    the upcoming binutils 2.43; big thanks for that).
    See https://sourceware.org/bugzilla/show_bug.cgi?id=31964
    
    The following patch adjusts default_elf_asm_output_ascii (i.e.
    ASM_OUTPUT_ASCII elfos.h implementation) to use it if it detects binary
    data and gas supports it.
    
    Without this patch, we emit stuff like:
            .string "\177ELF\002\001\001\003"
            .string ""
            .string ""
            .string ""
            .string ""
            .string ""
            .string ""
            .string ""
            .string "\002"
            .string ">"
    ...
            .string "\324\001\236 
0FS\202\002E\n0@\203\004\005&\202\021\337)\021\203C\020A\300\220I\004\t\b\206(\234\0132l\004b\300\bK\006\220$0\303\020P$\233\211\002D\f"
    etc., with this patch more compact
            .base64 
"f0VMRgIBAQMAAAAAAAAAAAIAPgABAAAAABf3AAAAAABAAAAAAAAAAACneB0AAAAAAAAAAEAAOAAOAEAALAArAAYAAAAEAAAAQAAAAAAAAABAAEAAAAAAAEAAQAAAAAAAEAMAAAAAAAAQAwAAAAAAAAgAAAAAAAAAAwAAAAQAAABQAwAAAAAAAFADQAAAAAAAUANAAAAAAAAcAAAAAAAAABwAAAAAAAAAAQAAAAAAAAABAAAABAAAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAAAADBwOQAAAAAAMHA5AAAAAAAAEAAAAAAAAAEAAAAFAAAAAIA5AAAAAAAAgHkAAAAA"
            .base64 
"AACAeQAAAAAAxSSgAgAAAADFJKACAAAAAAAQAAAAAAAAAQAAAAQAAAAAsNkCAAAAAACwGQMAAAAAALAZAwAAAADMtc0AAAAAAMy1zQAAAAAAABAAAAAAAAABAAAABgAAAGhmpwMAAAAAaHbnAwAAAABoducDAAAAAOAMAQAAAAAA4MEeAAAAAAAAEAAAAAAAAAIAAAAGAAAAkH2nAwAAAACQjecDAAAAAJCN5wMAAAAAQAIAAAAAAABAAgAAAAAAAAgAAAAAAAAABAAAAAQAAABwAwAAAAAAAHADQAAAAAAAcANAAAAAAABAAAAAAAAAAEAAAAAAAAAACAAAAAAA"
            .base64 
"AAAEAAAABAAAALADAAAAAAAAsANAAAAAAACwA0AAAAAAACAAAAAAAAAAIAAAAAAAAAAEAAAAAAAAAAcAAAAEAAAAaGanAwAAAABoducDAAAAAGh25wMAAAAAAAAAAAAAAAAQAAAAAAAAAAgAAAAAAAAAU+V0ZAQAAABwAwAAAAAAAHADQAAAAAAAcANAAAAAAABAAAAAAAAAAEAAAAAAAAAACAAAAAAAAABQ5XRkBAAAAAw/WAMAAAAADD+YAwAAAAAMP5gDAAAAAPy7CgAAAAAA/LsKAAAAAAAEAAAAAAAAAFHldGQGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
            .base64 
"AAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAUuV0ZAQAAABoZqcDAAAAAGh25wMAAAAAaHbnAwAAAACYGQAAAAAAAJgZAAAAAAAAAQAAAAAAAAAvbGliNjQvbGQtbGludXgteDg2LTY0LnNvLjIAAAAAAAQAAAAwAAAABQAAAEdOVQACgADABAAAAAEAAAAAAAAAAQABwAQAAAAJAAAAAAAAAAIAAcAEAAAAAwAAAAAAAAAEAAAAEAAAAAEAAABHTlUAAAAAAAMAAAACAAAAAAAAAAOAAACsqAAAgS0AAOJWAAAjNwAAXjAAAAAAAAAAAAAAF1gAAHsxAABBBwAA"
            .base64 
"G0kAALGmAACwoAAAAAAAAAAAAACQhAAAAAAAAOw1AACNYgAAAAAAAFQoAAAAAAAAx3UAALZAAAAAAAAAiIUAALGeAABBlAAAWEsAAPmRAACmOgAAAAAAADh3AAAAAAAAlCAAAAAAAABymgAAaosAAMIjAAAKMQAAMkIAADU0AAAAAAAA5ZwAAAAAAAAAAAAAAAAAAFIdAAAIGQAAAAAAAMFbAAAoTQAAGDcAAIRgAAA6HgAAlxwAAAAAAADOlgAAAAAAAEhPAAARiwAAMGgAAOVtAADMFgAAAAAAAAAAAACrjgAAYl4AACZVAAA/HgAAAAAAAAAAAABqPwAAAAAA"
    The patch attempts to juggle between readability and compactness, so
    if it detects some hunk of the initializer that would be shorter to be
    emitted as .string/.ascii directive, it does so, but if it previously
    used .base64 directive it switches mode only if there is a 16+ char
    ASCII-ish string.
    
    On my #embed testcase from yesterday
    unsigned char a[] = {
     #embed "cc1plus"
    };
    without this patch it emits 2.4GB of assembly, while with this
    patch 649M.
    Compile times (trunk, so yes,rtl,extra checking) are:
    time ./xgcc -B ./ -S -std=c23 -O2 embed-11.c
    
    real    0m13.647s
    user    0m7.157s
    sys     0m2.597s
    time ./xgcc -B ./ -c -std=c23 -O2 embed-11.c
    
    real    0m28.649s
    user    0m26.653s
    sys     0m1.958s
    without the patch and
    time ./xgcc -B ./ -S -std=c23 -O2 embed-11.c
    
    real    0m4.283s
    user    0m2.288s
    sys     0m0.859s
    time ./xgcc -B ./ -c -std=c23 -O2 embed-11.c
    
    real    0m6.888s
    user    0m5.876s
    sys     0m1.002s
    with the patch, so that feels like significant improvement.
    The resulting embed-11.o is identical between the two ways of expressing
    the mostly binary data in the assembly.  But note that there are portions
    like:
            .base64 
"nAAAAAAAAAAvZRcAIgAOAFAzMwEAAAAABgAAAAAAAACEQBgAEgAOAFBHcAIAAAAA7AAAAAAAAAAAX19nbXB6X2dldF9zaQBtcGZyX3NldF9zaV8yZXhwAG1wZnJfY29zaABtcGZyX3RhbmgAbXBmcl9zZXRfbmFuAG1wZnJfc3ViAG1wZnJfdGFuAG1wZnJfc3RydG9mcgBfX2dtcHpfc3ViX3VpAF9fZ21wX2dldF9tZW1vcnlfZnVuY3Rpb25zAF9fZ21wel9zZXRfdWkAbXBmcl9wb3cAX19nbXB6X3N1YgBfX2dtcHpfZml0c19zbG9uZ19wAG1wZnJfYXRh"
            .base64 
"bjIAX19nbXB6X2RpdmV4YWN0AG1wZnJfc2V0X2VtaW4AX19nbXB6X3NldABfX2dtcHpfbXVsAG1wZnJfY2xlYXIAbXBmcl9sb2cAbXBmcl9hdGFuaABfX2dtcHpfc3dhcABtcGZyX2FzaW5oAG1wZnJfYXNpbgBtcGZyX2NsZWFycwBfX2dtcHpfbXVsXzJleHAAX19nbXB6X2FkZG11bABtcGZyX3NpbmgAX19nbXB6X2FkZF91aQBfX2dtcHFfY2xlYXIAX19nbW9uX3N0YXJ0X18AbXBmcl9hY29zAG1wZnJfc2V0X2VtYXgAbXBmcl9jb3MAbXBmcl9zaW4A"
            .string "__gmpz_ui_pow_ui"
            .string "mpfr_get_str"
            .string "mpfr_acosh"
            .string "mpfr_sub_ui"
            .string "__gmpq_set_ui"
            .string "mpfr_set_inf"
    ...
            .string "GLIBC_2.14"
            .string "GLIBC_2.11"
            .base64 
"AAABAAIAAQADAAMAAwADAAMAAwAEAAUABgADAAEAAQADAAMABwABAAEAAwADAAMAAwAIAAEAAwADAAEAAwABAAMAAwABAAMAAQADAAMAAwADAAMAAwADAAYAAwADAAEAAQAIAAMAAwADAAMAAwABAAMAAQADAAMAAQABAAEAAwAIAAEAAwADAAEAAwABAAMAAQADAAEABgADAAMAAQAHAAMAAwADAAMAAwABAAMAAQABAAMAAwADAAkAAQABAAEAAwAKAAEAAwADAAMAAQABAAMAAwALAAEAAwADAAEAAQADAAMAAwABAAMAAwABAAEAAwADAAMABwABAAMAAwAB"
            .base64 
"AAEAAwADAAEAAwABAAMAAQADAAMAAwADAAEAAQABAAEAAwADAAMAAQABAAEAAQABAAEAAQADAAMAAwADAAMAAQABAAwAAwADAA0AAwADAAMAAwADAAEAAQADAAMAAQABAAMAAwADAAEAAwADAAEAAwAIAAMAAwADAAMABgABAA4ACwAGAAEAAQADAAEAAQADAAEAAwABAAMAAwABAAEAAwABAAMAAwABAAEAAwADAAMAAwABAAMAAQABAAEAAQABAAMADwABAAMAAQADAAMAAwABAAEAAQAIAAEADAADAAMAAQABAAMAAwADAAEAAQABAAEAAQADAAEAAwADAAEA"
            .base64 
"AwABAAMAAQADAAMAAQABAAEAAwADAAMAAwADAAMAAQADAAMACAAQAA8AAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQA="
    so it isn't all just totally unreadable stuff.
    
    2024-07-15  Jakub Jelinek  <ja...@redhat.com>
    
            * configure.ac (HAVE_GAS_BASE64): New check.
            * config/elfos.h (BASE64_ASM_OP): Define if HAVE_GAS_BASE64 is
            defined.
            * varasm.cc (assemble_string): Bump maximum from 2000 to 16384 if
            BASE64_ASM_OP is defined.
            (default_elf_asm_output_limited_string): Emit opening '"' together
            with STRING_ASM_OP.
            (default_elf_asm_output_ascii): Use BASE64_ASM_OP if defined and
            beneficial.  Remove UB when last_null is NULL.
            * configure: Regenerate.
            * config.in: Regenerate.

Diff:
---
 gcc/config.in      |   6 +++
 gcc/config/elfos.h |   4 ++
 gcc/configure      |  33 +++++++++++++++
 gcc/configure.ac   |   5 +++
 gcc/varasm.cc      | 117 +++++++++++++++++++++++++++++++++++++++++++++++++++--
 5 files changed, 162 insertions(+), 3 deletions(-)

diff --git a/gcc/config.in b/gcc/config.in
index ae41596596fa..bc819005bd62 100644
--- a/gcc/config.in
+++ b/gcc/config.in
@@ -1437,6 +1437,12 @@
 #endif
 
 
+/* Define if your assembler supports .base64. */
+#ifndef USED_FOR_TARGET
+#undef HAVE_GAS_BASE64
+#endif
+
+
 /* Define 0/1 if your assembler supports CFI directives. */
 #undef HAVE_GAS_CFI_DIRECTIVE
 
diff --git a/gcc/config/elfos.h b/gcc/config/elfos.h
index da45593fb305..133881509df2 100644
--- a/gcc/config/elfos.h
+++ b/gcc/config/elfos.h
@@ -444,6 +444,10 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  
If not, see
 
 #define STRING_ASM_OP  "\t.string\t"
 
+#ifdef HAVE_GAS_BASE64
+#define BASE64_ASM_OP  "\t.base64\t"
+#endif
+
 /* The routine used to output NUL terminated strings.  We use a special
    version of this for most svr4 targets because doing so makes the
    generated assembly code more compact (and thus faster to assemble)
diff --git a/gcc/configure b/gcc/configure
index 1335db2d4d2e..4faae0fa5fb8 100755
--- a/gcc/configure
+++ b/gcc/configure
@@ -25859,6 +25859,39 @@ case "${target}" in
     ;;
 esac
 
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking assembler for .base64" >&5
+$as_echo_n "checking assembler for .base64... " >&6; }
+if ${gcc_cv_as_base64+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  gcc_cv_as_base64=no
+  if test x$gcc_cv_as != x; then
+    $as_echo ' .section .rodata
+       .base64 
"Tm9uIHB1ZG9yIGVzdCBuaWwgc2NpcmUsIHB1ZG9yIG5pbCBkaXNjZXJlIHZlbGxlLgo="' > 
conftest.s
+    if { ac_try='$gcc_cv_as $gcc_cv_as_flags  -o conftest.o conftest.s >&5'
+  { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }
+    then
+       gcc_cv_as_base64=yes
+    else
+      echo "configure: failed program was" >&5
+      cat conftest.s >&5
+    fi
+    rm -f conftest.o conftest.s
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gcc_cv_as_base64" >&5
+$as_echo "$gcc_cv_as_base64" >&6; }
+if test $gcc_cv_as_base64 = yes; then
+
+$as_echo "#define HAVE_GAS_BASE64 1" >>confdefs.h
+
+fi
+
+
 # gnu_indirect_function type is an extension proposed at
 # http://groups.google/com/group/generic-abi/files. It allows dynamic runtime
 # selection of function implementation
diff --git a/gcc/configure.ac b/gcc/configure.ac
index eb72f65865ab..3da1eaa70646 100644
--- a/gcc/configure.ac
+++ b/gcc/configure.ac
@@ -3054,6 +3054,11 @@ case "${target}" in
     ;;
 esac
 
+gcc_GAS_CHECK_FEATURE([.base64], gcc_cv_as_base64,,
+ [     .section .rodata
+       .base64 
"Tm9uIHB1ZG9yIGVzdCBuaWwgc2NpcmUsIHB1ZG9yIG5pbCBkaXNjZXJlIHZlbGxlLgo="],,
+[AC_DEFINE(HAVE_GAS_BASE64, 1, [Define if your assembler supports .base64.])])
+
 # gnu_indirect_function type is an extension proposed at
 # http://groups.google/com/group/generic-abi/files. It allows dynamic runtime
 # selection of function implementation
diff --git a/gcc/varasm.cc b/gcc/varasm.cc
index a6ef099c3641..fe0a6b873ef4 100644
--- a/gcc/varasm.cc
+++ b/gcc/varasm.cc
@@ -2101,7 +2101,19 @@ void
 assemble_string (const char *p, int size)
 {
   int pos = 0;
+#if defined(BASE64_ASM_OP) \
+    && BITS_PER_UNIT == 8 \
+    && CHAR_BIT == 8 \
+    && 'A' == 65 \
+    && 'a' == 97 \
+    && '0' == 48 \
+    && '+' == 43 \
+    && '/' == 47 \
+    && '=' == 61
+  int maximum = 16384;
+#else
   int maximum = 2000;
+#endif
 
   /* If the string is very long, split it up.  */
 
@@ -8454,8 +8466,7 @@ default_elf_asm_output_limited_string (FILE *f, const 
char *s)
   int escape;
   unsigned char c;
 
-  fputs (STRING_ASM_OP, f);
-  putc ('"', f);
+  fputs (STRING_ASM_OP "\"", f);
   while (*s != '\0')
     {
       c = *s;
@@ -8489,9 +8500,11 @@ default_elf_asm_output_ascii (FILE *f, const char *s, 
unsigned int len)
 {
   const char *limit = s + len;
   const char *last_null = NULL;
+  const char *last_base64 = s;
   unsigned bytes_in_chunk = 0;
   unsigned char c;
   int escape;
+  bool prev_base64 = false;
 
   for (; s < limit; s++)
     {
@@ -8504,7 +8517,7 @@ default_elf_asm_output_ascii (FILE *f, const char *s, 
unsigned int len)
          bytes_in_chunk = 0;
        }
 
-      if (s > last_null)
+      if ((uintptr_t) s > (uintptr_t) last_null)
        {
          for (p = s; p < limit && *p != '\0'; p++)
            continue;
@@ -8513,6 +8526,104 @@ default_elf_asm_output_ascii (FILE *f, const char *s, 
unsigned int len)
       else
        p = last_null;
 
+#if defined(BASE64_ASM_OP) \
+    && BITS_PER_UNIT == 8 \
+    && CHAR_BIT == 8 \
+    && 'A' == 65 \
+    && 'a' == 97 \
+    && '0' == 48 \
+    && '+' == 43 \
+    && '/' == 47 \
+    && '=' == 61
+      if (s >= last_base64)
+       {
+         unsigned cnt = 0;
+         const char *t;
+         for (t = s; t < limit && (t - s) < (long) ELF_STRING_LIMIT - 1; t++)
+           {
+             if (t == p && t != s)
+               {
+                 if (cnt <= (t - s + 1 + 2) / 3 * 4
+                     && (!prev_base64 || (t - s) >= 16)
+                     && ((t - s) > 1 || cnt <= 2))
+                   {
+                     last_base64 = p;
+                     goto no_base64;
+                   }
+               }
+             c = *t;
+             escape = ELF_ASCII_ESCAPES[c];
+             switch (escape)
+               {
+               case 0:
+                 ++cnt;
+                 break;
+               case 1:
+                 if (c == 0)
+                   cnt += 2 + strlen (STRING_ASM_OP) + 1;
+                 else
+                   cnt += 4;
+                 break;
+               default:
+                 cnt += 2;
+                 break;
+               }
+           }
+         if (cnt > (t - s + 2) / 3 * 4 && (t - s) >= 3)
+           {
+             if (bytes_in_chunk > 0)
+               {
+                 putc ('\"', f);
+                 putc ('\n', f);
+                 bytes_in_chunk = 0;
+               }
+
+             unsigned char buf[(ELF_STRING_LIMIT + 2) / 3 * 4 + 3];
+             unsigned j = 0;
+             static const char base64_enc[] =
+               "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+               "0123456789+/";
+
+             fputs (BASE64_ASM_OP "\"", f);
+             while (s < t)
+               {
+                 unsigned char a = *s;
+                 unsigned char b = 0, c = 0;
+                 if (s < t - 1)
+                   b = s[1];
+                 if (s < t - 2)
+                   c = s[2];
+                 unsigned long v = ((((unsigned long) a) << 16)
+                                    | (((unsigned long) b) << 8)
+                                    | c);
+                 buf[j++] = base64_enc[(v >> 18) & 63];
+                 buf[j++] = base64_enc[(v >> 12) & 63];
+                 buf[j++] = base64_enc[(v >> 6) & 63];
+                 buf[j++] = base64_enc[v & 63];
+                 if (s >= t - 2)
+                   {
+                     buf[j - 1] = '=';
+                     if (s >= t - 1)
+                       buf[j - 2] = '=';
+                     break;
+                   }
+                 s += 3;
+               }
+             memcpy (buf + j, "\"\n", 3);
+             fputs ((const char *) buf, f);
+             s = t - 1;
+             prev_base64 = true;
+             continue;
+           }
+         last_base64 = t;
+       no_base64:
+         prev_base64 = false;
+       }
+#else
+      (void) last_base64;
+      (void) prev_base64;
+#endif
+
       if (p < limit && (p - s) <= (long) ELF_STRING_LIMIT)
        {
          if (bytes_in_chunk > 0)

Reply via email to