public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* protected alloca class for malloc fallback
@ 2016-08-04 11:30 Aldy Hernandez
  2016-08-04 12:58 ` Richard Biener
                   ` (2 more replies)
  0 siblings, 3 replies; 52+ messages in thread
From: Aldy Hernandez @ 2016-08-04 11:30 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 2049 bytes --]

Howdy!

As part of my -Walloca-larger-than=life work, I've been running said 
pass over gcc, binutils, and gdb, and trying to fix things along the way.

Particularly irritating and error prone is having to free malloc'd 
pointers on every function exit point.  We end up with a lot of:

foo(size_t len)
{
   void *p, *m_p = NULL;
   if (len < HUGE)
     p = alloca(len);
   else
     p = m_p = malloc(len);
   if (something)
     goto out;
   stuff();
out:
   free (m_p);
}

...which nobody really likes.

I've been thinking that for GCC we could have a protected_alloca class 
whose destructor frees any malloc'd memory:

void foo()
{
   char *p;
   protected_alloca chunk(50000);
   p = (char *) chunk.pointer();
   f(p);
}

This would generate:

void foo() ()
{
   void * _3;

   <bb 2>:
   _3 = malloc (50000);
   f (_3);

   <bb 3>:
   free (_3); [tail call]
   return;
}

Now the problem with this is that the memory allocated by chunk is freed 
when it goes out of scope, which may not be what you want.  For example:

      func()
      {
        char *str;
        {
          protected_alloca chunk (99999999);
	 // malloc'd pointer will be freed when chunk goes out of scope.
          str = (char *) chunk.pointer ();
        }
        use (str);  // BAD!  Use after free.
      }

In the attached patch implementing this class I have provided another 
idiom for avoiding this problem:

      func()
      {
        void *ptr;
        protected_alloca chunk;
        {
          chunk.alloc (9999999);
	 str = (char *) chunk.pointer ();
        }
        // OK, pointer will be freed on function exit.
        use (str);
      }

So I guess it's between annoying gotos and keeping track of multiple 
exit points to a function previously calling alloca, or making sure the 
protected_alloca object always resides in the scope where the memory is 
going to be used.

Is there a better blessed C++ way?  If not, is this OK?

Included is the conversion of tree.c.  More to follow once we agree on a 
solution.

Tested on x86-64 Linux.

Aldy

[-- Attachment #2: curr --]
[-- Type: text/plain, Size: 7516 bytes --]

commit fd0078ef60dd75ab488392e0e05b28f27d971bdf
Author: Aldy Hernandez <aldyh@redhat.com>
Date:   Thu Aug 4 06:43:37 2016 -0400

    	* protected-alloca.h: New.
    	* tree.c (get_file_function_name): Use protected_alloca.
    	(tree_check_failed): Same.
    	(tree_not_check_failed): Same.
    	(tree_range_check_failed): Same.
    	(omp_clause_range_check_failed): Same.

diff --git a/gcc/protected-alloca.h b/gcc/protected-alloca.h
new file mode 100644
index 0000000..62f2a7b
--- /dev/null
+++ b/gcc/protected-alloca.h
@@ -0,0 +1,113 @@
+/* Alloca wrapper with a malloc fallback.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+   Contributed by Aldy Hernandez <aldyh@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 _PROTECTED_ALLOCA_H_
+#define _PROTECTED_ALLOCA_H_
+
+#ifndef MAX_ALLOCA_SIZE
+#define MAX_ALLOCA_SIZE 4096
+#endif
+
+#ifdef __GNUC__
+#define _ALLOCA_INLINE_ __attribute__((always_inline))
+#else
+#define _ALLOCA_INLINE_
+#endif
+
+/* This is a wrapper class for alloca that falls back to malloc if the
+   allocation size is > MAX_ALLOCA_SIZE.  It is meant to replace:
+
+     char *str = (char *) alloca (N);
+
+   by this:
+
+     protected_alloca chunk (N);
+     char *str = (char *) chunk.pointer ();
+
+   or this:
+
+     protected_alloca chunk;
+     chunk.alloc (N);
+     char *str = (char *) chunk.pointer ();
+
+   If N > MAX_ALLOCA_SIZE, malloc is used, and whenever `chunk' goes
+   out of scope, the malloc'd memory will be freed.  Keep in mind that
+   the malloc'd memory gets freed when `chunk' goes out of scope, and
+   may be freed earlier than expected.  For example:
+
+     func()
+     {
+       char *str;
+       {
+         protected_alloca chunk (99999999);
+	 // If malloc'd, pointer will be freed when chunk goes out of scope.
+         str = (char *) chunk.pointer ();
+       }
+       use (str);  // BAD!  Use after free.
+     }
+
+   In this case, it is best to use the following idiom:
+
+     func()
+     {
+       void *ptr;
+       protected_alloca chunk;
+       {
+         chunk.alloc (9999999);
+	 str = (char *) chunk.pointer ();
+       }
+       // OK, pointer will be freed on function exit.
+       use (str);
+     }
+*/
+
+class protected_alloca {
+  void *p;
+  bool on_heap;
+public:
+  // GCC will refuse to inline a function that calls alloca unless
+  // `always_inline' is used, for fear of inlining an alloca call
+  // appearing in a loop.  Inlining this constructor won't make code
+  // any worse than it already is wrt loops.  We know what we're
+  // doing.
+  _ALLOCA_INLINE_ void
+  internal_alloc (size_t size)
+  {
+    if (size < MAX_ALLOCA_SIZE)
+      {
+	p = alloca (size);
+	on_heap = false;
+      }
+    else
+      {
+	p = xmalloc (size);
+	on_heap = true;
+      }
+  }
+  _ALLOCA_INLINE_
+  protected_alloca (size_t size) { internal_alloc (size); }
+  protected_alloca () { p = NULL; on_heap = false; }
+  ~protected_alloca () { if (on_heap) free (p); }
+  __attribute__((always_inline))
+  void alloc (size_t size) { gcc_assert (!p); internal_alloc (size); }
+  void *pointer () { return p; }
+};
+
+#endif // _PROTECTED_ALLOCA_H_
diff --git a/gcc/tree.c b/gcc/tree.c
index 11d3b51..621dcd5 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -62,6 +62,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "print-tree.h"
 #include "ipa-utils.h"
 #include "selftest.h"
+#include "protected-alloca.h"
 
 /* Tree code classes.  */
 
@@ -9639,6 +9640,7 @@ get_file_function_name (const char *type)
   char *buf;
   const char *p;
   char *q;
+  protected_alloca q_alloca;
 
   /* If we already have a name we know to be unique, just use that.  */
   if (first_global_object_name)
@@ -9674,7 +9676,8 @@ get_file_function_name (const char *type)
 	file = LOCATION_FILE (input_location);
 
       len = strlen (file);
-      q = (char *) alloca (9 + 17 + len + 1);
+      q_alloca.alloc (9 + 17 + len + 1);
+      q = (char *) q_alloca.pointer ();
       memcpy (q, file, len + 1);
 
       snprintf (q + len, 9 + 17 + 1, "_%08X_" HOST_WIDE_INT_PRINT_HEX, 
@@ -9684,8 +9687,9 @@ get_file_function_name (const char *type)
     }
 
   clean_symbol_name (q);
-  buf = (char *) alloca (sizeof (FILE_FUNCTION_FORMAT) + strlen (p)
-			 + strlen (type));
+  protected_alloca buf_alloca (sizeof (FILE_FUNCTION_FORMAT) + strlen (p)
+			       + strlen (type));
+  buf = (char *) buf_alloca.pointer ();
 
   /* Set up the name of the file-level functions we may need.
      Use a global object (which is already required to be unique over
@@ -9709,7 +9713,8 @@ tree_check_failed (const_tree node, const char *file,
 {
   va_list args;
   const char *buffer;
-  unsigned length = 0;
+  protected_alloca buffer_alloca;
+  size_t length = 0;
   enum tree_code code;
 
   va_start (args, function);
@@ -9721,7 +9726,8 @@ tree_check_failed (const_tree node, const char *file,
       char *tmp;
       va_start (args, function);
       length += strlen ("expected ");
-      buffer = tmp = (char *) alloca (length);
+      buffer_alloca.alloc (length);
+      buffer = tmp = (char *) buffer_alloca.pointer ();
       length = 0;
       while ((code = (enum tree_code) va_arg (args, int)))
 	{
@@ -9752,7 +9758,8 @@ tree_not_check_failed (const_tree node, const char *file,
 {
   va_list args;
   char *buffer;
-  unsigned length = 0;
+  protected_alloca buffer_alloca;
+  size_t length = 0;
   enum tree_code code;
 
   va_start (args, function);
@@ -9760,7 +9767,8 @@ tree_not_check_failed (const_tree node, const char *file,
     length += 4 + strlen (get_tree_code_name (code));
   va_end (args);
   va_start (args, function);
-  buffer = (char *) alloca (length);
+  buffer_alloca.alloc (length);
+  buffer = (char *) buffer_alloca.pointer ();
   length = 0;
   while ((code = (enum tree_code) va_arg (args, int)))
     {
@@ -9802,14 +9810,16 @@ tree_range_check_failed (const_tree node, const char *file, int line,
 			 enum tree_code c2)
 {
   char *buffer;
-  unsigned length = 0;
+  protected_alloca buffer_alloca;
+  size_t length = 0;
   unsigned int c;
 
   for (c = c1; c <= c2; ++c)
     length += 4 + strlen (get_tree_code_name ((enum tree_code) c));
 
   length += strlen ("expected ");
-  buffer = (char *) alloca (length);
+  buffer_alloca.alloc (length);
+  buffer = (char *) buffer_alloca.pointer ();
   length = 0;
 
   for (c = c1; c <= c2; ++c)
@@ -9863,14 +9873,16 @@ omp_clause_range_check_failed (const_tree node, const char *file, int line,
 			       enum omp_clause_code c2)
 {
   char *buffer;
-  unsigned length = 0;
+  protected_alloca buffer_alloca;
+  size_t length = 0;
   unsigned int c;
 
   for (c = c1; c <= c2; ++c)
     length += 4 + strlen (omp_clause_code_name[c]);
 
   length += strlen ("expected ");
-  buffer = (char *) alloca (length);
+  buffer_alloca.alloc (length);
+  buffer = (char *) buffer_alloca.pointer ();
   length = 0;
 
   for (c = c1; c <= c2; ++c)

^ permalink raw reply	[flat|nested] 52+ messages in thread

end of thread, other threads:[~2016-08-23 23:17 UTC | newest]

Thread overview: 52+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-08-04 11:30 protected alloca class for malloc fallback Aldy Hernandez
2016-08-04 12:58 ` Richard Biener
2016-08-04 15:19   ` Aldy Hernandez
2016-08-04 19:24     ` Jeff Law
2016-08-05 14:37       ` Aldy Hernandez
2016-08-05 15:15         ` Pedro Alves
2016-08-05 16:23         ` Jeff Law
2016-08-05 17:48           ` Richard Biener
2016-08-05  8:17     ` Richard Biener
2016-08-04 19:06 ` Pedro Alves
2016-08-04 19:16   ` Jeff Law
2016-08-04 19:22     ` Pedro Alves
2016-08-04 19:26       ` Jeff Law
2016-08-04 19:31         ` Pedro Alves
2016-08-05  2:10 ` Martin Sebor
2016-08-05 14:42   ` Aldy Hernandez
2016-08-05 17:56     ` Richard Biener
2016-08-05 18:16       ` Oleg Endo
2016-08-05 20:07         ` Richard Biener
2016-08-06 10:09           ` Aldy Hernandez
2016-08-06 10:15           ` Aldy Hernandez
2016-08-06 15:08             ` Richard Biener
2016-08-08 17:00               ` Jeff Law
2016-08-08 17:32                 ` Trevor Saunders
2016-08-08 19:03                   ` Richard Biener
2016-08-09 11:34                   ` Oleg Endo
2016-08-09 17:34                     ` Trevor Saunders
2016-08-10 17:03                       ` Oleg Endo
2016-08-11  1:23                         ` Trevor Saunders
2016-08-11 12:18                           ` Oleg Endo
2016-08-11 17:55                             ` Trevor Saunders
2016-08-20  2:29                         ` Mike Stump
2016-08-21 20:00                           ` C++11? (Re: protected alloca class for malloc fallback) Pedro Alves
2016-08-22  7:10                             ` Trevor Saunders
2016-08-22  7:28                               ` Richard Biener
2016-08-22 12:02                             ` Eric Gallager
2016-08-22 12:58                               ` Manuel López-Ibáñez
2016-08-22 22:08                               ` Mike Stump
2016-08-23 23:17                                 ` Eric Gallager
2016-08-09 13:17       ` protected alloca class for malloc fallback Aldy Hernandez
2016-08-09 13:21         ` Bernd Schmidt
2016-08-10 10:04         ` Richard Biener
2016-08-10 10:12           ` Aldy Hernandez
2016-08-10 10:39             ` Richard Biener
2016-08-10 18:00           ` Jeff Law
2016-08-10 18:33             ` Richard Biener
2016-08-16 16:28               ` Jeff Law
2016-08-16 16:44                 ` Jakub Jelinek
2016-08-16 16:47                   ` Jeff Law
2016-08-16 17:54                     ` Martin Sebor
2016-08-17  8:27                       ` Richard Biener
2016-08-17 13:39                         ` Martin Sebor

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).