]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
Added support for x86 intel AES instruction acceleration if detected.
authorNikos Mavrogiannopoulos <nmav@gnutls.org>
Sun, 10 Apr 2011 06:55:41 +0000 (08:55 +0200)
committerNikos Mavrogiannopoulos <nmav@gnutls.org>
Sun, 10 Apr 2011 07:55:27 +0000 (09:55 +0200)
configure.ac
lib/Makefile.am
lib/accelerated/Makefile.am [new file with mode: 0644]
lib/accelerated/accelerated.c [new file with mode: 0644]
lib/accelerated/accelerated.h [new file with mode: 0644]
lib/accelerated/aes-x86.c [new file with mode: 0644]
lib/accelerated/aes-x86.h [new file with mode: 0644]
lib/accelerated/x86.h [new file with mode: 0644]
lib/gnutls_global.c
m4/gcc.m4 [new file with mode: 0644]

index f451464d72c3377dc720a21d31a5d9d9c2ee18fb..941fa9f6863e234e2ddbe4d526103923d0abfdae 100644 (file)
@@ -91,85 +91,27 @@ AC_CHECK_TYPES(uint,,, [
 # include <sys/types.h>
 ])
 
-# For Guile bindings.
-opt_guile_bindings=yes
-AC_MSG_CHECKING([whether building Guile bindings])
-AC_ARG_ENABLE(guile,
-       AS_HELP_STRING([--enable-guile], [build GNU Guile bindings]),
-opt_guile_bindings=$enableval)
-AC_MSG_RESULT($opt_guile_bindings)
-
-AC_ARG_WITH([--with-guile-site-dir],
-  [AS_HELP_STRING([--with-guile-site-dir],
-     [use the given directory as the Guile site (use with care)])])
-
-if test "$opt_guile_bindings" = "yes"; then
-   AC_MSG_RESULT([***
-*** Detecting GNU Guile...
-])
-
-   AC_PATH_PROG([guile_snarf], [guile-snarf])
-   if test "x$guile_snarf" = "x"; then
-      AC_MSG_WARN([`guile-snarf' from Guile 1.8 not found.  Guile bindings not built.])
-      opt_guile_bindings=no
-   else
-      GUILE_PROGS
-      GUILE_FLAGS
-
-      save_CFLAGS="$CFLAGS"
-      save_LIBS="$LIBS"
-      CFLAGS="$CFLAGS $GUILE_CFLAGS"
-      LIBS="$LIBS $GUILE_LDFLAGS"
-      AC_MSG_CHECKING([whether GNU Guile is recent enough])
-      AC_LINK_IFELSE(AC_LANG_CALL([], [scm_from_locale_string]),
-        [], [opt_guile_bindings=no])
-      CFLAGS="$save_CFLAGS"
-      LIBS="$save_LIBS"
-
-      if test "$opt_guile_bindings" = "yes"; then
-        AC_MSG_RESULT([yes])
-       case "x$with_guile_site_dir" in 
-            xno)
-               # Use the default $(GUILE_SITE).
-               GUILE_SITE_DIR
-               ;;
-            x|xyes)
-               # Automatically derive $(GUILE_SITE) from $(pkgdatadir).  This
-               # hack is used to allow `distcheck' to work (see
-               # `DISTCHECK_CONFIGURE_FLAGS' in the top-level `Makefile.am').
-               GUILE_SITE="\$(datadir)/guile/site"
-               AC_SUBST(GUILE_SITE)
-               ;;
-            *)
-               # Use the user-specified directory as $(GUILE_SITE).
-               GUILE_SITE="$with_guile_site_dir"
-               AC_SUBST(GUILE_SITE)
-               ;;
-       esac
-        AC_MSG_CHECKING([whether gcc supports -fgnu89-inline])
-        _gcc_cflags_save="$CFLAGS"
-        CFLAGS="${CFLAGS} -fgnu89-inline"
-        AC_COMPILE_IFELSE([AC_LANG_PROGRAM([])],
-                          gnu89_inline=yes, gnu89_inline=no)
-        AC_MSG_RESULT($gnu89_inline)
-        CFLAGS="$_gcc_cflags_save"
+AC_ARG_ENABLE(hardware-acceleration,
+  AS_HELP_STRING([--disable-hardware-acceleration], [unconditionally disable hardware acceleration]),
+    use_accel=$enableval, use_accel=yes)
+hw_accel=none
+
+if test "$use_accel" != "no"; then
+case $host_cpu in
+  i?86 | x86_64 | amd64)
+    GCC_FLAG_ADD([-maes -mpclmul],[X86])
+
+    if test "x$X86" = "xyes";then
+      hw_accel="x86"
+    fi
+  ;;
+  *)
+  ;;
+esac
 
-       # Optional Guile functions.
-       save_CFLAGS="$CFLAGS"
-       save_LIBS="$LIBS"
-       CFLAGS="$CFLAGS $GUILE_CFLAGS"
-       LIBS="$LIBS $GUILE_LDFLAGS"
-       AC_CHECK_FUNCS([scm_gc_malloc_pointerless])
-       CFLAGS="$save_CFLAGS"
-       LIBS="$save_LIBS"
-      else
-        AC_MSG_RESULT([no])
-        AC_MSG_WARN([A sufficiently recent GNU Guile not found.  Guile bindings not built.])
-        opt_guile_bindings=no
-      fi
-   fi
 fi
-AM_CONDITIONAL(HAVE_GUILE, test "$opt_guile_bindings" = "yes")
+
+AM_CONDITIONAL(TRY_X86_OPTIMIZATIONS, test "$X86" = "yes")
 AM_CONDITIONAL(HAVE_GCC_GNU89_INLINE_OPTION, test "$gnu89_inline" = "yes"])
 AM_CONDITIONAL(HAVE_GCC, test "$GCC" = "yes")
 
@@ -310,6 +252,87 @@ AC_SUBST([WSTACK_CFLAGS])
 AC_SUBST([WARN_CFLAGS])
 AM_CONDITIONAL(ENABLE_CXX, test "$use_cxx" != "no")
 
+# For Guile bindings.
+opt_guile_bindings=yes
+AC_MSG_CHECKING([whether building Guile bindings])
+AC_ARG_ENABLE(guile,
+       AS_HELP_STRING([--enable-guile], [build GNU Guile bindings]),
+opt_guile_bindings=$enableval)
+AC_MSG_RESULT($opt_guile_bindings)
+
+AC_ARG_WITH([--with-guile-site-dir],
+  [AS_HELP_STRING([--with-guile-site-dir],
+     [use the given directory as the Guile site (use with care)])])
+
+if test "$opt_guile_bindings" = "yes"; then
+   AC_MSG_RESULT([***
+*** Detecting GNU Guile...
+])
+
+   AC_PATH_PROG([guile_snarf], [guile-snarf])
+   if test "x$guile_snarf" = "x"; then
+      AC_MSG_WARN([`guile-snarf' from Guile 1.8 not found.  Guile bindings not built.])
+      opt_guile_bindings=no
+   else
+      GUILE_PROGS
+      GUILE_FLAGS
+
+      save_CFLAGS="$CFLAGS"
+      save_LIBS="$LIBS"
+      CFLAGS="$CFLAGS $GUILE_CFLAGS"
+      LIBS="$LIBS $GUILE_LDFLAGS"
+      AC_MSG_CHECKING([whether GNU Guile is recent enough])
+      AC_LINK_IFELSE(AC_LANG_CALL([], [scm_from_locale_string]),
+        [], [opt_guile_bindings=no])
+      CFLAGS="$save_CFLAGS"
+      LIBS="$save_LIBS"
+
+      if test "$opt_guile_bindings" = "yes"; then
+        AC_MSG_RESULT([yes])
+       case "x$with_guile_site_dir" in 
+            xno)
+               # Use the default $(GUILE_SITE).
+               GUILE_SITE_DIR
+               ;;
+            x|xyes)
+               # Automatically derive $(GUILE_SITE) from $(pkgdatadir).  This
+               # hack is used to allow `distcheck' to work (see
+               # `DISTCHECK_CONFIGURE_FLAGS' in the top-level `Makefile.am').
+               GUILE_SITE="\$(datadir)/guile/site"
+               AC_SUBST(GUILE_SITE)
+               ;;
+            *)
+               # Use the user-specified directory as $(GUILE_SITE).
+               GUILE_SITE="$with_guile_site_dir"
+               AC_SUBST(GUILE_SITE)
+               ;;
+       esac
+        AC_MSG_CHECKING([whether gcc supports -fgnu89-inline])
+        _gcc_cflags_save="$CFLAGS"
+        CFLAGS="${CFLAGS} -fgnu89-inline"
+        AC_COMPILE_IFELSE([AC_LANG_PROGRAM([])],
+                          gnu89_inline=yes, gnu89_inline=no)
+        AC_MSG_RESULT($gnu89_inline)
+        CFLAGS="$_gcc_cflags_save"
+
+       # Optional Guile functions.
+       save_CFLAGS="$CFLAGS"
+       save_LIBS="$LIBS"
+       CFLAGS="$CFLAGS $GUILE_CFLAGS"
+       LIBS="$LIBS $GUILE_LDFLAGS"
+       AC_CHECK_FUNCS([scm_gc_malloc_pointerless])
+       CFLAGS="$save_CFLAGS"
+       LIBS="$save_LIBS"
+      else
+        AC_MSG_RESULT([no])
+        AC_MSG_WARN([A sufficiently recent GNU Guile not found.  Guile bindings not built.])
+        opt_guile_bindings=no
+      fi
+   fi
+fi
+AM_CONDITIONAL(HAVE_GUILE, test "$opt_guile_bindings" = "yes")
+
+
 LIBGNUTLS_EXTRA_LIBS="-L${libdir} -lgnutls-extra $LZO_LIBS $LIBGNUTLS_LIBS"
 LIBGNUTLS_EXTRA_CFLAGS="-I${includedir}"
 AC_SUBST(LIBGNUTLS_EXTRA_LIBS)
@@ -376,6 +399,7 @@ AC_CONFIG_FILES([
   lib/gcrypt/Makefile
   lib/nettle/Makefile
   tests/suite/Makefile
+  lib/accelerated/Makefile
 ])
 
 AC_OUTPUT
@@ -386,6 +410,7 @@ AC_MSG_NOTICE([summary of build options:
   Host type:        ${host}
   Install prefix:   ${prefix}
   Compiler:         ${CC}
+  CFlags:           ${CFLAGS}
   Warning flags:    errors: ${WERROR_CFLAGS} warnings: ${WARN_CFLAGS}
   Library types:    Shared=${enable_shared}, Static=${enable_static}
   Valgrind:         $opt_valgrind_tests ${VALGRIND}
@@ -393,5 +418,6 @@ AC_MSG_NOTICE([summary of build options:
   C++ library:      $use_cxx
   OpenSSL library:  $enable_openssl
   /dev/crypto:      $enable_cryptodev
+  Hardware accel:   $hw_accel
   Crypto library:   $cryptolib
 ])
index f8dac6716a904f6fcf154e832c5cefefd51400b7..982ae6ea48d508293b02591aec4088c9807d17b3 100644 (file)
@@ -23,7 +23,7 @@
 
 ACLOCAL_AMFLAGS = -I ../m4 -I ../gl/m4
 
-SUBDIRS = includes x509
+SUBDIRS = includes x509 accelerated
 if ENABLE_MINITASN1
 SUBDIRS += minitasn1
 endif
@@ -121,6 +121,7 @@ libgnutls_la_LDFLAGS = -no-undefined \
        -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE)
 
 libgnutls_la_LIBADD = ../gl/libgnu.la x509/libgnutls_x509.la \
+       accelerated/libaccelerated.la \
        $(LTLIBZ)  $(LTLIBINTL) $(LIBSOCKET)
 
 if ENABLE_OPENPGP
diff --git a/lib/accelerated/Makefile.am b/lib/accelerated/Makefile.am
new file mode 100644 (file)
index 0000000..bfe9a16
--- /dev/null
@@ -0,0 +1,40 @@
+## Process this file with automake to produce Makefile.in
+# Copyright (C) 2011 Free Software
+# Foundation, Inc.
+#
+# Author: Nikos Mavrogiannopoulos
+#
+# This file is part of GNUTLS.
+#
+# The GNUTLS library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public License
+# as published by the Free Software Foundation; either version 2.1 of
+# the License, or (at your option) any later version.
+#
+# The GNUTLS library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with the GNUTLS library; if not, write to the Free
+# Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA
+
+AM_CFLAGS = $(WERROR_CFLAGS) $(WSTACK_CFLAGS) $(WARN_CFLAGS)
+AM_CPPFLAGS = \
+       -I$(srcdir)/../../gl            \
+       -I$(srcdir)/../includes         \
+       -I$(builddir)/../includes       \
+       -I$(srcdir)/..
+
+noinst_LTLIBRARIES = libaccelerated.la
+
+EXTRA_DIST = x86.h aes-x86.h accelerated.h
+libaccelerated_la_SOURCES = accelerated.c
+
+if TRY_X86_OPTIMIZATIONS
+AM_CFLAGS += -DTRY_X86_OPTIMIZATIONS
+libaccelerated_la_SOURCES += aes-x86.c
+endif
+
diff --git a/lib/accelerated/accelerated.c b/lib/accelerated/accelerated.c
new file mode 100644 (file)
index 0000000..91e7040
--- /dev/null
@@ -0,0 +1,14 @@
+#include <accelerated.h>
+#ifdef TRY_X86_OPTIMIZATIONS
+# include <aes-x86.h>
+#endif
+
+void _gnutls_register_accel_crypto(void)
+{
+
+#ifdef TRY_X86_OPTIMIZATIONS
+  register_x86_crypto ();
+#endif
+
+  return;
+}
diff --git a/lib/accelerated/accelerated.h b/lib/accelerated/accelerated.h
new file mode 100644 (file)
index 0000000..1fccccb
--- /dev/null
@@ -0,0 +1 @@
+void _gnutls_register_accel_crypto(void);
diff --git a/lib/accelerated/aes-x86.c b/lib/accelerated/aes-x86.c
new file mode 100644 (file)
index 0000000..1b705ed
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2011, Free Software Foundation
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA
+ *
+ * The following code is an implementation of the AES-128-CBC cipher
+ * using intel's AES instruction set. It is based on Intel reference
+ * code.
+ */
+
+#include <gnutls_errors.h>
+#include <gnutls_int.h>
+#include <gnutls/crypto.h>
+#include <gnutls_errors.h>
+#include <wmmintrin.h>
+#include <aes-x86.h>
+#include <x86.h>
+
+struct aes_ctx {
+       uint8_t iv[16];
+       uint8_t key[16*10];
+       size_t keysize;
+};
+
+static int
+aes_cipher_init (gnutls_cipher_algorithm_t algorithm, void **_ctx)
+{
+  struct aes_ctx *ctx;
+  
+  if (algorithm != GNUTLS_CIPHER_AES_128_CBC)
+    return GNUTLS_E_INVALID_REQUEST;
+
+  *_ctx = gnutls_calloc (1, sizeof (struct aes_ctx));
+  if (*_ctx == NULL)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_MEMORY_ERROR;
+    }
+
+  ctx = *_ctx;
+
+  return 0;
+}
+
+inline static __m128i aes128_assist (__m128i temp1, __m128i temp2)
+{
+__m128i temp3;
+       temp2 = _mm_shuffle_epi32 (temp2 ,0xff);
+       temp3 = _mm_slli_si128 (temp1, 0x4);
+       temp1 = _mm_xor_si128 (temp1, temp3);
+       temp3 = _mm_slli_si128 (temp3, 0x4);
+       temp1 = _mm_xor_si128 (temp1, temp3);
+       temp3 = _mm_slli_si128 (temp3, 0x4);
+       temp1 = _mm_xor_si128 (temp1, temp3);
+       temp1 = _mm_xor_si128 (temp1, temp2);
+
+       return temp1;
+}
+
+static int
+aes_cipher_setkey (void *_ctx, const void *userkey, size_t keysize)
+{
+struct aes_ctx *ctx = _ctx;
+__m128i temp1, temp2;
+__m128i *Key_Schedule = (__m128i*)ctx->key;
+
+       temp1 = _mm_loadu_si128((__m128i*)userkey);
+       Key_Schedule[0] = temp1;
+       temp2 = _mm_aeskeygenassist_si128 (temp1 ,0x1);
+       temp1 = aes128_assist(temp1, temp2);
+       Key_Schedule[1] = temp1;
+       temp2 = _mm_aeskeygenassist_si128 (temp1,0x2);
+       temp1 = aes128_assist(temp1, temp2);
+       Key_Schedule[2] = temp1;
+       temp2 = _mm_aeskeygenassist_si128 (temp1,0x4);
+       temp1 = aes128_assist(temp1, temp2);
+       Key_Schedule[3] = temp1;
+       temp2 = _mm_aeskeygenassist_si128 (temp1,0x8);
+       temp1 = aes128_assist(temp1, temp2);
+       Key_Schedule[4] = temp1;
+       temp2 = _mm_aeskeygenassist_si128 (temp1,0x10);
+       temp1 = aes128_assist(temp1, temp2);
+       Key_Schedule[5] = temp1;
+       temp2 = _mm_aeskeygenassist_si128 (temp1,0x20);
+       temp1 = aes128_assist(temp1, temp2);
+       Key_Schedule[6] = temp1;
+       temp2 = _mm_aeskeygenassist_si128 (temp1,0x40);
+       temp1 = aes128_assist(temp1, temp2);
+       Key_Schedule[7] = temp1;
+       temp2 = _mm_aeskeygenassist_si128 (temp1,0x80);
+       temp1 = aes128_assist(temp1, temp2);
+       Key_Schedule[8] = temp1;
+       temp2 = _mm_aeskeygenassist_si128 (temp1,0x1b);
+       temp1 = aes128_assist(temp1, temp2);
+       Key_Schedule[9] = temp1;
+       temp2 = _mm_aeskeygenassist_si128 (temp1,0x36);
+       temp1 = aes128_assist(temp1, temp2);
+       Key_Schedule[10] = temp1;
+
+       ctx->keysize = keysize;
+
+       return 0;
+}
+
+static int
+aes_setiv (void *_ctx, const void *iv, size_t iv_size)
+{
+  struct aes_ctx *ctx = _ctx;
+
+  memcpy (ctx->iv, iv, 16);
+
+  return 0;
+}
+
+#define AES_128_ROUNDS 10
+
+static int
+aes_encrypt (void *_ctx, const void *plain, size_t plainsize,
+                   void *encr, size_t length)
+{
+struct aes_ctx *ctx = _ctx;
+__m128i feedback,data;
+int i,j;
+
+       feedback=_mm_loadu_si128 ((__m128i*)ctx->iv);
+       for(i=0; i < length; i++) {
+               data = _mm_loadu_si128 (&((__m128i*)plain)[i]);
+               feedback = _mm_xor_si128 (data,feedback);
+               feedback = _mm_xor_si128 (feedback,((__m128i*)ctx->key)[0]);
+    
+               for(j=1; j <AES_128_ROUNDS; j++)
+                       feedback = _mm_aesenc_si128 (feedback,((__m128i*)ctx->key)[j]);
+
+               feedback = _mm_aesenclast_si128 (feedback,((__m128i*)ctx->key)[j]);
+               _mm_storeu_si128 (&((__m128i*)encr)[i],feedback);
+       }
+
+  return 0;
+}
+
+static int
+aes_decrypt (void *_ctx, const void *encr, size_t encrsize,
+                   void *plain, size_t length)
+{
+struct aes_ctx *ctx = _ctx;
+__m128i data,feedback,last_in;
+int i,j;
+
+       feedback=_mm_loadu_si128 ((__m128i*)ctx->iv);
+
+       for(i=0; i < length; i++) {
+               last_in=_mm_loadu_si128 (&((__m128i*)encr)[i]);
+               data = _mm_xor_si128 (last_in,((__m128i*)ctx->key)[0]);
+    
+               for(j=1; j <AES_128_ROUNDS; j++)
+                       data = _mm_aesdec_si128 (data,((__m128i*)ctx->key)[j]);
+
+               data = _mm_aesdeclast_si128 (data,((__m128i*)ctx->key)[j]);
+               data = _mm_xor_si128 (data,feedback);
+               _mm_storeu_si128 (&((__m128i*)plain)[i],data);
+               feedback=last_in;
+       }
+
+       return 0;
+}
+
+static void
+aes_deinit (void *_ctx)
+{
+  gnutls_free (_ctx);
+}
+
+static const gnutls_crypto_cipher_st cipher_struct = {
+  .init = aes_cipher_init,
+  .setkey = aes_cipher_setkey,
+  .setiv = aes_setiv,
+  .encrypt = aes_encrypt,
+  .decrypt = aes_decrypt,
+  .deinit = aes_deinit,
+};
+
+static unsigned check_optimized_aes(void)
+{
+unsigned int a,b,c,d;
+  cpuid(1, a,b,c,d);
+  
+  return (c & 0x2000000);
+}
+
+void
+register_x86_crypto (void)
+{
+int ret;
+       if (check_optimized_aes()) {
+               fprintf(stderr, "Intel AES accelerator was detected\n");
+               ret = gnutls_crypto_single_cipher_register (GNUTLS_CIPHER_AES_128_CBC, 90, &cipher_struct);
+               if (ret < 0)
+               {
+                 gnutls_assert ();
+               }
+       }
+
+       return;
+}
diff --git a/lib/accelerated/aes-x86.h b/lib/accelerated/aes-x86.h
new file mode 100644 (file)
index 0000000..40d6a0c
--- /dev/null
@@ -0,0 +1 @@
+void register_x86_crypto (void);
diff --git a/lib/accelerated/x86.h b/lib/accelerated/x86.h
new file mode 100644 (file)
index 0000000..c344283
--- /dev/null
@@ -0,0 +1,3 @@
+#define cpuid(func,ax,bx,cx,dx)\
+  __asm__ __volatile__ ("cpuid":\
+  "=a" (ax), "=b" (bx), "=c" (cx), "=d" (dx) : "a" (func));
index 1a59bdfc19db18d19b849ad47797178cdddab28b..3c7f263968d5cd7adc1e03f8360ee30b8461fc3b 100644 (file)
@@ -33,6 +33,7 @@
 #include <gnutls_extensions.h>  /* for _gnutls_ext_init */
 #include <gnutls_cryptodev.h>
 #include <locks.h>
+#include <accelerated/accelerated.h>
 
 #include "sockets.h"
 #include "gettext.h"
@@ -187,6 +188,8 @@ gnutls_global_init (void)
       return GNUTLS_E_CRYPTO_INIT_FAILED;
     }
 
+  _gnutls_register_accel_crypto();
+
   /* initialize ASN.1 parser
    * This should not deal with files in the final
    * version.
diff --git a/m4/gcc.m4 b/m4/gcc.m4
new file mode 100644 (file)
index 0000000..ad40ada
--- /dev/null
+++ b/m4/gcc.m4
@@ -0,0 +1,28 @@
+# gcc.m4 serial 2
+dnl Copyright (C) 2008-2011 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl Based on code by Simon Josefsson
+
+# GCC_FLAG_ADD(PARAMETER, [ACTION])
+# ------------------------------------------------
+# Adds parameter to CFLAGS if the compiler supports it.  For example,
+# GCC_FLAG_ADD([-maes], [VAR]).
+AC_DEFUN([GCC_FLAG_ADD],
+[AS_VAR_PUSHDEF([GCC_FLAG], [gl_cv_GCC_FLAG_$1])dnl
+AC_CACHE_CHECK([whether compiler handles $1], [GCC_FLAG], [
+  save_CFLAGS="$CFLAGS"
+  CFLAGS="${CFLAGS} $1"
+  AC_PREPROC_IFELSE([AC_LANG_PROGRAM([])],
+                    [AS_VAR_SET([GCC_FLAG], [yes])],
+                    [AS_VAR_SET([GCC_FLAG], [no])])
+  CFLAGS="$save_CFLAGS"
+])
+AS_VAR_IF([GCC_FLAG], [yes], [
+m4_ifval([$2], [AC_SUBST([$2],[yes])])
+CFLAGS="${CFLAGS} $1"
+])dnl
+AS_VAR_POPDEF([GCC_FLAG])
+])