Writing up an answer closely based on the comments as I've found a use for this in my own code base and checked that the method works OK for me.
int do_things(void);
int application_main(void)
{
return do_things();
}
int test_main(void)
{
return 42;
}
int main(void)
{
return test_main();
}
The layout approximately reflects my use case. A given block of IR may have two entry points, one for running unit tests and one for doing whatever the code is used for. The unit tests need a subset of the symbols needed by the entire module. There is an advantage to being able to build the unit test part without building everything else.
Deadstripping is a definite improvement over my previous method of -Wl,--unresolved-symbols=ignore-all
clang demo.c # undefined reference to `do_things'
clang -O3 demo.c # undefined reference to `do_things'
clang demo.c -c -emit-llvm -o demo.bc # OK
llvm-nm demo.bc
---------------- T application_main
U do_things
---------------- T main
---------------- T test_main
clang demo.bc # undefined reference to `do_things'
opt -o stripped.bc -internalize -internalize-public-api-list=main -globaldce demo.bc
llvm-nm stripped.bc
---------------- T main
---------------- t test_main
clang stripped.bc # OK
The list of public symbols can be derived from ir files (in my case at least) so the opt invocation is actually
opt -internalize -internalize-public-api-list=`llvm-nm -extern-only -defined-only -just-symbol-name some-file.bc` -globaldce -O2