Hello list, I updated this patch to latest trunk and also incorporated
apinski's suggestion to use stpcpy() instead of a custom loop.
Bootstrapped and tested again on i386, no regressions. The comments from
my previous email still apply, I never got a reply:
On Mon, 22 Aug 2011, Dimitrios Apostolou wrote:
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.
Slightly more descriptive 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 stpcpy() 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 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 as these functions, 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 the new
faster functions.
* dwarf2out.c (dwarf2out_source_line): Convert fprintf() calls to
the new faster functions.
I had measured during the summer ([1] before, [2] after, if necessary I'll
refresh measurements) that this gives a good 2-3% improvement, especially
when debug info is enabled. Major obstacle was that it made code less
readable but I think the new function names are more understandable.
[1]
http://gcc.gnu.org/wiki/OptimisingGCC?action=AttachFile&do=view&target=4.6.0-baseline_prof.txt
[2]
http://gcc.gnu.org/wiki/OptimisingGCC?action=AttachFile&do=view&target=4.6.0-changed1_prof.txt
Jakub, you had raised concerns that it's better to avoid this optimisation
completely and make gcc always optimise fprintf("%d") calls, to keep the
code readable. Do you think that's the case still?
I appreciate all comments,
Dimitris
P.S. Cc'd i386 backend maintainers
=== modified file 'gcc/config/elfos.h'
--- gcc/config/elfos.h 2010-11-21 00:54:14 +0000
+++ gcc/config/elfos.h 2011-10-29 22:57:35 +0000
@@ -117,10 +117,17 @@ see the files COPYING3 and COPYING.RUNTI
#define ASM_GENERATE_INTERNAL_LABEL(LABEL, PREFIX, NUM) \
do \
{ \
- sprintf (LABEL, "*.%s%u", PREFIX, (unsigned) (NUM)); \
+ char *__p; \
+ (LABEL)[0] = '*'; \
+ (LABEL)[1] = '.'; \
+ __p = stpcpy (&(LABEL)[2], PREFIX); \
+ sprint_ul (__p, (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 +378,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 +400,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 +410,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 +420,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. */
=== modified file 'gcc/config/i386/i386.c'
--- gcc/config/i386/i386.c 2011-10-29 18:38:27 +0000
+++ gcc/config/i386/i386.c 2011-10-29 22:07:03 +0000
@@ -13436,26 +13436,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 2011-07-09 20:56:42 +0000
+++ gcc/defaults.h 2011-10-29 22:07:03 +0000
@@ -142,7 +142,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
@@ -165,7 +168,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-07-09 20:18:32 +0000
+++ gcc/dwarf2asm.c 2011-10-29 22:07:03 +0000
@@ -47,8 +47,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);
}
@@ -100,16 +99,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);
}
@@ -590,7 +592,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)
{
@@ -635,7 +638,7 @@ dw2_asm_output_data_uleb128 (unsigned HO
}
}
#endif
- fputc ('\n', asm_out_file);
+ putc ('\n', asm_out_file);
va_end (ap);
}
@@ -739,7 +742,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 ();
@@ -769,7 +772,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-10-25 22:14:26 +0000
+++ gcc/dwarf2out.c 2011-10-29 22:07:03 +0000
@@ -20429,12 +20429,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-09-21 07:41:22 +0000
+++ gcc/final.c 2011-10-29 22:07:03 +0000
@@ -3579,7 +3579,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:
@@ -3693,6 +3693,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-09-27 17:52:13 +0000
+++ gcc/output.h 2011-10-29 22:24:55 +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);
@@ -653,8 +658,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 void default_elf_init_array_asm_out_constructor (rtx, int);
extern void default_elf_fini_array_asm_out_destructor (rtx, int);
extern int maybe_assemble_visibility (tree);
=== modified file 'gcc/varasm.c'
--- gcc/varasm.c 2011-10-13 09:54:50 +0000
+++ gcc/varasm.c 2011-10-29 22:28:53 +0000
@@ -564,7 +564,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. */
@@ -1928,7 +1928,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,
@@ -2852,7 +2852,7 @@ compare_constant (const tree t1, const t
return 0;
link2 = TREE_CHAIN (link2);
}
-
+
return 1;
}
@@ -5754,7 +5754,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;
}
}
@@ -5820,7 +5820,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;
@@ -5863,7 +5863,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
@@ -7363,6 +7363,124 @@ 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);
+}
+
static GTY(()) section *elf_init_array_section;
static GTY(()) section *elf_fini_array_section;