emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Emacs-diffs] trunk r117851: Improve the experimental local and scoped a


From: Paul Eggert
Subject: [Emacs-diffs] trunk r117851: Improve the experimental local and scoped allocation.
Date: Wed, 10 Sep 2014 06:38:46 +0000
User-agent: Bazaar (2.6b2)

------------------------------------------------------------
revno: 117851
revision-id: address@hidden
parent: address@hidden
committer: Paul Eggert <address@hidden>
branch nick: trunk
timestamp: Tue 2014-09-09 23:38:38 -0700
message:
  Improve the experimental local and scoped allocation.
  
  * configure.ac (HAVE_STRUCT_ATTRIBUTE_ALIGNED)
  (HAVE_STATEMENT_EXPRESSIONS): New configure-time checks.
  * src/alloc.c (local_string_init, local_vector_init):
  New functions, defined if USE_LOCAL_ALLOCATORS.
  Mostly, these are moved here from lisp.h, as it's not
  clear it's worth making them inline.
  * src/lisp.h (USE_STACK_LISP_OBJECTS): Default to false.
  (GCALIGNED): Depend on HAVE_STRUCT_ATTRIBUTE_ALIGNED and
  USE_STACK_LISP_OBJECTS, not on a laundry list.
  (local_string_init, local_vector_init): New decls.
  (union Aligned_Cons): New type.
  (scoped_cons): Use it.  Give up on the char trick, as it's a too
  much of a maintenance hassle; if someone wants this speedup
  they'll just need to convince their compiler to align properly.
  Conversely, use the speedup if struct Lisp_Cons happens to
  be aligned even without a directive.  Better yet, help it along
  by using union Aligned_Cons rather than struct Lisp_Cons.
  (pointer_valid_for_lisp_object): Remove.  This check is not
  necessary, since make_lisp_ptr is already doing it.  All uses removed.
  (local_vector_init, local_string_init): Move to alloc.c.
  (build_local_vector): Remove this awkward macro, replacing with ...
  (make_local_vector): New macro, which acts more like a function.
  Use statement expressions and use __COUNTER__ to avoid macro
  capture.  Fall back on functions if these features are not supported.
  (build_local_string, make_local_string): Likewise.
modified:
  ChangeLog                      changelog-20091113204419-o5vbwnq5f7feedwu-1538
  configure.ac                   
configure.in-20091113204419-o5vbwnq5f7feedwu-783
  src/ChangeLog                  changelog-20091113204419-o5vbwnq5f7feedwu-1438
  src/alloc.c                    alloc.c-20091113204419-o5vbwnq5f7feedwu-252
  src/lisp.h                     lisp.h-20091113204419-o5vbwnq5f7feedwu-253
=== modified file 'ChangeLog'
--- a/ChangeLog 2014-09-07 08:46:42 +0000
+++ b/ChangeLog 2014-09-10 06:38:38 +0000
@@ -1,3 +1,9 @@
+2014-09-10  Paul Eggert  <address@hidden>
+
+       Improve the experimental local and scoped allocation.
+       * configure.ac (HAVE_STRUCT_ATTRIBUTE_ALIGNED)
+       (HAVE_STATEMENT_EXPRESSIONS): New configure-time checks.
+
 2014-09-07  Paul Eggert  <address@hidden>
 
        Expand @AM_DEFAULT_VERBOSITY@ even if Automake is old (Bug#18415).

=== modified file 'configure.ac'
--- a/configure.ac      2014-09-07 08:46:42 +0000
+++ b/configure.ac      2014-09-10 06:38:38 +0000
@@ -4822,6 +4822,33 @@
 fi
 AC_SUBST(LIBXMENU)
 
+AC_CACHE_CHECK([for struct alignment],
+  [emacs_cv_struct_alignment],
+  [AC_COMPILE_IFELSE(
+     [AC_LANG_PROGRAM([[#include <stddef.h>
+                       struct __attribute__ ((aligned (8))) s { char c; };
+                       struct t { char c; struct s s; };
+                       char verify[offsetof (struct t, s) == 8 ? 1 : -1];
+                     ]])],
+     [emacs_cv_struct_alignment=yes],
+     [emacs_cv_struct_alignment=no])])
+if test "$emacs_cv_struct_alignment" = yes; then
+  AC_DEFINE([HAVE_STRUCT_ATTRIBUTE_ALIGNED], 1,
+    [Define to 1 if 'struct __attribute__ ((aligned (N)))' aligns the
+     structure to an N-byte boundary.])
+fi
+
+AC_CACHE_CHECK([for statement expressions],
+  [emacs_cv_statement_expressions],
+  [AC_COMPILE_IFELSE(
+     [AC_LANG_PROGRAM([], [[return ({ int x = 5; x-x; });]])],
+     [emacs_cv_statement_expressions=yes],
+     [emacs_cv_statement_expressions=no])])
+if test "$emacs_cv_statement_expressions" = yes; then
+  AC_DEFINE([HAVE_STATEMENT_EXPRESSIONS], 1,
+    [Define to 1 if statement expressions work.])
+fi
+
 if test "${GNU_MALLOC}" = "yes" ; then
   AC_DEFINE(GNU_MALLOC, 1,
            [Define to 1 if you want to use the GNU memory allocator.])

=== modified file 'src/ChangeLog'
--- a/src/ChangeLog     2014-09-09 11:43:22 +0000
+++ b/src/ChangeLog     2014-09-10 06:38:38 +0000
@@ -1,3 +1,30 @@
+2014-09-10  Paul Eggert  <address@hidden>
+
+       Improve the experimental local and scoped allocation.
+       * alloc.c (local_string_init, local_vector_init):
+       New functions, defined if USE_LOCAL_ALLOCATORS.
+       Mostly, these are moved here from lisp.h, as it's not
+       clear it's worth making them inline.
+       * lisp.h (USE_STACK_LISP_OBJECTS): Default to false.
+       (GCALIGNED): Depend on HAVE_STRUCT_ATTRIBUTE_ALIGNED and
+       USE_STACK_LISP_OBJECTS, not on a laundry list.
+       (local_string_init, local_vector_init): New decls.
+       (union Aligned_Cons): New type.
+       (scoped_cons): Use it.  Give up on the char trick, as it's a too
+       much of a maintenance hassle; if someone wants this speedup
+       they'll just need to convince their compiler to align properly.
+       Conversely, use the speedup if struct Lisp_Cons happens to
+       be aligned even without a directive.  Better yet, help it along
+       by using union Aligned_Cons rather than struct Lisp_Cons.
+       (pointer_valid_for_lisp_object): Remove.  This check is not
+       necessary, since make_lisp_ptr is already doing it.  All uses removed.
+       (local_vector_init, local_string_init): Move to alloc.c.
+       (build_local_vector): Remove this awkward macro, replacing with ...
+       (make_local_vector): New macro, which acts more like a function.
+       Use statement expressions and use __COUNTER__ to avoid macro
+       capture.  Fall back on functions if these features are not supported.
+       (build_local_string, make_local_string): Likewise.
+
 2014-09-09  Dmitry Antipov  <address@hidden>
 
        * xterm.c (x_term_init): Consolidate duplicated code.

=== modified file 'src/alloc.c'
--- a/src/alloc.c       2014-09-09 11:43:22 +0000
+++ b/src/alloc.c       2014-09-10 06:38:38 +0000
@@ -2226,6 +2226,32 @@
   return val;
 }
 
+#ifdef USE_LOCAL_ALLOCATORS
+
+/* Initialize the string S from DATA and SIZE.  S must be followed by
+   SIZE + 1 bytes of memory that can be used.  Return S tagged as a
+   Lisp object.  */
+
+Lisp_Object
+local_string_init (struct Lisp_String *s, char const *data, ptrdiff_t size)
+{
+  unsigned char *data_copy = (unsigned char *) (s + 1);
+  parse_str_as_multibyte ((unsigned char const *) data,
+                         size, &s->size, &s->size_byte);
+  if (size == s->size || size != s->size_byte)
+    {
+      s->size = size;
+      s->size_byte = -1;
+    }
+  s->intervals = NULL;
+  s->data = data_copy;
+  memcpy (data_copy, data, size);
+  data_copy[size] = '\0';
+  return make_lisp_ptr (s, Lisp_String);
+}
+
+#endif
+
 
 /* Make an unibyte string from LENGTH bytes at CONTENTS.  */
 
@@ -3288,6 +3314,22 @@
   return vector;
 }
 
+#ifdef USE_LOCAL_ALLOCATORS
+
+/* Initialize V with LENGTH objects each with value INIT,
+   and return it tagged as a Lisp Object.  */
+
+INLINE Lisp_Object
+local_vector_init (struct Lisp_Vector *v, ptrdiff_t length, Lisp_Object init)
+{
+  v->header.size = length;
+  for (ptrdiff_t i = 0; i < length; i++)
+    v->contents[i] = init;
+  return make_lisp_ptr (v, Lisp_Vectorlike);
+}
+
+#endif
+
 
 DEFUN ("vector", Fvector, Svector, 0, MANY, 0,
        doc: /* Return a newly created vector with specified arguments as 
elements.

=== modified file 'src/lisp.h'
--- a/src/lisp.h        2014-09-09 11:43:22 +0000
+++ b/src/lisp.h        2014-09-10 06:38:38 +0000
@@ -298,12 +298,14 @@
 # endif
 #endif
 
-/* Stolen from gnulib.  */
-#if (__GNUC__ || __HP_cc || __HP_aCC || __IBMC__       \
-     || __IBMCPP__ || __ICC || 0x5110 <= __SUNPRO_C)
-#define GCALIGNED __attribute__ ((aligned (GCALIGNMENT)))
+#ifndef USE_STACK_LISP_OBJECTS
+# define USE_STACK_LISP_OBJECTS false
+#endif
+
+#if defined HAVE_STRUCT_ATTRIBUTE_ALIGNED && USE_STACK_LISP_OBJECTS
+# define GCALIGNED __attribute__ ((aligned (GCALIGNMENT)))
 #else
-#define GCALIGNED /* empty */
+# define GCALIGNED /* empty */
 #endif
 
 /* Some operations are so commonly executed that they are implemented
@@ -3685,6 +3687,8 @@
 extern Lisp_Object bool_vector_fill (Lisp_Object, Lisp_Object);
 extern _Noreturn void string_overflow (void);
 extern Lisp_Object make_string (const char *, ptrdiff_t);
+extern Lisp_Object local_string_init (struct Lisp_String *, char const *,
+                                     ptrdiff_t);
 extern Lisp_Object make_formatted_string (char *, const char *, ...)
   ATTRIBUTE_FORMAT_PRINTF (2, 3);
 extern Lisp_Object make_unibyte_string (const char *, ptrdiff_t);
@@ -3773,6 +3777,8 @@
 extern struct window *allocate_window (void);
 extern struct frame *allocate_frame (void);
 extern struct Lisp_Process *allocate_process (void);
+extern Lisp_Object local_vector_init (struct Lisp_Vector *, ptrdiff_t,
+                                     Lisp_Object);
 extern struct terminal *allocate_terminal (void);
 extern bool gc_in_progress;
 extern bool abort_on_gc;
@@ -4546,142 +4552,109 @@
       memory_full (SIZE_MAX);                                 \
   } while (false)
 
-/* This feature is experimental and requires very careful debugging.
-   Brave user should compile with CPPFLAGS='-DUSE_STACK_LISP_OBJECTS'
+
+/* If USE_STACK_LISP_OBJECTS, define macros that and functions that
+   allocate block-scoped conses and function-scoped vectors and
+   strings.  These objects are not managed by the garbage collector,
+   so they are dangerous: passing them out of their scope (e.g., to
+   user code) results in undefined behavior.  Conversely, they have
+   better performance because GC is not involved.
+
+   This feature is experimental and requires careful debugging.
+   Brave users can compile with CPPFLAGS='-DUSE_STACK_LISP_OBJECTS'
    to get into the game.  */
 
-#ifdef USE_STACK_LISP_OBJECTS
-
-/* Use the following functions to allocate temporary (function-
-   or block-scoped) conses, vectors, and strings.  These objects
-   are not managed by GC, and passing them out of their scope
-   most likely causes an immediate crash at next GC.  */
-
-#if (__GNUC__ || __HP_cc || __HP_aCC || __IBMC__       \
-     || __IBMCPP__ || __ICC || 0x5110 <= __SUNPRO_C)
-
-/* Allocate temporary block-scoped cons.  This version assumes
-   that stack-allocated Lisp_Cons is always aligned properly.  */
-
-#define scoped_cons(car, cdr)                                          \
-  make_lisp_ptr (&((struct Lisp_Cons) { car, { cdr } }), Lisp_Cons)
-
-#else /* not __GNUC__ etc... */
-
-/* Helper function for an alternate scoped cons, see below.  */                
                     
-
-INLINE Lisp_Object
-scoped_cons_init (void *ptr, Lisp_Object x, Lisp_Object y)
+/* A struct Lisp_Cons inside a union that is no larger and may be
+   better-aligned.  */
+
+union Aligned_Cons
 {
-  struct Lisp_Cons *c = (struct Lisp_Cons *)
-    (((uintptr_t) ptr + (GCALIGNMENT - 1)) & ~(GCALIGNMENT - 1));
-  c->car = x;
-  c->u.cdr = y;
-  return make_lisp_ptr (c, Lisp_Cons);
-}
+  struct Lisp_Cons s;
+  double d; intmax_t i; void *p;
+};
+verify (sizeof (struct Lisp_Cons) == sizeof (union Aligned_Cons));
 
-/* This version uses explicit alignment.  */
+/* Allocate a block-scoped cons.  */
 
 #define scoped_cons(car, cdr)                                          \
-  scoped_cons_init ((char[sizeof (struct Lisp_Cons)                    \
-                         + (GCALIGNMENT - 1)]) {}, (car), (cdr))
-
-#endif /* __GNUC__ etc... */
+   ((USE_STACK_LISP_OBJECTS                                            \
+     && alignof (union Aligned_Cons) % GCALIGNMENT == 0)               \
+    ? make_lisp_ptr (&((union Aligned_Cons) {{car, {cdr}}}).s, Lisp_Cons) \
+    : Fcons (car, cdr))
 
 /* Convenient utility macros similar to listX functions.  */
 
-#define scoped_list1(x) scoped_cons (x, Qnil)
-#define scoped_list2(x, y) scoped_cons (x, scoped_cons (y, Qnil))
-#define scoped_list3(x, y, z)                                  \
-  scoped_cons (x, scoped_cons (y, scoped_cons (z, Qnil)))
-
-/* True if Lisp_Object may be placed at P.  Used only
-   under ENABLE_CHECKING and optimized away otherwise.  */
-
-INLINE bool
-pointer_valid_for_lisp_object (void *p)
-{
-  uintptr_t v = (uintptr_t) p;
-  return !(USE_LSB_TAG ? (v & ~VALMASK) : v >> VALBITS);
-}
-
-/* Helper function for build_local_vector, see below.  */
-
-INLINE Lisp_Object
-local_vector_init (uintptr_t addr, ptrdiff_t length, Lisp_Object init)
-{
-  ptrdiff_t i;
-  struct Lisp_Vector *v = (struct Lisp_Vector *) addr;
-
-  eassert (pointer_valid_for_lisp_object (v));
-  v->header.size = length;
-  for (i = 0; i < length; i++)
-    v->contents[i] = init;
-  return make_lisp_ptr (v, Lisp_Vectorlike);
-}
-
-/* If size permits, create temporary function-scoped vector OBJ of
-   length SIZE, with each element being INIT.  Otherwise create
-   regular GC-managed vector.  */
-
-#define build_local_vector(obj, size, init)                            \
-  (MAX_ALLOCA < (size) * word_size + header_size                       \
-   ? obj = Fmake_vector (make_number (size), (init))                   \
-   : (obj = XIL ((uintptr_t) alloca                                    \
-                ((size) * word_size + header_size)),                   \
-      obj = local_vector_init ((uintptr_t) XLI (obj), (size), (init))))
-
-/* Helper function for make_local_string, see below.  */
-
-INLINE Lisp_Object
-local_string_init (uintptr_t addr, const char *data, ptrdiff_t size)
-{
-  ptrdiff_t nchars, nbytes;
-  struct Lisp_String *s = (struct Lisp_String *) addr;
-
-  eassert (pointer_valid_for_lisp_object (s));
-  parse_str_as_multibyte ((const unsigned char *) data,
-                         size, &nchars, &nbytes);
-  s->data = (unsigned char *) (addr + sizeof *s);
-  s->intervals = NULL;
-  memcpy (s->data, data, size);
-  s->data[size] = '\0';
-  if (size == nchars || size != nbytes)
-    s->size = size, s->size_byte = -1;
-  else
-    s->size = nchars, s->size_byte = nbytes;
-  return make_lisp_ptr (s, Lisp_String);
-}
-
-/* If size permits, create temporary function-scoped string OBJ
-   with contents DATA of length NBYTES.  Otherwise create regular
-   GC-managed string.  */
-
-#define make_local_string(obj, data, nbytes)                           \
-  (MAX_ALLOCA < (nbytes) + sizeof (struct Lisp_String)                 \
-   ? obj = make_string ((data), (nbytes))                              \
-   : (obj = XIL ((uintptr_t) alloca                                    \
-                ((nbytes) + sizeof (struct Lisp_String))),             \
-      obj = local_string_init ((uintptr_t) XLI (obj), data, nbytes)))
-
-/* We want an interface similar to make_string and build_string, right?  */
-
-#define build_local_string(obj, data)          \
-  make_local_string (obj, data, strlen (data))
-
-#else /* not USE_STACK_LISP_OBJECTS */
-
-#define scoped_cons(x, y) Fcons ((x), (y))
-#define scoped_list1(x) list1 (x)
-#define scoped_list2(x, y) list2 ((x), (y))
-#define scoped_list3(x, y, z) list3 ((x), (y), (z))
-#define build_local_vector(obj, size, init)            \
-  (obj = Fmake_vector (make_number ((size), (init))))
-#define make_local_string(obj, data, nbytes)   \
-  (obj = make_string ((data), (nbytes)))
-#define build_local_string(obj, data) (obj = build_string (data))
-
-#endif /* USE_STACK_LISP_OBJECTS */
+#if USE_STACK_LISP_OBJECTS
+# define scoped_list1(x) scoped_cons (x, Qnil)
+# define scoped_list2(x, y) scoped_cons (x, scoped_list1 (y))
+# define scoped_list3(x, y, z) scoped_cons (x, scoped_list2 (y, z))
+#else
+# define scoped_list1(x) list1 (x)
+# define scoped_list2(x, y) list2 (x, y)
+# define scoped_list3(x, y, z) list3 (x, y, z)
+#endif
+
+#if USE_STACK_LISP_OBJECTS && HAVE_STATEMENT_EXPRESSIONS && defined __COUNTER__
+
+# define USE_LOCAL_ALLOCATORS
+
+/* Return a function-scoped vector of length SIZE, with each element
+   being INIT.  */
+
+# define make_local_vector(size, init) \
+    make_local_vector_n (size, init, __COUNTER__)
+# define make_local_vector_n(size_arg, init_arg, n)                    \
+    ({                                                                 \
+       ptrdiff_t size##n = size_arg;                                   \
+       Lisp_Object init##n = init_arg;                                 \
+       Lisp_Object vec##n;                                             \
+       if (size##n <= (MAX_ALLOCA - header_size) / word_size)          \
+        {                                                              \
+          void *ptr##n = alloca (size##n * word_size + header_size);   \
+          vec##n = local_vector_init (ptr##n, size##n, init##n);       \
+        }                                                              \
+       else                                                            \
+        vec##n = Fmake_vector (make_number (size##n), init##n);        \
+       vec##n;                                                         \
+    })
+
+/* Return a function-scoped string with contents DATA and length NBYTES.  */
+
+# define make_local_string(data, nbytes) \
+    make_local_string (data, nbytes, __COUNTER__)
+# define make_local_string_n(data_arg, nbytes_arg, n)                  \
+    ({                                                                 \
+       char const *data##n = data_arg;                                 \
+       ptrdiff_t nbytes##n = nbytes_arg;                               \
+       Lisp_Object string##n;                                          \
+       if (nbytes##n <= MAX_ALLOCA - sizeof (struct Lisp_String) - 1)  \
+        {                                                              \
+          struct Lisp_String *ptr##n                                   \
+            = alloca (sizeof (struct Lisp_String) + 1 + nbytes);       \
+          string##n = local_string_init (ptr##n, data##n, nbytes##n);  \
+        }                                                              \
+       else                                                            \
+        string##n = make_string (data##n, nbytes##n);                  \
+       string##n;                                                      \
+    })
+
+/* Return a function-scoped string with contents DATA.  */
+
+# define build_local_string(data) build_local_string_n (data, __COUNTER__)
+# define build_local_string_n(data_arg, n)                     \
+    ({                                                         \
+       char const *data##n = data_arg;                         \
+       make_local_string (data##n, strlen (data##n));          \
+    })
+
+#else
+
+/* Safer but slower implementations.  */
+# define make_local_vector(size, init) Fmake_vector (make_number (size), init)
+# define make_local_string(data, nbytes) make_string (data, nbytes)
+# define build_local_string(data) build_string (data)
+#endif
+
 
 /* Loop over all tails of a list, checking for cycles.
    FIXME: Make tortoise and n internal declarations.


reply via email to

[Prev in Thread] Current Thread [Next in Thread]