[PATCH v2] x86-64: Don't use asm statement for trunc/truncf

H.J. Lu hjl.tools@gmail.com
Fri Sep 12 12:57:01 GMT 2025


Compiler inlines trunc and truncf with SSE4.1.  But older versions of GCC
doesn't inline them with -Os:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121861

Don't use asm statement for trunc and truncf if compiler can inline them
with -Os.  It removes one register move with GCC 16:

__modff_sse41:                        __modff_sse41:
.LFB23:                               .LFB23:
   .cfi_startproc                        .cfi_startproc
   endbr64                               endbr64
   subq  $24, %rsp                       subq  $24, %rsp
   .cfi_def_cfa_offset 32                .cfi_def_cfa_offset 32
   movq  %fs:40, %rax                    movq  %fs:40, %rax
   movq  %rax, 8(%rsp)                   movq  %rax, 8(%rsp)
   xorl  %eax, %eax                      xorl  %eax, %eax
   movd  %xmm0, %eax                     movd  %xmm0, %eax
   addl  %eax, %eax                      addl  %eax, %eax
   cmpl  $-16777216, %eax                cmpl  $-16777216, %eax
   je .L7                                je .L7
                                   >     movaps   %xmm0, %xmm3
   movaps   %xmm0, %xmm4                 movaps   %xmm0, %xmm4
   movss .LC0(%rip), %xmm2         |     movss .LC0(%rip), %xmm1
   movaps   %xmm2, %xmm3           |     movaps   %xmm1, %xmm2
   andps %xmm0, %xmm2              |     roundss  $11, %xmm3, %xmm3
   roundss $11, %xmm0, %xmm1       |     subss %xmm3, %xmm4
   subss %xmm1, %xmm4              |     andps %xmm0, %xmm1
   andnps   %xmm4, %xmm3           |     andnps   %xmm4, %xmm2
   orps  %xmm3, %xmm2              |     orps  %xmm2, %xmm1
.L3:                                  .L3:
   movss %xmm1, (%rdi)             |     movss %xmm3, (%rdi)
   movq  8(%rsp), %rax                   movq  8(%rsp), %rax
   subq  %fs:40, %rax                    subq  %fs:40, %rax
   jne   .L8                             jne   .L8
   movaps   %xmm2, %xmm0           |     movaps   %xmm1, %xmm0
   addq  $24, %rsp                       addq  $24, %rsp
   .cfi_remember_state                   .cfi_remember_state
   .cfi_def_cfa_offset 8                 .cfi_def_cfa_offset 8
   ret                                   ret

Signed-off-by: H.J. Lu <hjl.tools@gmail.com>
---
 config.h.in                    |  3 ++
 sysdeps/x86/fpu/math_private.h | 24 ++++++++++------
 sysdeps/x86_64/configure       | 52 ++++++++++++++++++++++++++++++++++
 sysdeps/x86_64/configure.ac    | 31 ++++++++++++++++++++
 4 files changed, 102 insertions(+), 8 deletions(-)

diff --git a/config.h.in b/config.h.in
index 8b4077f578..af2ab31379 100644
--- a/config.h.in
+++ b/config.h.in
@@ -308,4 +308,7 @@
 /* Define if -mapxf is enabled by default on x86.  */
 #undef HAVE_X86_APX
 
+/* Define if -Os inlines trunc on x86.  */
+#undef HAVE_X86_OS_INLINE_TRUNC
+
 #endif
diff --git a/sysdeps/x86/fpu/math_private.h b/sysdeps/x86/fpu/math_private.h
index d30d580cea..610ae364f3 100644
--- a/sysdeps/x86/fpu/math_private.h
+++ b/sysdeps/x86/fpu/math_private.h
@@ -33,27 +33,35 @@ __NTH (__ieee754_atan2l (long double y, long double x))
 __extern_always_inline double
 __trunc (double x)
 {
-#ifdef __AVX__
+#if HAVE_X86_OS_INLINE_TRUNC
+  return trunc (x);
+#else
+# ifdef __AVX__
   asm ("vroundsd $11, %1, %1, %0" : "=v" (x) : "v" (x));
-#elif defined __SSE4_1__
+# elif defined __SSE4_1__
   asm ("roundsd $11, %1, %0" : "=x" (x) : "x" (x));
-#else
+# else
   x = trunc (x);
-#endif
+# endif
   return x;
+#endif
 }
 
 __extern_always_inline float
 __truncf (float x)
 {
-#ifdef __AVX__
+#if HAVE_X86_OS_INLINE_TRUNC
+  return truncf (x);
+#else
+# ifdef __AVX__
   asm ("vroundss $11, %1, %1, %0" : "=v" (x) : "v" (x));
-#elif defined __SSE4_1__
+# elif defined __SSE4_1__
   asm ("roundss $11, %1, %0" : "=x" (x) : "x" (x));
-#else
+# else
   x = truncf (x);
-#endif
+# endif
   return x;
+#endif
 }
 
 #endif
diff --git a/sysdeps/x86_64/configure b/sysdeps/x86_64/configure
index 32324f62da..9e98d020ae 100644
--- a/sysdeps/x86_64/configure
+++ b/sysdeps/x86_64/configure
@@ -289,6 +289,58 @@ fi
 config_vars="$config_vars
 have-x86-apx = $libc_cv_x86_have_apx"
 
+conftest_code="
+extern float truncf (float __x) __attribute__ ((__nothrow__,__const__));
+
+float
+tf (float x)
+{
+  return truncf (x);
+}
+"
+
+cat > conftest.c <<EOF
+$conftest_code
+EOF
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if -Os inlines trunc" >&5
+printf %s "checking if -Os inlines trunc... " >&6; }
+if test ${libc_cv_cc_x86_inline_trunc+y}
+then :
+  printf %s "(cached) " >&6
+else case e in #(
+  e)   if { ac_try='${CC-cc} $CFLAGS $CPPFLAGS -S -Os -msse4.1 conftest.c -o conftest 1>&5'
+  { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }
+  then
+
+libc_cv_cc_x86_inline_trunc=no
+if grep -E -q "roundss" conftest; then
+  libc_cv_cc_x86_inline_trunc=yes
+fi
+
+  else
+
+echo "failed to check if -Os inlines trunc."
+rm -f conftest*
+exit 1
+
+  fi ;;
+esac
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $libc_cv_cc_x86_inline_trunc" >&5
+printf "%s\n" "$libc_cv_cc_x86_inline_trunc" >&6; }
+rm -f conftest*
+if test "$libc_cv_cc_x86_inline_trunc" = yes; then
+  printf "%s\n" "#define HAVE_X86_OS_INLINE_TRUNC 1" >>confdefs.h
+
+else
+  printf "%s\n" "#define HAVE_X86_OS_INLINE_TRUNC 0" >>confdefs.h
+
+fi
+
 libc_cv_support_sframe=yes
 
 test -n "$critic_missing" && as_fn_error $? "
diff --git a/sysdeps/x86_64/configure.ac b/sysdeps/x86_64/configure.ac
index a00958e219..848dc4e170 100644
--- a/sysdeps/x86_64/configure.ac
+++ b/sysdeps/x86_64/configure.ac
@@ -104,6 +104,37 @@ if test $libc_cv_x86_have_apx = yes; then
 fi
 LIBC_CONFIG_VAR([have-x86-apx], [$libc_cv_x86_have_apx])
 
+conftest_code="
+extern float truncf (float __x) __attribute__ ((__nothrow__,__const__));
+
+float
+tf (float x)
+{
+  return truncf (x);
+}
+"
+dnl Check if CC inlines trunc with -Os.
+LIBC_TRY_CC_COMMAND([if -Os inlines trunc],
+  [$conftest_code],
+  [-S -Os -msse4.1],
+  libc_cv_cc_x86_inline_trunc,
+  [
+libc_cv_cc_x86_inline_trunc=no
+if grep -E -q "roundss" conftest; then
+  libc_cv_cc_x86_inline_trunc=yes
+fi
+],
+[
+echo "failed to check if -Os inlines trunc."
+rm -f conftest*
+exit 1
+])
+if test "$libc_cv_cc_x86_inline_trunc" = yes; then
+  AC_DEFINE(HAVE_X86_OS_INLINE_TRUNC, 1)
+else
+  AC_DEFINE(HAVE_X86_OS_INLINE_TRUNC, 0)
+fi
+
 libc_cv_support_sframe=yes
 
 test -n "$critic_missing" && AC_MSG_ERROR([
-- 
2.51.0



More information about the Libc-alpha mailing list