De-pessimize SigSetConverter usage.

While looking at the disassembly for the epoll stuff I noticed that this
expands to quite a lot of code that the compiler can't optimize out for
LP64 (because it doesn't know that the "copy the argument into a local
and then use the local" bit isn't important).

There are two obvious options here. Something like this:
```
  int signalfd64(int fd, const sigset64_t* mask, int flags) {
    return __signalfd4(fd, mask, sizeof(*mask), flags);
  }

  int signalfd(int fd, const sigset_t* mask, int flags) {
  #if defined(__LP64__)
    return signalfd64(fd, mask, flags);
  #else
    SigSetConverter set = {.sigset = *mask};
    return signalfd64(fd, &set.sigset64, flags);
  #endif
  }
```
Or something like this:
```
  int signalfd64(int fd, const sigset64_t* mask, int flags) {
    return __signalfd4(fd, mask, sizeof(*mask), flags);
  }

  #if defined(__LP64__)
  __strong_alias(signalfd, signalfd64);
  #else
  int signalfd(int fd, const sigset_t* mask, int flags) {
    SigSetConverter set = {};
    set.sigset = *mask;
    return signalfd64(fd, &set.sigset64, flags);
  }
  #endif
```
The former is slightly more verbose, but seems a bit more obvious, so I
initially went with that. (The former is more verbose in the generated
code too, given that the latter expands to _no_ code, just another symbol
pointing to the same code address.)

Having done that, I realized that slight changes to the interface would
let clang optimize away most/all of the overhead for LP64 with the only
preprocessor hackery being in SigSetConverter itself.

I also pulled out the legacy bsd `int` conversions since they're only
used in two (secret!) functions, so it's clearer to just have a separate
union for them. While doing so, I suppressed those functions for
riscv64, since there's no reason to keep carrying that mistake forward.

posix_spawn() is another simple case that doesn't actually benefit from
SigSetConverter, so I've given that its own anonymous union too.

Test: treehugger
Change-Id: Iaf67486da40d40fc53ec69717c3492ab7ab81ad6
This commit is contained in:
Elliott Hughes 2023-07-17 17:15:01 -07:00
parent a2cdb247e1
commit 215baed16f
9 changed files with 94 additions and 87 deletions

View file

@ -50,14 +50,8 @@ int poll(pollfd* fds, nfds_t fd_count, int ms) {
int ppoll(pollfd* fds, nfds_t fd_count, const timespec* ts, const sigset_t* ss) {
// The underlying `__ppoll` system call only takes `sigset64_t`.
SigSetConverter set;
sigset64_t* ss_ptr = nullptr;
if (ss != nullptr) {
set = {};
set.sigset = *ss;
ss_ptr = &set.sigset64;
}
return ppoll64(fds, fd_count, ts, ss_ptr);
SigSetConverter set{ss};
return ppoll64(fds, fd_count, ts, set.ptr);
}
int ppoll64(pollfd* fds, nfds_t fd_count, const timespec* ts, const sigset64_t* ss) {
@ -99,14 +93,8 @@ int select(int fd_count, fd_set* read_fds, fd_set* write_fds, fd_set* error_fds,
int pselect(int fd_count, fd_set* read_fds, fd_set* write_fds, fd_set* error_fds,
const timespec* ts, const sigset_t* ss) {
// The underlying `__pselect6` system call only takes `sigset64_t`.
SigSetConverter set;
sigset64_t* ss_ptr = nullptr;
if (ss != nullptr) {
set = {};
set.sigset = *ss;
ss_ptr = &set.sigset64;
}
return pselect64(fd_count, read_fds, write_fds, error_fds, ts, ss_ptr);
SigSetConverter set{ss};
return pselect64(fd_count, read_fds, write_fds, error_fds, ts, set.ptr);
}
int pselect64(int fd_count, fd_set* read_fds, fd_set* write_fds, fd_set* error_fds,

View file

@ -76,13 +76,23 @@ int sigaddset64(sigset64_t* set, int sig) {
return SigAddSet(set, sig);
}
// This isn't in our header files, but is exposed on all architectures.
union BsdSigSet {
int mask;
sigset64_t set;
};
// This isn't in our header files, but is exposed on all architectures except riscv64.
extern "C" int sigblock(int mask) {
SigSetConverter in, out;
sigemptyset(&in.sigset);
in.bsd = mask;
if (sigprocmask(SIG_BLOCK, &in.sigset, &out.sigset) == -1) return -1;
return out.bsd;
BsdSigSet in{.mask = mask}, out;
if (sigprocmask64(SIG_BLOCK, &in.set, &out.set) == -1) return -1;
return out.mask;
}
// This isn't in our header files, but is exposed on all architectures except riscv64.
extern "C" int sigsetmask(int mask) {
BsdSigSet in{.mask = mask}, out;
if (sigprocmask64(SIG_SETMASK, &in.set, &out.set) == -1) return -1;
return out.mask;
}
template <typename SigSetT>
@ -198,10 +208,9 @@ int sigpause(int sig) {
}
int sigpending(sigset_t* bionic_set) {
SigSetConverter set = {};
set.sigset = *bionic_set;
if (__rt_sigpending(&set.sigset64, sizeof(set.sigset64)) == -1) return -1;
*bionic_set = set.sigset;
SigSetConverter set{bionic_set};
if (__rt_sigpending(set.ptr, sizeof(sigset64_t)) == -1) return -1;
set.copy_out();
return 0;
}
@ -245,19 +254,9 @@ sighandler_t sigset(int sig, sighandler_t disp) {
return sigismember64(&old_mask, sig) ? SIG_HOLD : old_sa.sa_handler;
}
// This isn't in our header files, but is exposed on all architectures.
extern "C" int sigsetmask(int mask) {
SigSetConverter in, out;
sigemptyset(&in.sigset);
in.bsd = mask;
if (sigprocmask(SIG_SETMASK, &in.sigset, &out.sigset) == -1) return -1;
return out.bsd;
}
int sigsuspend(const sigset_t* bionic_set) {
SigSetConverter set = {};
set.sigset = *bionic_set;
return sigsuspend64(&set.sigset64);
SigSetConverter set{bionic_set};
return sigsuspend64(set.ptr);
}
int sigsuspend64(const sigset64_t* set) {
@ -271,9 +270,8 @@ int sigsuspend64(const sigset64_t* set) {
}
int sigtimedwait(const sigset_t* bionic_set, siginfo_t* info, const timespec* timeout) {
SigSetConverter set = {};
set.sigset = *bionic_set;
return sigtimedwait64(&set.sigset64, info, timeout);
SigSetConverter set{bionic_set};
return sigtimedwait64(set.ptr, info, timeout);
}
int sigtimedwait64(const sigset64_t* set, siginfo_t* info, const timespec* timeout) {
@ -287,9 +285,8 @@ int sigtimedwait64(const sigset64_t* set, siginfo_t* info, const timespec* timeo
}
int sigwait(const sigset_t* bionic_set, int* sig) {
SigSetConverter set = {};
set.sigset = *bionic_set;
return sigwait64(&set.sigset64, sig);
SigSetConverter set{bionic_set};
return sigwait64(set.ptr, sig);
}
int sigwait64(const sigset64_t* set, int* sig) {

View file

@ -44,24 +44,13 @@ extern "C" int __rt_sigprocmask(int, const sigset64_t*, sigset64_t*, size_t);
int sigprocmask(int how,
const sigset_t* bionic_new_set,
sigset_t* bionic_old_set) __attribute__((__noinline__)) {
SigSetConverter new_set;
sigset64_t* new_set_ptr = nullptr;
if (bionic_new_set != nullptr) {
sigemptyset64(&new_set.sigset64);
new_set.sigset = *bionic_new_set;
new_set_ptr = &new_set.sigset64;
SigSetConverter new_set{bionic_new_set};
SigSetConverter old_set{bionic_old_set};
int rc = sigprocmask64(how, new_set.ptr, old_set.ptr);
if (rc == 0 && bionic_old_set != nullptr) {
old_set.copy_out();
}
SigSetConverter old_set;
if (sigprocmask64(how, new_set_ptr, &old_set.sigset64) == -1) {
return -1;
}
if (bionic_old_set != nullptr) {
*bionic_old_set = old_set.sigset;
}
return 0;
return rc;
}
int sigprocmask64(int how,

View file

@ -41,7 +41,6 @@
#include <android/fdsan.h>
#include "private/ScopedSignalBlocker.h"
#include "private/SigSetConverter.h"
static int set_cloexec(int i) {
int v = fcntl(i, F_GETFD);
@ -131,8 +130,10 @@ struct __posix_spawnattr {
pid_t pgroup;
sched_param schedparam;
int schedpolicy;
SigSetConverter sigmask;
SigSetConverter sigdefault;
union {
sigset_t sigset;
sigset64_t sigset64;
} sigmask, sigdefault;
};
static void ApplyAttrs(short flags, const posix_spawnattr_t* attr) {

View file

@ -48,14 +48,8 @@ int epoll_create1(int flags) {
}
int epoll_pwait(int fd, epoll_event* events, int max_events, int timeout, const sigset_t* ss) {
SigSetConverter set;
sigset64_t* ss_ptr = nullptr;
if (ss != nullptr) {
set = {};
set.sigset = *ss;
ss_ptr = &set.sigset64;
}
return epoll_pwait64(fd, events, max_events, timeout, ss_ptr);
SigSetConverter set{ss};
return epoll_pwait64(fd, events, max_events, timeout, set.ptr);
}
int epoll_pwait64(int fd, epoll_event* events, int max_events, int timeout, const sigset64_t* ss) {

View file

@ -32,12 +32,12 @@
extern "C" int __signalfd4(int, const sigset64_t*, size_t, int);
int signalfd(int fd, const sigset_t* mask, int flags) {
SigSetConverter set = {};
set.sigset = *mask;
return signalfd64(fd, &set.sigset64, flags);
}
int signalfd64(int fd, const sigset64_t* mask, int flags) {
return __signalfd4(fd, mask, sizeof(*mask), flags);
}
int signalfd(int fd, const sigset_t* mask, int flags) {
// The underlying `__signalfd4` system call only takes `sigset64_t`.
SigSetConverter set{mask};
return signalfd64(fd, set.ptr, flags);
}

View file

@ -955,7 +955,7 @@ LIBC {
sigaction;
sigaddset; # introduced=21
sigaltstack;
sigblock;
sigblock; # arm x86 arm64 x86_64
sigdelset; # introduced=21
sigemptyset; # introduced=21
sigfillset; # introduced=21
@ -968,7 +968,7 @@ LIBC {
sigprocmask;
sigqueue; # introduced=23
sigsetjmp; # introduced-arm=9 introduced-arm64=21 introduced-x86=12 introduced-x86_64=21
sigsetmask;
sigsetmask; # arm x86 arm64 x86_64
sigsuspend;
sigtimedwait; # introduced=23
sigwait;

View file

@ -28,8 +28,45 @@
#pragma once
union SigSetConverter {
int bsd;
sigset_t sigset;
sigset64_t sigset64;
// Android's 32-bit ABI shipped with a sigset_t too small to include any
// of the realtime signals, so we have both sigset_t and sigset64_t. Many
// new system calls only accept a sigset64_t, so this helps paper over
// the difference at zero cost to LP64 in most cases after the optimizer
// removes the unnecessary temporary `ptr`.
struct SigSetConverter {
public:
SigSetConverter(const sigset_t* s) : SigSetConverter(const_cast<sigset_t*>(s)) {}
SigSetConverter(sigset_t* s) {
#if defined(__LP64__)
// sigset_t == sigset64_t on LP64.
ptr = s;
#else
sigset64 = {};
if (s != nullptr) {
original_ptr = s;
sigset = *s;
ptr = &sigset64;
} else {
ptr = nullptr;
}
#endif
}
void copy_out() {
#if defined(__LP64__)
// We used the original pointer directly, so no copy needed.
#else
*original_ptr = sigset;
#endif
}
sigset64_t* ptr;
private:
[[maybe_unused]] sigset_t* original_ptr;
union {
sigset_t sigset;
sigset64_t sigset64;
};
};

View file

@ -530,14 +530,15 @@ TEST(signal, sighold_filter) {
#endif
}
#if defined(__BIONIC__)
#if defined(__BIONIC__) && !defined(__riscv)
// Not exposed via headers, but the symbols are available if you declare them yourself.
extern "C" int sigblock(int);
extern "C" int sigsetmask(int);
#define HAVE_SIGBLOCK_SIGSETMASK
#endif
TEST(signal, sigblock_filter) {
#if defined(__BIONIC__)
#if defined(HAVE_SIGBLOCK_SIGSETMASK)
TestSignalMaskFunction([]() {
sigblock(~0U);
});
@ -545,7 +546,7 @@ TEST(signal, sigblock_filter) {
}
TEST(signal, sigsetmask_filter) {
#if defined(__BIONIC__)
#if defined(HAVE_SIGBLOCK_SIGSETMASK)
TestSignalMaskFunction([]() {
sigsetmask(~0U);
});