Issue 179611
Summary Object file loading performance regression from LLVM 21 to 22
Labels new issue
Assignees
Reporter jeaye
    Hey folks! I have found a performance in loading object files via `LLJIT::addObjectFile`. The regression is particularly affected by the number of ELF sections present in the object file. For demonstration, please see the scaling here for the time it takes to load objects with growing sections across LLVM 21 and 22.

<div align="center">
<img width="600" height="390" alt="Image" src="" />
</div>

In practice, this makes object loading so slow for jank that it's twice as fast to just compile the C++ all over again, rather than load a pre-compiled object.

## Reproduction
### `test.sh`
This is a helper script which can make reproduction easier. I'm using this as `test.sh`.

```bash
#!/usr/bin/env bash

set -euo pipefail

case "${1:-}" in
  compile)
    clang++ -c -O0 test.cpp
    readelf -h test.o
    ;;
  test)
    time echo -e "%o\nextern \"C\" void find_me();\nfind_me();" | ./build/bin/clang-repl
    ;;
  *)
    echo "Usage: ${0} <compile|test>"
    exit 1
    ;;
esac
```
To use, for `./test.sh compile` to build our object file and then `./test.sh test` to see the time it takes to load the object file.

### `test.cpp`
There doesn't need to be anything in particular in here, but our main goal is to generate a lot of section headers. With C++, I've found a great way to do this with vtables, so our example here is triggering that case.

```cpp
template <int N> struct base {
  virtual ~base() {};
  virtual int get_int() = 0;
};

template <int N> struct derived : base<N> {
  int get_int() override { return derived<N - 1>{}.get_int(); }
};

template <> struct derived<0> : base<0> {
  int get_int() override { return 0; }
};

/* Tweak this number to generate more/less section headers. */
template struct derived<100>;

extern "C" int printf(const char *, ...);
extern "C" void find_me() { printf("found!\n"); }
```

### Changes to `ClangRepl.cpp`
We hack in a change to `clang-repl`, to make it support object loading. This is the easiest way I have found to reproduce this issue.

```diff
diff --git a/clang/tools/clang-repl/ClangRepl.cpp b/clang/tools/clang-repl/ClangRepl.cpp
index c9873540a5d6..650b61af74fe 100644
--- a/clang/tools/clang-repl/ClangRepl.cpp
+++ b/clang/tools/clang-repl/ClangRepl.cpp
@@ -254,6 +254,16 @@ ReplListCompleter::operator()(llvm::StringRef Buffer, size_t Pos,
   return Comps;
 }

+inline llvm::orc::LLJIT *getExecutionEngine(clang::Interpreter &I) {
+  struct OrcIncrementalExecutor : public clang::IncrementalExecutor {
+    std::unique_ptr<llvm::orc::LLJIT> Jit;
+  };
+
+  auto &engine = static_cast<OrcIncrementalExecutor &>(
+ llvm::cantFail(I.getExecutionEngine()));
+  return engine.Jit.get();
+}
+
 llvm::ExitOnError ExitOnErr;
 int main(int argc, const char **argv) {
 llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
@@ -406,6 +416,11 @@ int main(int argc, const char **argv) {
       } else if (Input.rfind("%lib ", 0) == 0) {
         if (auto Err = Interp->LoadDynamicLibrary(Input.data() + 5))
           llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");
+      } else if (Input.rfind("%o", 0) == 0) {
+ auto file{llvm::MemoryBuffer::getFile("test.o")};
+        if (auto Err = getExecutionEngine(*Interp)->addObjectFile(
+ std::move(file.get())))
+ llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");
 } else if (Input[0] == '%') {
         auto Err = llvm::make_error<llvm::StringError>(
             llvm::formatv(
```

## Extra notes
Trimming down the number of ELF sections alleviates this problem. For example, running `strip` on the object file and then re-running the above test will bring LLVM 22 back on par with LLVM 21 (at the loss of debug info).

I have tried a few times to bisect and find the originating commit, but it has so far eluded me. I think this could be because the issue existed pre-21, but was fixed in 21 and not fixed in 22. 

## System info
* Clang/LLVM 22 from `release/22.x` git
* Clang/LLVM 21 from `release/21.x` git
* Linux x86_64

@vgvassilev and @lhames for vis.
_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs

Reply via email to