Skip to content

Commit 5538c90

Browse files
committed
Resolve getFullyQualifiedType in a statically-linked plugin host.
clad's reverse-mode argument handling (CladUtils::makeTypeReadable) calls clang::TypeName::getFullyQualifiedType, but within clang that symbol is referenced only by the Interpreter library. A clang driver built without LLVM_LINK_LLVM_DYLIB does not contain it, so loading clad as a -fplugin leaves the reference unresolved; under -undefined dynamic_lookup it binds to null and the first call jumps to address 0. Reverse mode over any class construction crashes as a result, which on such a host is most of the reverse-mode suite. Force-reference the symbol from clad (RequiredSymbols.cpp) and link the clangAST archive file into the plugin when not building against the LLVM dylib. Linking the clangAST target instead would drag in LLVMSupport and duplicate its cl::opt registrations, so the archive file is used directly. The attribute that keeps the reference alive is spelled for GNU compilers only and expands to nothing on MSVC, which has no equivalent and does not need one: Windows fails a link on an unresolved symbol rather than binding it to null.
1 parent 6e3b00f commit 5538c90

3 files changed

Lines changed: 66 additions & 4 deletions

File tree

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// RUN: %cladclang %s -I%S/../../include -oReverseCtorArgQualification.out
2+
// RUN: ./ReverseCtorArgQualification.out | %filecheck_exec %s
3+
//
4+
// Regression test for the plugin availability of
5+
// clang::TypeName::getFullyQualifiedType. It is used by
6+
// CladUtils::makeTypeReadable in reverse-mode call-argument handling, but within
7+
// clang only the Interpreter library references it. When clad is loaded as a
8+
// -fplugin into a clang driver built without LLVM_LINK_LLVM_DYLIB, the symbol is
9+
// absent from the host binary; without clad supplying it itself
10+
// (tools/RequiredSymbols.cpp + clangAST archive linkage) the call binds to null
11+
// and reverse-differentiating any function that constructs a class object
12+
// crashes with a jump to address 0. See issue vgvassilev/clad#XXXX.
13+
14+
#include "clad/Differentiator/Differentiator.h"
15+
#include <cstdio>
16+
17+
struct Pair {
18+
double a, b;
19+
Pair(double x, double y) : a(x), b(y) {}
20+
};
21+
22+
double f(double x) {
23+
Pair p(x * x, x);
24+
return p.a + p.b; // x^2 + x -> f'(x) = 2x + 1
25+
}
26+
27+
int main() {
28+
auto g = clad::gradient(f);
29+
double dx = 0;
30+
g.execute(3.0, &dx);
31+
printf("dx = %.2f\n", dx); // CHECK-EXEC: dx = 7.00
32+
return 0;
33+
}

tools/CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@ endif()
1616

1717
set(link_libs cladDifferentiator)
1818

19+
# Supply the symbols RequiredSymbols.cpp references, which such a host lacks.
20+
# Link the clangAST archive file, not the clangAST target: the target would
21+
# also drag in LLVMSupport and duplicate its cl::opt registrations. A dylib
22+
# build already has them from libclang-cpp.
23+
if (NOT LLVM_LINK_LLVM_DYLIB AND TARGET clangAST)
24+
list(APPEND link_libs "$<TARGET_FILE:clangAST>")
25+
endif()
26+
1927
if (NOT CLAD_BUILD_STATIC_ONLY)
2028
# Get rid of libLLVM-X.so which is appended to the list of static libraries.
2129
if (LLVM_LINK_LLVM_DYLIB)

tools/RequiredSymbols.cpp

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,27 @@
1+
#include "clang/AST/QualTypeNames.h"
2+
3+
// MSVC has no equivalent spelling, and needs none: an unresolved symbol fails
4+
// the link there rather than binding to null.
5+
#if defined(__GNUC__) || defined(__clang__)
6+
#define CLAD_USED __attribute__((used))
7+
#else
8+
#define CLAD_USED
9+
#endif
10+
111
namespace clad {
2-
namespace internal {
3-
void symbol_requester() {
4-
}
5-
}
12+
namespace internal {
13+
// Force-reference clang symbols a statically-linked plugin host may not pull
14+
// in itself; tools/CMakeLists.txt links clangAST to supply them.
15+
CLAD_USED void* symbol_requester() {
16+
static void* const kRequiredSymbols[] = {
17+
// Spelled out so an upstream signature change fails the build here,
18+
// instead of forcing in a symbol makeTypeReadable no longer calls.
19+
reinterpret_cast<void*>(
20+
static_cast<clang::QualType (*)(clang::QualType,
21+
const clang::ASTContext&, bool)>(
22+
&clang::TypeName::getFullyQualifiedType)),
23+
};
24+
return kRequiredSymbols[0];
625
}
26+
} // namespace internal
27+
} // namespace clad

0 commit comments

Comments
 (0)