From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 29378 invoked by alias); 24 Jan 2002 09:06:03 -0000 Mailing-List: contact gcc-prs-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Archive: List-Post: List-Help: Sender: gcc-prs-owner@gcc.gnu.org Received: (qmail 29348 invoked by uid 71); 24 Jan 2002 09:06:01 -0000 Resent-Date: 24 Jan 2002 09:06:01 -0000 Resent-Message-ID: <20020124090601.29347.qmail@sources.redhat.com> Resent-From: gcc-gnats@gcc.gnu.org (GNATS Filer) Resent-To: nobody@gcc.gnu.org Resent-Cc: gcc-prs@gcc.gnu.org, gcc-bugs@gcc.gnu.org Resent-Reply-To: gcc-gnats@gcc.gnu.org, espenlaub@informatik.uni-ulm.de Received:(qmail 28777 invoked by uid 61); 24 Jan 2002 09:04:18 -0000 Message-Id:<20020124090418.28776.qmail@sources.redhat.com> Date: Thu, 24 Jan 2002 01:06:00 -0000 From: espenlaub@informatik.uni-ulm.de Reply-To: espenlaub@informatik.uni-ulm.de To: gcc-gnats@gcc.gnu.org X-Send-Pr-Version:gnatsweb-2.9.3 (1.1.1.1.2.31) Subject: optimization/5477: gcc 3.0.x reserves a large stack frame, but uses only some parts of it X-SW-Source: 2002-01/txt/msg00865.txt.bz2 List-Id: >Number: 5477 >Category: optimization >Synopsis: gcc 3.0.x reserves a large stack frame, but uses only some parts of it >Confidential: no >Severity: serious >Priority: medium >Responsible: unassigned >State: open >Class: pessimizes-code >Submitter-Id: net >Arrival-Date: Thu Jan 24 01:06:00 PST 2002 >Closed-Date: >Last-Modified: >Originator: Klaus Espenlaub >Release: gcc-3.0.3 >Organization: >Environment: System: Linux croc 2.4.7-4GB #1 Thu Oct 25 17:53:12 GMT 2001 i686 unknown Architecture: i686 >Description: All recent gcc C-compiler versions (starting from 3.0) create code that has the distinctive tendency to allocate stack frames that are much larger than required for the respective function. The situation is worsened by heavy use of small functions declared __inline__. In my case - embedded environment with very tight stack - this makes recent gcc versions unusable. The attached sample compiles to a single function, which (on the i386 architecture) reserves 380 bytes for local variables. The same code compiled with gcc 2.95.3 reserves just 108 bytes. If the inline function printf_putchar is replaced by an equivalent preprocessor macro, then the stack allocation is reduced drastically to 124 bytes. Interestingly for gcc 2.95 it doesn't make a substantial difference whether inline functions or preprocessor macros are used, but for gcc 3.0 it does. As a side note: the same code compiled by g++ creates code that uses a reasonable amount of stack, regardless of the compiler version (2.95 or 3.0.x). >How-To-Repeat: (assuming that the attached file - which BTW uses #include, but only stuff that comes with gcc - is saved to printf-stackwaste.c) gcc -O2 -S printf-stackwaste.c for the version using inlining, and gcc -O2 -S -DPUTCHAR_MACRO printf-stackwaste.c for the version using a preprocessor macro. The interesting number is the value that is subtracted from %esp at the end of the epilogue for do_printf. >Fix: >Release-Note: >Audit-Trail: >Unformatted: ----gnatsweb-attachment---- Content-Type: text/plain; name="printf-stackwaste.c" Content-Disposition: inline; filename="printf-stackwaste.c" #include #include /* Some gcc versions don't define LLONG_MAX, because it wasn't defined in the * earlier C standards. */ #ifndef LLONG_MAX #define LLONG_MAX 9223372036854775807LL #endif /* LLONG_MAX */ #define NULL ((void *)0) typedef unsigned int size_t; #define ZEROPAD 1 /* pad with zero */ #define SIGN 2 /* unsigned/signed long */ #define PLUS 4 /* show plus */ #define SPACE 8 /* space if plus */ #define LEFT 16 /* left justified */ #define SPECIAL 32 /* 0x */ #define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */ #ifdef PUTCHAR_MACRO #define printf_putchar(buf, c) \ if (*(buf) != NULL) { \ **(buf) = c; \ (*(buf))++; \ } else { \ __console_putchar(c); \ } #else static __inline__ void printf_putchar(char **buf, unsigned char c) { if (*buf != NULL) { **buf = c; (*buf)++; } else { __console_putchar(c); } } #endif static __inline__ void number(char **str, unsigned long long num, unsigned base, int size, unsigned precision, int type, size_t *maxlen) { char sign, tmp[22]; static const char digits[16] = "0123456789abcdef"; unsigned i; if (type & LEFT) type &= ~ZEROPAD; if (base < 8 || base > 16) return; sign = 0; if (type & SIGN) { if (num > LLONG_MAX) { sign = '-'; num = -num; size--; } else if (type & PLUS) { sign = '+'; size--; } else if (type & SPACE) { sign = ' '; size--; } } if (type & SPECIAL) { if (base == 16) size -= 2; else if (base == 8) size--; } i = 0; if (num == 0) tmp[i++] = '0'; else while (num != 0) { tmp[i] = digits[num % base]; num /= base; if ((type & LARGE) && (tmp[i] > '9')) tmp[i] &= 0xdf; i++; } if ((i > precision) || (precision == UINT_MAX)) precision = i; size -= precision; if (!(type&(ZEROPAD+LEFT))) while((size-->0) && (*maxlen > 0)) { printf_putchar(str, ' '); (*maxlen)--; } if (sign && (*maxlen > 0)) { printf_putchar(str, sign); (*maxlen)--; } if (type & SPECIAL) { if ((base==8) && (*maxlen > 0)) { printf_putchar(str, '0'); (*maxlen)--; } else if (base==16) { if (*maxlen > 0) { printf_putchar(str, '0'); (*maxlen)--; } if (*maxlen > 0) { printf_putchar(str, (type & LARGE) ? 'X' : 'x'); (*maxlen)--; } } } if (!(type & LEFT)) while ((size-- > 0) && (*maxlen > 0)) { printf_putchar(str, (type & ZEROPAD) ? '0' : ' '); (*maxlen)--; } while ((i < precision--) && (*maxlen > 0)) { printf_putchar(str, '0'); (*maxlen)--; } while ((i-- > 0) && (*maxlen > 0)) { printf_putchar(str, tmp[i]); (*maxlen)--; } while ((size-- > 0) && (*maxlen > 0)) { printf_putchar(str, ' '); (*maxlen)--; } } static __inline__ unsigned skip_atoi(const char **s) { unsigned i = 0; while ((**s >= '0') && (**s <= '9')) i = i*10 + *((*s)++) - '0'; return i; } int do_printf(char *buf, size_t maxlen, const char *fmt, va_list args) { int len; unsigned long long num; int i; unsigned base; char *str; const char *s; int flags; /* flags to number() */ int field_width; /* width of output field */ unsigned precision; /* min. digits (int); max. chars (string) */ int qualifier; /* 'h', 'l', or 'L' for integer fields */ for (str=buf ; *fmt ; ++fmt) { if (*fmt != '%') { if (maxlen > 0) { printf_putchar(&str, *fmt); maxlen--; } continue; } /* process flags */ flags = 0; repeat: ++fmt; /* this also skips first '%' */ switch (*fmt) { case '-': flags |= LEFT; goto repeat; case '+': flags |= PLUS; goto repeat; case ' ': flags |= SPACE; goto repeat; case '#': flags |= SPECIAL; goto repeat; case '0': flags |= ZEROPAD; goto repeat; } /* get field width */ field_width = -1; if ((*fmt >= '0') && (*fmt <= '9')) field_width = skip_atoi(&fmt); else if (*fmt == '*') { ++fmt; /* it's the next argument */ field_width = va_arg(args, int); if (field_width < 0) { field_width = -field_width; flags |= LEFT; } } /* get the precision */ precision = UINT_MAX; if (*fmt == '.') { ++fmt; if ((*fmt >= '0') && (*fmt <= '9')) precision = skip_atoi(&fmt); else if (*fmt == '*') { ++fmt; /* it's the next argument */ precision = va_arg(args, unsigned int); if ((signed) precision < 0) precision = 0; } } /* get the conversion qualifier */ qualifier = -1; if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') { qualifier = *fmt; ++fmt; } /* default base */ base = 10; switch (*fmt) { case 'c': if (!(flags & LEFT)) while ((--field_width > 0) && (maxlen > 0)) { printf_putchar(&str, ' '); maxlen--; } if (maxlen > 0) { printf_putchar(&str, (unsigned char) va_arg(args, int)); maxlen--; } while ((--field_width > 0) && (maxlen > 0)) { printf_putchar(&str, ' '); maxlen--; } continue; case 's': s = va_arg(args, char *); if (!s) s = ""; len = strnlen(s, precision); if (!(flags & LEFT)) while ((len < field_width--) && (maxlen > 0)) { printf_putchar(&str, ' '); maxlen--; } for (i = 0; (i < len) && (maxlen > 0); ++i) { printf_putchar(&str, *s++); maxlen--; } while ((len < field_width--) && (maxlen > 0)) { printf_putchar(&str, ' '); maxlen--; } continue; case 'n': if (qualifier == 'l') { long *ip = va_arg(args, long *); *ip = (str - buf); } else if (qualifier == 'L') { long long *ip = va_arg(args, long long *); *ip = (str - buf); } else { int *ip = va_arg(args, int *); *ip = (str - buf); } continue; /* integer number formats - set up the flags and "break" */ case 'o': base = 8; break; case 'X': flags |= LARGE; case 'x': base = 16; break; case 'd': case 'i': flags |= SIGN; case 'u': break; case 'p': if (field_width == -1) { field_width = 2*sizeof(void *); flags |= ZEROPAD; flags &= ~SIGN; } qualifier = '@'; base = 16; break; default: if ((*fmt != '%') && (maxlen > 0)) { printf_putchar(&str, '%'); maxlen--; } if (*fmt) { if (maxlen > 0) { printf_putchar(&str, *fmt); maxlen--; } } else { --fmt; } continue; } if (qualifier == 'l') { if (flags & SIGN) { num = va_arg(args, long); } else { num = va_arg(args, unsigned long); } } else if (qualifier == 'L') { if (flags & SIGN) { num = va_arg(args, long long); } else { num = va_arg(args, unsigned long long); } } else if (qualifier == 'h') { if (flags & SIGN) { num = va_arg(args, int); /* promoted!!! */ } else { num = va_arg(args, int); /* promoted!!! */ } } else if (qualifier == '@') { /* Fake qualifier to fit %p formats into integer formats. */ num = (unsigned long)va_arg(args, void *); } else if (flags & SIGN) { num = va_arg(args, int); } else { num = va_arg(args, unsigned int); } number(&str, num, base, field_width, precision, flags, &maxlen); } if (maxlen > 0) { if (str != NULL) { *str = '\0'; } maxlen--; return maxlen; } else { return -1; } }