llvm / llvm-project

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

Segmentation fault in llvm-lto #70184

Open aytekinar opened 1 year ago

aytekinar commented 1 year ago

Hi,

I receive a segmentation fault when running llvm-lto -thinlto -thinlto-action=thinlink ....

Note that I am not running this command myself --- it's part of the Makefile of a PostgreSQL extension, and the corresponding PostgreSQL instance has been built with --enable-llvm configuration option.

I have modified the Makefile to include PG_LDFLAGS += -Wl,--reproduce=repro.tar.gz and am attaching repro.tar.gz in this issue. The trace is as follows:

0.      Program arguments: /usr/lib/llvm-15/bin/llvm-lto -thinlto -thinlto-action=thinlink -o vector.index.bc vector/src/hnsw.bc vector/src/hnswbuild.bc vector/src/hnswinsert.bc vector/src/hnswscan.bc vector/src/hnswutils.bc vector/src/hnswvacuum.bc vector/src/ivfbuild.bc vector/src/ivfflat.bc vector/src/ivfinsert.bc vector/src/ivfkmeans.bc vector/src/ivfscan.bc vector/src/ivfutils.bc vector/src/ivfvacuum.bc vector/src/vector.bc
 #0 0x00007f53ade7e3b1 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/usr/lib/llvm-15/bin/../lib/libLLVM-15.so.1+0xf043b1)
 #1 0x00007f53ade7c0fe llvm::sys::RunSignalHandlers() (/usr/lib/llvm-15/bin/../lib/libLLVM-15.so.1+0xf020fe)
 #2 0x00007f53ade7e8d6 (/usr/lib/llvm-15/bin/../lib/libLLVM-15.so.1+0xf048d6)
 #3 0x00007f53aca58520 (/lib/x86_64-linux-gnu/libc.so.6+0x42520)
 #4 0x00007f53ae8f7e2a (/usr/lib/llvm-15/bin/../lib/libLLVM-15.so.1+0x197de2a)
 #5 0x00007f53ae8f59b2 (/usr/lib/llvm-15/bin/../lib/libLLVM-15.so.1+0x197b9b2)
 #6 0x00007f53ae8d3fe4 llvm::BitcodeWriter::writeIndex(llvm::ModuleSummaryIndex const*, std::map<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, llvm::DenseMap<unsigned long, llvm::GlobalValueSummary*, llvm::DenseMapInfo<unsigned long, void>, llvm::detail::DenseMapPair<unsigned long, llvm::GlobalValueSummary*>>, std::less<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const, llvm::DenseMap<unsigned long, llvm::GlobalValueSummary*, llvm::DenseMapInfo<unsigned long, void>, llvm::detail::DenseMapPair<unsigned long, llvm::GlobalValueSummary*>>>>> const*) (/usr/lib/llvm-15/bin/../lib/libLLVM-15.so.1+0x1959fe4)
 #7 0x00007f53ae8d4627 llvm::writeIndexToFile(llvm::ModuleSummaryIndex const&, llvm::raw_ostream&, std::map<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, llvm::DenseMap<unsigned long, llvm::GlobalValueSummary*, llvm::DenseMapInfo<unsigned long, void>, llvm::detail::DenseMapPair<unsigned long, llvm::GlobalValueSummary*>>, std::less<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const, llvm::DenseMap<unsigned long, llvm::GlobalValueSummary*, llvm::DenseMapInfo<unsigned long, void>, llvm::detail::DenseMapPair<unsigned long, llvm::GlobalValueSummary*>>>>> const*) (/usr/lib/llvm-15/bin/../lib/libLLVM-15.so.1+0x195a627)
 #8 0x0000562e5e4a9bea (/usr/lib/llvm-15/bin/llvm-lto+0x15bea)
 #9 0x0000562e5e4a32a5 (/usr/lib/llvm-15/bin/llvm-lto+0xf2a5)
#10 0x00007f53aca3fd90 __libc_start_call_main ./csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#11 0x00007f53aca3fe40 call_init ./csu/../csu/libc-start.c:128:20
#12 0x00007f53aca3fe40 __libc_start_main ./csu/../csu/libc-start.c:379:5
#13 0x0000562e5e49e055 (/usr/lib/llvm-15/bin/llvm-lto+0xa055)
Segmentation fault
make: *** [/home/aytekinar/.pgenv/pgsql-16.0/lib/pgxs/src/makefiles/pgxs.mk:242: install] Error 139

Cc: @aykut-bozkurt

aytekinar commented 1 year ago

I have now created a minimal, reproducible example.

Assume we have the following library header:

#ifndef __MYLIB_H__
#define __MYLIB_H__

#include <stdlib.h>

float myfunc(const size_t n, const float *restrict x, const float *restrict y);

#endif // __MYLIB_H__

and its implementation file:

#include <stdlib.h>

#include "mylib.h"

__attribute__((target("default"))) static float
myfunc_impl_scalar(const size_t n, const float *restrict x,
                   const float *restrict y) {
  float sum = 0;
  for (size_t i = 0; i < n; i++) {
    sum += x[i] * y[i];
  }
  return sum;
}

__attribute__((target("avx"))) static float
myfunc_impl_avx(const size_t n, const float *restrict x,
                const float *restrict y) {
  float sum = 0;
  for (size_t i = 0; i < n; i++) {
    sum -= x[i] * y[i];
  }
  return sum;
}

static float myfunc_dispatcher(const size_t n, const float *restrict x,
                               const float *restrict y);

static float (*myfunc_ptr)(const size_t n, const float *restrict x,
                           const float *restrict y) = myfunc_dispatcher;

static float myfunc_dispatcher(const size_t n, const float *restrict x,
                               const float *restrict y) {
  if (__builtin_cpu_supports("avx")) {
    myfunc_ptr = myfunc_impl_avx;
    return myfunc_impl_avx(n, x, y);
  } else {
    myfunc_ptr = myfunc_impl_scalar;
    return myfunc_impl_scalar(n, x, y);
  }
}

float myfunc(const size_t n, const float *restrict x, const float *restrict y) {
  return myfunc_ptr(n, x, y);
}

If we use the -flto=thin switch, we get the following result:

$ clang-15 -Wall -Wpedantic -O2 -g -flto=thin mylib.c -c -emit-llvm -o mylib.bc
$ llvm-dis-15 mylib.bc
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
Stack dump:
0.      Program arguments: llvm-dis-15 mylib.bc
Stack dump without symbol names (ensure you have llvm-symbolizer in your PATH or set the environment var `LLVM_SYMBOLIZER_PATH` to point to it):
/usr/lib/llvm-15/bin/../lib/libLLVM-15.so.1(_ZN4llvm3sys15PrintStackTraceERNS_11raw_ostreamEi+0x31)[0x7f5f5b9c53b1]
/usr/lib/llvm-15/bin/../lib/libLLVM-15.so.1(_ZN4llvm3sys17RunSignalHandlersEv+0xee)[0x7f5f5b9c30fe]
/usr/lib/llvm-15/bin/../lib/libLLVM-15.so.1(+0xf048d6)[0x7f5f5b9c58d6]
/lib/x86_64-linux-gnu/libc.so.6(+0x42520)[0x7f5f5a59f520]
/usr/lib/llvm-15/bin/../lib/libLLVM-15.so.1(+0xf256d5)[0x7f5f5b9e66d5]
/usr/lib/llvm-15/bin/../lib/libLLVM-15.so.1(_ZNK4llvm18ModuleSummaryIndex5printERNS_11raw_ostreamEb+0xbbe)[0x7f5f5b9d634e]
llvm-dis-15(+0x5e07)[0x55c135f0de07]
/lib/x86_64-linux-gnu/libc.so.6(+0x29d90)[0x7f5f5a586d90]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80)[0x7f5f5a586e40]
llvm-dis-15(+0x3c45)[0x55c135f0bc45]
fish: Job 1, 'llvm-dis-15 mylib.bc' terminated by signal SIGSEGV (Address boundary error)
$

Without the -flto-thin switch, we seem to get going with the implementation:

$ clang-15 -Wall -Wpedantic -O2 -g mylib.c -c -emit-llvm -o mylib.bc
$ llvm-dis-15 mylib.bc
$

If, on the other hand, I resort to LLVM's function multi-versioning support (by unifying the function signatures), I don't have any problems at all. Revised mylib.c is as follows:

#include <stdlib.h>

#include "mylib.h"

__attribute__((target("default"))) static float
myfunc_impl(const size_t n, const float *restrict x,
                   const float *restrict y) {
  float sum = 0;
  for (size_t i = 0; i < n; i++) {
    sum += x[i] * y[i];
  }
  return sum;
}

__attribute__((target("avx"))) static float
myfunc_impl(const size_t n, const float *restrict x,
                const float *restrict y) {
  float sum = 0;
  for (size_t i = 0; i < n; i++) {
    sum -= x[i] * y[i];
  }
  return sum;
}

float myfunc(const size_t n, const float *restrict x, const float *restrict y) {
  return myfunc_impl(n, x, y);
}

with the commands as below:

$ clang-15 -Wall -Wpedantic -O2 -g mylib.c -c -emit-llvm -o mylib.bc
mylib.c:16:1: warning: unused function 'myfunc_impl' [-Wunused-function]
myfunc_impl(const size_t n, const float *restrict x,
^
1 warning generated.
$ llvm-dis-15 mylib.bc
$ clang-15 -Wall -Wpedantic -O2 -g -flto=thin mylib.c -c -emit-llvm -o mylib.bc
mylib.c:16:1: warning: unused function 'myfunc_impl' [-Wunused-function]
myfunc_impl(const size_t n, const float *restrict x,
^
1 warning generated.
$ llvm-dis-15 mylib.bc
$

Obviously, if I go for the latter approach, or add __attribute__((target_clones(...))) to a single function definition, for that matter, I lose portability of the code.

What should I do? Do you think this is a bug or I can do some workaround to alleviate the issue?

kleisauke commented 6 months ago

Reduced to:

reduced.c

static int __attribute__((target_clones("default,avx")))
has_target_clones(void) {
    return 0;
}

int main(void) {
    int (*func)(void) = has_target_clones;
    return func();
}
$ clang -O0 -g reduced.c
$ clang -O0 -flto -g reduced.c
clang: error: unable to execute command: Segmentation fault (core dumped)
clang: error: linker command failed due to signal (use -v to see invocation)
$ clang --version
clang version 18.1.1 (Fedora 18.1.1-1.fc40)
Target: x86_64-redhat-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
Configuration file: /etc/clang/clang.cfg