Hello again,
most of this patch was posted at the beginning of my GSOC adventure and
primarily is about replacing fprintf() calls with custom faster ones. From
that time I changed minor things you suggested, omitted some complexities
that offered minor speed-up, and made the code as clear as possible, while
following more closely coding guidelines.
Bootstrapped and tested on both i386 and x86_64, showing runtime
improvement when compiling with debug info enabled.
The only thing I am not sure is the differentiation between HOST_WIDE_INT
and long, so I provided two versions of the same function. Please let me
know if I should handle this differently.
Future speedup in assembly generation *is possible* but requires more
intrusive changes, maybe making assembly hard to read by human:
* Always use hex numbers, they are much faster to produce
* Call GNU assembler with -s parameter, though it's pretty hard to be
compliant.
* Even further in the future we could generate binary data, if we *know*
the assembler is GAS.
Changelog:
2011-08-12 Dimitrios Apostolou <ji...@gmx.net>
* final.c, output.h (fprint_whex, fprint_w, fprint_ul, sprint_ul):
New functions serving as fast replacements for fprintf() integer
to string conversions. They were used in the following changes.
* final.c (sprint_ul_rev): Internal helper for the above.
(output_addr_const): case CONST_INT: don't use fprintf().
* elfos.h (ASM_GENERATE_INTERNAL_LABEL): Don't use sprintf("%u"),
use sprint_ul() and custom loop which are much faster.
(TARGET_ASM_INTERNAL_LABEL): Define as default_elf_internal_label.
(ELF_ASCII_ESCAPES, ELF_STRING_LIMIT): Are the old ESCAPES and
STRING_LIMIT macros.
(ASM_OUTPUT_LIMITED_STRING, ASM_OUTPUT_ASCII): Macros now just
call respective functions that provide now the same
functionality. Those are default_elf_asm_output_limited_string()
and default_elf_asm_output_ascii() in varasm.c.
* varasm.c: Fixed some whitespace inconsistencies.
(default_elf_asm_output_limited_string)
(default_elf_asm_output_ascii): The above macros from elfos.h are
implemented here, avoiding superfluous calls to fprintf().
(default_elf_internal_label): Hook for
targetm.asm_out.internal_label and ASM_OUTPUT_DEBUG_LABEL.
* i386.c: Don't call fprintf("%u") but fprint_ul() instead.
* defaults.h (ASM_OUTPUT_LABEL, ASM_OUTPUT_INTERNAL_LABEL):
Expanded the macros on multiple lines for readability.
(ASM_OUTPUT_LABELREF): Have two calls to fputs() instead of one to
asm_fprintf().
* dwarf2asm.c (dw2_assemble_integer, dw2_asm_output_data)
(dw2_asm_data_uleb128, dw2_asm_delta_uleb128)
(dw2_asm_delta_sleb128): Convert fprintf() calls to faster,
simpler functions.
* dwarf2out.c (dwarf2out_source_line): Convert fprintf() calls to
faster, simpler functions.
Thanks,
Dimitris
=== modified file 'gcc/config/elfos.h'
--- gcc/config/elfos.h 2010-11-21 00:54:14 +0000
+++ gcc/config/elfos.h 2011-08-22 11:39:43 +0000
@@ -117,10 +117,23 @@ see the files COPYING3 and COPYING.RUNTI
#define ASM_GENERATE_INTERNAL_LABEL(LABEL, PREFIX, NUM) \
do \
{ \
- sprintf (LABEL, "*.%s%u", PREFIX, (unsigned) (NUM)); \
+ int __i = 2; \
+ int __j = 0; \
+ (LABEL)[0] = '*'; \
+ (LABEL)[1] = '.'; \
+ do \
+ { \
+ (LABEL)[__i] = (PREFIX)[__j]; \
+ __i++; __j++; \
+ } \
+ while ((PREFIX)[__j] != '\0'); \
+ sprint_ul (&(LABEL)[__i], (unsigned long) (NUM)); \
} \
while (0)
+#undef TARGET_ASM_INTERNAL_LABEL
+#define TARGET_ASM_INTERNAL_LABEL default_elf_internal_label
+
/* Output the label which precedes a jumptable. Note that for all svr4
systems where we actually generate jumptables (which is to say every
svr4 target except i386, where we use casesi instead) we put the jump-
@@ -371,7 +384,7 @@ see the files COPYING3 and COPYING.RUNTI
the i386) don't know about that. Also, we don't use \v
since some versions of gas, such as 2.2 did not accept it. */
-#define ESCAPES \
+#define ELF_ASCII_ESCAPES \
"\1\1\1\1\1\1\1\1btn\1fr\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\
\0\0\"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\\\0\0\0\
@@ -393,9 +406,7 @@ see the files COPYING3 and COPYING.RUNTI
should define this to zero.
*/
-#define STRING_LIMIT ((unsigned) 256)
-
-#define STRING_ASM_OP "\t.string\t"
+#define ELF_STRING_LIMIT ((unsigned) 256)
/* The routine used to output NUL terminated strings. We use a special
version of this for most svr4 targets because doing so makes the
@@ -405,36 +416,7 @@ see the files COPYING3 and COPYING.RUNTI
comma separated lists of numbers). */
#define ASM_OUTPUT_LIMITED_STRING(FILE, STR) \
- do \
- { \
- register const unsigned char *_limited_str = \
- (const unsigned char *) (STR); \
- register unsigned ch; \
- \
- fprintf ((FILE), "%s\"", STRING_ASM_OP); \
- \
- for (; (ch = *_limited_str); _limited_str++) \
- { \
- register int escape; \
- \
- switch (escape = ESCAPES[ch]) \
- { \
- case 0: \
- putc (ch, (FILE)); \
- break; \
- case 1: \
- fprintf ((FILE), "\\%03o", ch); \
- break; \
- default: \
- putc ('\\', (FILE)); \
- putc (escape, (FILE)); \
- break; \
- } \
- } \
- \
- fprintf ((FILE), "\"\n"); \
- } \
- while (0)
+ default_elf_asm_output_limited_string ((FILE), (STR))
/* The routine used to output sequences of byte values. We use a special
version of this for most svr4 targets because doing so makes the
@@ -444,76 +426,8 @@ see the files COPYING3 and COPYING.RUNTI
STRING_LIMIT) we output those using ASM_OUTPUT_LIMITED_STRING. */
#undef ASM_OUTPUT_ASCII
-#define ASM_OUTPUT_ASCII(FILE, STR, LENGTH) \
- do \
- { \
- const unsigned char *_ascii_bytes = \
- (const unsigned char *) (STR); \
- const unsigned char *limit = _ascii_bytes + (LENGTH); \
- const unsigned char *last_null = NULL; \
- unsigned bytes_in_chunk = 0; \
- \
- for (; _ascii_bytes < limit; _ascii_bytes++) \
- { \
- const unsigned char *p; \
- \
- if (bytes_in_chunk >= 60) \
- { \
- fprintf ((FILE), "\"\n"); \
- bytes_in_chunk = 0; \
- } \
- \
- if (_ascii_bytes > last_null) \
- { \
- for (p = _ascii_bytes; p < limit && *p != '\0'; p++) \
- continue; \
- last_null = p; \
- } \
- else \
- p = last_null; \
- \
- if (p < limit && (p - _ascii_bytes) <= (long)STRING_LIMIT) \
- { \
- if (bytes_in_chunk > 0) \
- { \
- fprintf ((FILE), "\"\n"); \
- bytes_in_chunk = 0; \
- } \
- \
- ASM_OUTPUT_LIMITED_STRING ((FILE), _ascii_bytes); \
- _ascii_bytes = p; \
- } \
- else \
- { \
- register int escape; \
- register unsigned ch; \
- \
- if (bytes_in_chunk == 0) \
- fprintf ((FILE), "%s\"", ASCII_DATA_ASM_OP); \
- \
- switch (escape = ESCAPES[ch = *_ascii_bytes]) \
- { \
- case 0: \
- putc (ch, (FILE)); \
- bytes_in_chunk++; \
- break; \
- case 1: \
- fprintf ((FILE), "\\%03o", ch); \
- bytes_in_chunk += 4; \
- break; \
- default: \
- putc ('\\', (FILE)); \
- putc (escape, (FILE)); \
- bytes_in_chunk += 2; \
- break; \
- } \
- } \
- } \
- \
- if (bytes_in_chunk > 0) \
- fprintf ((FILE), "\"\n"); \
- } \
- while (0)
+#define ASM_OUTPUT_ASCII(FILE, STR, LENGTH) \
+ default_elf_asm_output_ascii ((FILE), (STR), (LENGTH));
/* Allow the use of the -frecord-gcc-switches switch via the
elf_record_gcc_switches function defined in varasm.c. */
@@ -526,6 +440,6 @@ see the files COPYING3 and COPYING.RUNTI
It is needed to properly support non-default visibility. */
#ifndef ASM_OUTPUT_EXTERNAL
-#define ASM_OUTPUT_EXTERNAL(FILE, DECL, NAME) \
+#define ASM_OUTPUT_EXTERNAL(FILE, DECL, NAME) \
default_elf_asm_output_external (FILE, DECL, NAME)
#endif
=== modified file 'gcc/config/i386/i386.c'
--- gcc/config/i386/i386.c 2011-06-02 17:43:22 +0000
+++ gcc/config/i386/i386.c 2011-08-22 11:39:43 +0000
@@ -13762,26 +13762,28 @@ print_reg (rtx x, int code, FILE *file)
code = GET_MODE_SIZE (GET_MODE (x));
/* Irritatingly, AMD extended registers use different naming convention
- from the normal registers. */
+ from the normal registers: "r%d[bwd]" */
if (REX_INT_REG_P (x))
{
gcc_assert (TARGET_64BIT);
+ putc ('r', file);
+ fprint_ul (file, REGNO (x) - FIRST_REX_INT_REG + 8);
switch (code)
{
case 0:
error ("extended registers have no high halves");
break;
case 1:
- fprintf (file, "r%ib", REGNO (x) - FIRST_REX_INT_REG + 8);
+ putc ('b', file);
break;
case 2:
- fprintf (file, "r%iw", REGNO (x) - FIRST_REX_INT_REG + 8);
+ putc ('w', file);
break;
case 4:
- fprintf (file, "r%id", REGNO (x) - FIRST_REX_INT_REG + 8);
+ putc ('d', file);
break;
case 8:
- fprintf (file, "r%i", REGNO (x) - FIRST_REX_INT_REG + 8);
+ /* no suffix */
break;
default:
error ("unsupported operand size for extended register");
=== modified file 'gcc/defaults.h'
--- gcc/defaults.h 2010-11-29 14:09:41 +0000
+++ gcc/defaults.h 2011-08-22 11:39:43 +0000
@@ -137,7 +137,10 @@ see the files COPYING3 and COPYING.RUNTI
#ifndef ASM_OUTPUT_LABEL
#define ASM_OUTPUT_LABEL(FILE,NAME) \
- do { assemble_name ((FILE), (NAME)); fputs (":\n", (FILE)); } while (0)
+ do { \
+ assemble_name ((FILE), (NAME)); \
+ fputs (":\n", (FILE)); \
+ } while (0)
#endif
/* This is how to output the definition of a user-level label named
@@ -160,7 +163,11 @@ see the files COPYING3 and COPYING.RUNTI
/* This is how to output a reference to a user-level label named NAME. */
#ifndef ASM_OUTPUT_LABELREF
-#define ASM_OUTPUT_LABELREF(FILE,NAME) asm_fprintf ((FILE), "%U%s", (NAME))
+#define ASM_OUTPUT_LABELREF(FILE,NAME) \
+ do { \
+ fputs (user_label_prefix, (FILE)); \
+ fputs ((NAME), (FILE)); \
+ } while (0);
#endif
/* Allow target to print debug info labels specially. This is useful for
=== modified file 'gcc/dwarf2asm.c'
--- gcc/dwarf2asm.c 2011-03-25 16:39:10 +0000
+++ gcc/dwarf2asm.c 2011-08-22 11:39:43 +0000
@@ -53,8 +53,7 @@ dw2_assemble_integer (int size, rtx x)
{
fputs (op, asm_out_file);
if (CONST_INT_P (x))
- fprintf (asm_out_file, HOST_WIDE_INT_PRINT_HEX,
- (unsigned HOST_WIDE_INT) INTVAL (x));
+ fprint_whex (asm_out_file, (unsigned HOST_WIDE_INT) INTVAL (x));
else
output_addr_const (asm_out_file, x);
}
@@ -106,16 +105,19 @@ dw2_asm_output_data (int size, unsigned
value &= ~(~(unsigned HOST_WIDE_INT) 0 << (size * 8));
if (op)
- fprintf (asm_out_file, "%s" HOST_WIDE_INT_PRINT_HEX, op, value);
+ {
+ fputs (op, asm_out_file);
+ fprint_whex (asm_out_file, value);
+ }
else
assemble_integer (GEN_INT (value), size, BITS_PER_UNIT, 1);
if (flag_debug_asm && comment)
{
- fprintf (asm_out_file, "\t%s ", ASM_COMMENT_START);
+ fputs ("\t" ASM_COMMENT_START " ", asm_out_file);
vfprintf (asm_out_file, comment, ap);
}
- fputc ('\n', asm_out_file);
+ putc ('\n', asm_out_file);
va_end (ap);
}
@@ -596,7 +598,8 @@ dw2_asm_output_data_uleb128 (unsigned HO
va_start (ap, comment);
#ifdef HAVE_AS_LEB128
- fprintf (asm_out_file, "\t.uleb128 " HOST_WIDE_INT_PRINT_HEX , value);
+ fputs ("\t.uleb128 ", asm_out_file);
+ fprint_whex (asm_out_file, value);
if (flag_debug_asm && comment)
{
@@ -641,7 +644,7 @@ dw2_asm_output_data_uleb128 (unsigned HO
}
}
#endif
- fputc ('\n', asm_out_file);
+ putc ('\n', asm_out_file);
va_end (ap);
}
@@ -745,7 +748,7 @@ dw2_asm_output_delta_uleb128 (const char
#ifdef HAVE_AS_LEB128
fputs ("\t.uleb128 ", asm_out_file);
assemble_name (asm_out_file, lab1);
- fputc ('-', asm_out_file);
+ putc ('-', asm_out_file);
assemble_name (asm_out_file, lab2);
#else
gcc_unreachable ();
@@ -775,7 +778,7 @@ dw2_asm_output_delta_sleb128 (const char
#ifdef HAVE_AS_LEB128
fputs ("\t.sleb128 ", asm_out_file);
assemble_name (asm_out_file, lab1);
- fputc ('-', asm_out_file);
+ putc ('-', asm_out_file);
assemble_name (asm_out_file, lab2);
#else
gcc_unreachable ();
=== modified file 'gcc/dwarf2out.c'
--- gcc/dwarf2out.c 2011-06-06 17:46:00 +0000
+++ gcc/dwarf2out.c 2011-08-22 11:39:43 +0000
@@ -23274,12 +23274,27 @@ dwarf2out_source_line (unsigned int line
if (DWARF2_ASM_LINE_DEBUG_INFO)
{
/* Emit the .loc directive understood by GNU as. */
- fprintf (asm_out_file, "\t.loc %d %d 0", file_num, line);
+ /* "\t.loc %u %u 0 is_stmt %u discriminator %u",
+ file_num, line, is_stmt, discriminator */
+ fputs ("\t.loc ", asm_out_file);
+ fprint_ul (asm_out_file, file_num);
+ putc (' ', asm_out_file);
+ fprint_ul (asm_out_file, line);
+ putc (' ', asm_out_file);
+ putc ('0', asm_out_file);
+
if (is_stmt != table->is_stmt)
- fprintf (asm_out_file, " is_stmt %d", is_stmt ? 1 : 0);
+ {
+ fputs (" is_stmt ", asm_out_file);
+ putc (is_stmt ? '1' : '0', asm_out_file);
+ }
if (SUPPORTS_DISCRIMINATOR && discriminator != 0)
- fprintf (asm_out_file, " discriminator %d", discriminator);
- fputc ('\n', asm_out_file);
+ {
+ gcc_assert (discriminator > 0);
+ fputs (" discriminator ", asm_out_file);
+ fprint_ul (asm_out_file, (unsigned long) discriminator);
+ }
+ putc ('\n', asm_out_file);
}
else
{
=== modified file 'gcc/final.c'
--- gcc/final.c 2011-06-06 13:24:55 +0000
+++ gcc/final.c 2011-08-22 11:39:43 +0000
@@ -3604,7 +3604,7 @@ output_addr_const (FILE *file, rtx x)
break;
case CONST_INT:
- fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x));
+ fprint_w (file, INTVAL (x));
break;
case CONST:
@@ -3719,6 +3719,125 @@ output_quoted_string (FILE *asm_file, co
#endif
}
+
+/* Write a HOST_WIDE_INT number in hex form 0x1234, fast. */
+
+void
+fprint_whex (FILE *f, unsigned HOST_WIDE_INT value)
+{
+ char buf[2 + CHAR_BIT * sizeof (value) / 4];
+ if (value == 0)
+ putc ('0', f);
+ else
+ {
+ char *p = buf + sizeof (buf);
+ do
+ *--p = "0123456789abcdef"[value % 16];
+ while ((value /= 16) != 0);
+ *--p = 'x';
+ *--p = '0';
+ fwrite (p, 1, buf + sizeof (buf) - p, f);
+ }
+}
+
+/* Internal function that prints an unsigned long in decimal in reverse.
+ The output string IS NOT null-terminated. */
+
+static int
+sprint_ul_rev (char *s, unsigned long value)
+{
+ int i = 0;
+ do
+ {
+ s[i] = "0123456789"[value % 10];
+ value /= 10;
+ i++;
+ /* alternate version, without modulo */
+ /* oldval = value; */
+ /* value /= 10; */
+ /* s[i] = "0123456789" [oldval - 10*value]; */
+ /* i++ */
+ }
+ while (value != 0);
+ return i;
+}
+
+/* Write a signed HOST_WIDE_INT as decimal to a file, fast. */
+
+void
+fprint_w (FILE *f, HOST_WIDE_INT value)
+{
+ /* python says: len(str(2**64)) == 20 */
+ char s[20];
+ int i;
+
+ if (value >= 0)
+ i = sprint_ul_rev (s, (unsigned long) value);
+ else
+ {
+ /* cast to long long to output max negative correctly! */
+ i = sprint_ul_rev (s, ((unsigned long long) value) * -1);
+ putc('-', f);
+ }
+
+ /* It's probably too small so it's not worth string reversal and fputs */
+ do
+ {
+ i--;
+ putc (s[i], f);
+ }
+ while (i != 0);
+}
+
+/* Write an unsigned long as decimal to a file, fast. */
+
+void
+fprint_ul (FILE *f, unsigned long value)
+{
+ /* python says: len(str(2**64)) == 20 */
+ char s[20];
+ int i;
+
+ i = sprint_ul_rev (s, value);
+
+ /* It's probably too small so it's not worth string reversal and fputs */
+ do
+ {
+ i--;
+ putc (s[i], f);
+ }
+ while (i != 0);
+}
+
+/* Write an unsigned long as decimal to a string, fast.
+ s must be wide enough to not overflow, at least 21 chars.
+ Returns the length of the string (without terminating '\0'). */
+
+int
+sprint_ul (char *s, unsigned long value)
+{
+ int len;
+ char tmp_c;
+ int i;
+ int j;
+
+ len = sprint_ul_rev (s, value);
+ s[len] = '\0';
+
+ /* String reversal */
+ i = 0;
+ j = len - 1;
+ while (i < j)
+ {
+ tmp_c = s[i];
+ s[i] = s[j];
+ s[j] = tmp_c;
+ i++; j--;
+ }
+
+ return len;
+}
+
/* A poor man's fprintf, with the added features of %I, %R, %L, and %U.
%R prints the value of REGISTER_PREFIX.
%L prints the value of LOCAL_LABEL_PREFIX.
=== modified file 'gcc/output.h'
--- gcc/output.h 2011-02-03 19:12:07 +0000
+++ gcc/output.h 2011-08-22 11:39:43 +0000
@@ -129,6 +129,11 @@ typedef HOST_WIDE_INT __gcc_host_wide_in
#define ATTRIBUTE_ASM_FPRINTF(m, n) ATTRIBUTE_NONNULL(m)
#endif
+extern void fprint_w (FILE *, HOST_WIDE_INT);
+extern void fprint_whex (FILE *, unsigned HOST_WIDE_INT);
+extern void fprint_ul (FILE *, unsigned long);
+extern int sprint_ul (char *, unsigned long);
+
extern void asm_fprintf (FILE *file, const char *p, ...)
ATTRIBUTE_ASM_FPRINTF(2, 3);
@@ -652,8 +657,11 @@ extern void default_file_start (void);
extern void file_end_indicate_exec_stack (void);
extern void file_end_indicate_split_stack (void);
-extern void default_elf_asm_output_external (FILE *file, tree,
- const char *);
+extern void default_elf_asm_output_external (FILE *file, tree, const char *);
+extern void default_elf_asm_output_limited_string (FILE *, const char *);
+extern void default_elf_asm_output_ascii (FILE *, const char *, unsigned int);
+extern void default_elf_internal_label (FILE *, const char *, unsigned long);
+
extern int maybe_assemble_visibility (tree);
extern int default_address_cost (rtx, bool);
=== modified file 'gcc/varasm.c'
--- gcc/varasm.c 2011-05-30 16:42:05 +0000
+++ gcc/varasm.c 2011-08-22 11:39:43 +0000
@@ -559,7 +559,7 @@ default_function_section (tree decl, enu
/* Return the section for function DECL.
If DECL is NULL_TREE, return the text section. We can be passed
- NULL_TREE under some circumstances by dbxout.c at least.
+ NULL_TREE under some circumstances by dbxout.c at least.
If FORCE_COLD is true, return cold function section ignoring
the frequency info of cgraph_node. */
@@ -1921,7 +1921,7 @@ assemble_variable (tree decl, int top_le
/* Emulated TLS had better not get this far. */
gcc_checking_assert (targetm.have_tls || !DECL_THREAD_LOCAL_P (decl));
-
+
last_assemble_variable_decl = 0;
/* Normally no need to say anything here for external references,
@@ -2838,7 +2838,7 @@ compare_constant (const tree t1, const t
return 0;
link2 = TREE_CHAIN (link2);
}
-
+
return 1;
}
@@ -5740,7 +5740,7 @@ finish_aliases_1 (void)
&& ! lookup_attribute ("weakref", DECL_ATTRIBUTES (p->decl)))
{
error ("%q+D aliased to external symbol %qE",
- p->decl, p->target);
+ p->decl, p->target);
p->emitted_diags |= ALIAS_DIAG_TO_EXTERN;
}
}
@@ -5806,7 +5806,7 @@ assemble_alias (tree decl, tree target)
if (lookup_attribute ("ifunc", DECL_ATTRIBUTES (decl)))
error_at (DECL_SOURCE_LOCATION (decl),
"ifunc is not supported in this configuration");
- else
+ else
error_at (DECL_SOURCE_LOCATION (decl),
"only weak aliases are supported in this configuration");
return;
@@ -5849,7 +5849,7 @@ assemble_alias (tree decl, tree target)
the visibility type VIS, which must not be VISIBILITY_DEFAULT. */
void
-default_assemble_visibility (tree decl ATTRIBUTE_UNUSED,
+default_assemble_visibility (tree decl ATTRIBUTE_UNUSED,
int vis ATTRIBUTE_UNUSED)
{
#ifdef HAVE_GAS_HIDDEN
@@ -7347,4 +7347,122 @@ make_debug_expr_from_rtl (const_rtx exp)
return dval;
}
+void
+default_elf_asm_output_limited_string (FILE *f, const char *s)
+{
+ int escape;
+ unsigned char c;
+
+ fputs ("\t.string\t\"", f);
+ while (*s != '\0')
+ {
+ c = *s;
+ escape = ELF_ASCII_ESCAPES[c];
+ switch (escape)
+ {
+ case 0:
+ putc (c, f);
+ break;
+ case 1:
+ /* TODO: Print in hex with fast function... VERY IMPORTANT FOR
-flto!!! */
+ fprintf (f, "\\%03o", c);
+ break;
+ default:
+ putc ('\\', f);
+ putc (escape, f);
+ break;
+ }
+ s++;
+ }
+ putc ('\"', f);
+ putc ('\n', f);
+}
+
+void
+default_elf_asm_output_ascii (FILE *f, const char *s, unsigned int len)
+{
+ const char *limit = s + len;
+ const char *last_null = NULL;
+ unsigned bytes_in_chunk = 0;
+ unsigned char c;
+ int escape;
+
+ for (; s < limit; s++)
+ {
+ const char *p;
+
+ if (bytes_in_chunk >= 60)
+ {
+ putc ('\"', f);
+ putc ('\n', f);
+ bytes_in_chunk = 0;
+ }
+
+ if (s > last_null)
+ {
+ for (p = s; p < limit && *p != '\0'; p++)
+ continue;
+ last_null = p;
+ }
+ else
+ p = last_null;
+
+ if (p < limit && (p - s) <= (long) ELF_STRING_LIMIT)
+ {
+ if (bytes_in_chunk > 0)
+ {
+ putc ('\"', f);
+ putc ('\n', f);
+ bytes_in_chunk = 0;
+ }
+
+ default_elf_asm_output_limited_string (f, s);
+ s = p;
+ }
+ else
+ {
+ if (bytes_in_chunk == 0)
+ fputs (ASCII_DATA_ASM_OP "\"", f);
+
+ c = *s;
+ escape = ELF_ASCII_ESCAPES[c];
+ switch (escape)
+ {
+ case 0:
+ putc (c, f);
+ bytes_in_chunk++;
+ break;
+ case 1:
+ /* TODO: Print in hex with fast function... VERY IMPORTANT FOR
-flto!!! */
+ fprintf (f, "\\%03o", c);
+ bytes_in_chunk += 4;
+ break;
+ default:
+ putc ('\\', f);
+ putc (escape, f);
+ bytes_in_chunk += 2;
+ break;
+ }
+
+ }
+ }
+
+ if (bytes_in_chunk > 0)
+ {
+ putc ('\"', f);
+ putc ('\n', f);
+ }
+}
+
+void
+default_elf_internal_label (FILE *f, const char *prefix,
+ unsigned long labelno)
+{
+ putc ('.', f);
+ fputs (prefix, f);
+ fprint_ul (f, labelno);
+ putc (':', f);
+ putc ('\n', f);
+}
+
#include "gt-varasm.h"