LTO early plugin is too late - or how to disable dead code optimization?

I am writing an LTO plugin path that should be run before optimization,
however before my pass is running functions have already been removed.

Simple example below.
b.c contains foo(), bar() and foobar(). a.c contains the main and runs
either foo() or bar() but not foobar().

The bitcode file that ld.lld receives still contains the empty foobar()
function. I try to prevent any kind of optimization - but still the
foobar() function is already eliminated before the plugin is running.

How can I prevent the elimination of dead code?
I already use -O0 -fno-inline -fno-inline-functions -Wl,--discard-none
-Wl,--lto-O0 -femit-all-decls -fno-virtual-function-elimination
Thank you!

# cat a.c
#include <stdio.h>
int main(int argc, char **argv) {
  if (argc == 2)
  return 0;

# cat b.c
void foo() {}
void bar() {}
void foobar() {}

# cat f.cpp
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Module.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#include "llvm/Pass.h"
using namespace llvm;
namespace {
class Unreachable : public ModulePass {
  static char ID;
  Unreachable() : ModulePass(ID) {}
  bool runOnModule(Module &M) override;
char Unreachable::ID = 0;
bool Unreachable::runOnModule(Module &M) {
  errs() << "Running plugin ...\n";
  for (auto &F : M)
    errs() << F.getName() << "\n";
  return true;
static void registerUnreachablePass(const PassManagerBuilder &,
legacy::PassManagerBase &PM) {
  auto p = new Unreachable();
static RegisterStandardPasses RegisterUnreachablePassLTO(

# export CFLAGS="-O0 -g -flto=full"
# clang $CFLAGS -c a.c
# clang $CFLAGS -c b.c
# clang++ `llvm-config --cxxflags` -fno-rtti -fPIC -std=c++14 -o
-shared f.cpp

# clang -fno-inline -fno-inline-functions -Wl,--discard-none
-Wl,--lto-O0 -femit-all-decls -fno-virtual-function-elimination
-Wl,--no-gc-sections -fuse-ld=/usr/bin/ld.lld $CFLAGS
-fno-experimental-new-pass-manager -Wl,-mllvm=-load=./ -o ab a.o b.o

Running plugin ...

#^^^the foobar function is gone :frowning:


LTO itself has global dead code elimination. Try disabling with an internal option: -Wl,-mllvm,-compute-dead=false.