llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
27.92k stars 11.53k forks source link

Clang generates superfluous landing pad #99636

Open halayli opened 1 month ago

halayli commented 1 month ago

Clang treats ::close() syscall as potentially throwing and injects a superfuous landing pad unnecessarily bloating the output. Here's an example that demonstrates this behavior and a comparison against gcc's output:

https://godbolt.org/z/ExG4MzY8q

Additional info I gathered:

The syscall in unistd.h from my ubuntu box is declared as such:


#ifdef  __cplusplus
# define __BEGIN_DECLS  extern "C" {
# define __END_DECLS    }
#else
# define __BEGIN_DECLS
# define __END_DECLS
#endif

__BEGIN_DECLS

...

/* Close the file descriptor FD.

   This function is a cancellation point and therefore not marked with
   __THROW.  */
extern int close (int __fd);

...

__END_DECLS

The lack of __THROW (defined as noexcept(true)) is used to communicate that the thread can be cancelled.

I believe the main descripency between clang and gcc here is that gcc treats the function declarations wrapped in extern "C" {} as noexcept(true) by default(rightfully so) and clang does not.

shafik commented 1 month ago

CC @AaronBallman

llvmbot commented 1 month ago

@llvm/issue-subscribers-clang-codegen

Author: Hasan Alayli (halayli)

Clang treats `::close()` syscall as potentially throwing and injects a superfuous landing pad unnecessarily bloating the output. Here's an example that demonstrates this behavior and a comparison against gcc's output: https://godbolt.org/z/ExG4MzY8q Additional info I gathered: The syscall in unistd.h from my ubuntu box is declared as such: ``` #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS # define __END_DECLS #endif __BEGIN_DECLS ... /* Close the file descriptor FD. This function is a cancellation point and therefore not marked with __THROW. */ extern int close (int __fd); ... __END_DECLS ``` The lack of `__THROW` (defined as `noexcept(true)`) is used to communicate that the thread can be cancelled. I believe the main descripency between clang and gcc here is that gcc treats the function declarations wrapped in `extern "C" {} ` as noexcept(true) by default(rightfully so) and clang does not.
AaronBallman commented 1 month ago

I believe the main descripency between clang and gcc here is that gcc treats the function declarations wrapped in extern "C" {} as noexcept(true) by default(rightfully so) and clang does not.

I don't see why a function with C language linkage is noexcept(true) by default; nothing in the standard requires that. Consider:

extern "C" {
  int func(int (*fp)()) {
    return fp();
  }
}

extern "C" int callback() {
  throw 12;  
}

int main() {
  try {
    return func(callback);
  } catch (...) {
    return 0;
  }
}

if the extern "C" declarations were implicitly noexcept, the behavior would be non-conforming. It doesn't look like GCC actually does that in practice: https://godbolt.org/z/s59bWsKhP

More likely is that GCC has special knowledge of ::close so it can be optimized better, and Clang does not.

CC @rjmccall @efriedma-quic