[clang-repl] Allow Interpreter::getSymbolAddress to take a mangled name.

This commit is contained in:
Vassil Vassilev 2021-08-30 16:31:54 +00:00
parent 81c99c5404
commit 4fb0805c65
10 changed files with 189 additions and 6 deletions

View File

@ -74,6 +74,10 @@ public:
/// This may return null if there was no matching declaration.
const Decl *GetDeclForMangledName(llvm::StringRef MangledName);
/// Given a global declaration, return a mangled name for this declaration
/// which has been added to this code generator via a Handle method.
llvm::StringRef GetMangledName(GlobalDecl GD);
/// Return the LLVM address of the given global entity.
///
/// \param isForDefinition If true, the caller intends to define the

View File

@ -16,6 +16,8 @@
#include "clang/Interpreter/PartialTranslationUnit.h"
#include "clang/AST/GlobalDecl.h"
#include "llvm/ExecutionEngine/JITSymbol.h"
#include "llvm/Support/Error.h"
@ -66,8 +68,20 @@ public:
return Execute(*PTU);
return llvm::Error::success();
}
/// \returns the \c JITTargetAddress of a \c GlobalDecl. This interface uses
/// the CodeGenModule's internal mangling cache to avoid recomputing the
/// mangled name.
llvm::Expected<llvm::JITTargetAddress> getSymbolAddress(GlobalDecl GD) const;
/// \returns the \c JITTargetAddress of a given name as written in the IR.
llvm::Expected<llvm::JITTargetAddress>
getSymbolAddress(llvm::StringRef UnmangledName) const;
getSymbolAddress(llvm::StringRef IRName) const;
/// \returns the \c JITTargetAddress of a given name as written in the object
/// file.
llvm::Expected<llvm::JITTargetAddress>
getSymbolAddressFromLinkerName(llvm::StringRef LinkerName) const;
};
} // namespace clang

View File

@ -122,6 +122,10 @@ namespace {
return D;
}
llvm::StringRef GetMangledName(GlobalDecl GD) {
return Builder->getMangledName(GD);
}
llvm::Constant *GetAddrOfGlobal(GlobalDecl global, bool isForDefinition) {
return Builder->GetAddrOfGlobal(global, ForDefinition_t(isForDefinition));
}
@ -325,6 +329,10 @@ const Decl *CodeGenerator::GetDeclForMangledName(llvm::StringRef name) {
return static_cast<CodeGeneratorImpl*>(this)->GetDeclForMangledName(name);
}
llvm::StringRef CodeGenerator::GetMangledName(GlobalDecl GD) {
return static_cast<CodeGeneratorImpl *>(this)->GetMangledName(GD);
}
llvm::Constant *CodeGenerator::GetAddrOfGlobal(GlobalDecl global,
bool isForDefinition) {
return static_cast<CodeGeneratorImpl*>(this)

View File

@ -61,8 +61,11 @@ llvm::Error IncrementalExecutor::runCtors() const {
}
llvm::Expected<llvm::JITTargetAddress>
IncrementalExecutor::getSymbolAddress(llvm::StringRef UnmangledName) const {
auto Sym = Jit->lookup(UnmangledName);
IncrementalExecutor::getSymbolAddress(llvm::StringRef Name,
SymbolNameKind NameKind) const {
auto Sym = (NameKind == LinkerName) ? Jit->lookupLinkerMangled(Name)
: Jit->lookup(Name);
if (!Sym)
return Sym.takeError();
return Sym->getAddress();

View File

@ -35,6 +35,8 @@ class IncrementalExecutor {
llvm::orc::ThreadSafeContext &TSCtx;
public:
enum SymbolNameKind { IRName, LinkerName };
IncrementalExecutor(llvm::orc::ThreadSafeContext &TSC, llvm::Error &Err,
const llvm::Triple &Triple);
~IncrementalExecutor();
@ -42,7 +44,7 @@ public:
llvm::Error addModule(std::unique_ptr<llvm::Module> M);
llvm::Error runCtors() const;
llvm::Expected<llvm::JITTargetAddress>
getSymbolAddress(llvm::StringRef UnmangledName) const;
getSymbolAddress(llvm::StringRef Name, SymbolNameKind NameKind) const;
};
} // end namespace clang

View File

@ -291,4 +291,11 @@ IncrementalParser::Parse(llvm::StringRef input) {
return PTU;
}
llvm::StringRef IncrementalParser::GetMangledName(GlobalDecl GD) const {
CodeGenerator *CG = getCodeGen(Act.get());
assert(CG);
return CG->GetMangledName(GD);
}
} // end namespace clang

View File

@ -15,6 +15,8 @@
#include "clang/Interpreter/PartialTranslationUnit.h"
#include "clang/AST/GlobalDecl.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
@ -69,6 +71,10 @@ public:
/// \c TranslationUnitDecl and \c llvm::Module corresponding to the input.
llvm::Expected<PartialTranslationUnit &> Parse(llvm::StringRef Input);
/// Uses the CodeGenModule mangled name cache and avoids recomputing.
///\returns the mangled name of a \c GD.
llvm::StringRef GetMangledName(GlobalDecl GD) const;
private:
llvm::Expected<PartialTranslationUnit &> ParseOrWrapTopLevelDecl();
};

View File

@ -223,11 +223,31 @@ llvm::Error Interpreter::Execute(PartialTranslationUnit &T) {
}
llvm::Expected<llvm::JITTargetAddress>
Interpreter::getSymbolAddress(llvm::StringRef UnmangledName) const {
Interpreter::getSymbolAddress(GlobalDecl GD) const {
if (!IncrExecutor)
return llvm::make_error<llvm::StringError>("Operation failed. "
"No execution engine",
std::error_code());
llvm::StringRef MangledName = IncrParser->GetMangledName(GD);
return getSymbolAddress(MangledName);
}
llvm::Expected<llvm::JITTargetAddress>
Interpreter::getSymbolAddress(llvm::StringRef IRName) const {
if (!IncrExecutor)
return llvm::make_error<llvm::StringError>("Operation failed. "
"No execution engine",
std::error_code());
return IncrExecutor->getSymbolAddress(UnmangledName);
return IncrExecutor->getSymbolAddress(IRName, IncrementalExecutor::IRName);
}
llvm::Expected<llvm::JITTargetAddress>
Interpreter::getSymbolAddressFromLinkerName(llvm::StringRef Name) const {
if (!IncrExecutor)
return llvm::make_error<llvm::StringError>("Operation failed. "
"No execution engine",
std::error_code());
return IncrExecutor->getSymbolAddress(Name, IncrementalExecutor::LinkerName);
}

View File

@ -1,4 +1,5 @@
set(LLVM_LINK_COMPONENTS
${LLVM_TARGETS_TO_BUILD}
Core
)
@ -11,6 +12,7 @@ target_link_libraries(ClangReplInterpreterTests PUBLIC
clangBasic
clangInterpreter
clangFrontend
clangSema
)
# Exceptions on Windows are not yet supported.

View File

@ -14,8 +14,13 @@
#include "clang/AST/Decl.h"
#include "clang/AST/DeclGroup.h"
#include "clang/AST/Mangle.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Sema.h"
#include "llvm/Support/TargetSelect.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
@ -123,4 +128,116 @@ TEST(InterpreterTest, DeclsAndStatements) {
EXPECT_EQ("Parsing failed.", llvm::toString(std::move(Err)));
}
static std::string MangleName(NamedDecl *ND) {
ASTContext &C = ND->getASTContext();
std::unique_ptr<MangleContext> MangleC(C.createMangleContext());
std::string mangledName;
llvm::raw_string_ostream RawStr(mangledName);
MangleC->mangleName(ND, RawStr);
return RawStr.str();
}
struct LLVMInitRAII {
LLVMInitRAII() {
llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter();
}
~LLVMInitRAII() { llvm::llvm_shutdown(); }
} LLVMInit;
TEST(IncrementalProcessing, FindMangledNameSymbol) {
std::unique_ptr<Interpreter> Interp = createInterpreter();
auto &PTU(cantFail(Interp->Parse("int f(const char*) {return 0;}")));
EXPECT_EQ(1U, DeclsSize(PTU.TUPart));
auto R1DeclRange = PTU.TUPart->decls();
NamedDecl *FD = cast<FunctionDecl>(*R1DeclRange.begin());
// Lower the PTU
if (llvm::Error Err = Interp->Execute(PTU)) {
// We cannot execute on the platform.
consumeError(std::move(Err));
return;
}
std::string MangledName = MangleName(FD);
auto Addr = cantFail(Interp->getSymbolAddress(MangledName));
EXPECT_NE(0U, Addr);
GlobalDecl GD(FD);
EXPECT_EQ(Addr, cantFail(Interp->getSymbolAddress(GD)));
}
static void *AllocateObject(TypeDecl *TD, Interpreter &Interp) {
std::string Name = TD->getQualifiedNameAsString();
const clang::Type *RDTy = TD->getTypeForDecl();
clang::ASTContext &C = Interp.getCompilerInstance()->getASTContext();
size_t Size = C.getTypeSize(RDTy);
void *Addr = malloc(Size);
// Tell the interpreter to call the default ctor with this memory. Synthesize:
// new (loc) ClassName;
static unsigned Counter = 0;
std::stringstream SS;
SS << "auto _v" << Counter++ << " = "
<< "new ((void*)"
// Windows needs us to prefix the hexadecimal value of a pointer with '0x'.
<< std::hex << std::showbase << (size_t)Addr << ")" << Name << "();";
auto R = Interp.ParseAndExecute(SS.str());
if (!R)
return nullptr;
return Addr;
}
static NamedDecl *LookupSingleName(Interpreter &Interp, const char *Name) {
Sema &SemaRef = Interp.getCompilerInstance()->getSema();
ASTContext &C = SemaRef.getASTContext();
DeclarationName DeclName = &C.Idents.get(Name);
LookupResult R(SemaRef, DeclName, SourceLocation(), Sema::LookupOrdinaryName);
SemaRef.LookupName(R, SemaRef.TUScope);
assert(!R.empty());
return R.getFoundDecl();
}
TEST(IncrementalProcessing, InstantiateTemplate) {
// FIXME: We cannot yet handle delayed template parsing. If we run with
// -fdelayed-template-parsing we try adding the newly created decl to the
// active PTU which causes an assert.
std::vector<const char *> Args = {"-fno-delayed-template-parsing"};
std::unique_ptr<Interpreter> Interp = createInterpreter(Args);
llvm::cantFail(Interp->Parse("void* operator new(__SIZE_TYPE__, void* __p);"
"extern \"C\" int printf(const char*,...);"
"class A {};"
"struct B {"
" template<typename T>"
" int callme(T) { return 42; }"
"};"));
auto &PTU = llvm::cantFail(Interp->Parse("auto _t = &B::callme<A*>;"));
auto PTUDeclRange = PTU.TUPart->decls();
EXPECT_EQ(1, std::distance(PTUDeclRange.begin(), PTUDeclRange.end()));
// Lower the PTU
if (llvm::Error Err = Interp->Execute(PTU)) {
// We cannot execute on the platform.
consumeError(std::move(Err));
return;
}
TypeDecl *TD = cast<TypeDecl>(LookupSingleName(*Interp, "A"));
void *NewA = AllocateObject(TD, *Interp);
// Find back the template specialization
VarDecl *VD = static_cast<VarDecl *>(*PTUDeclRange.begin());
UnaryOperator *UO = llvm::cast<UnaryOperator>(VD->getInit());
NamedDecl *TmpltSpec = llvm::cast<DeclRefExpr>(UO->getSubExpr())->getDecl();
std::string MangledName = MangleName(TmpltSpec);
typedef int (*TemplateSpecFn)(void *);
auto fn = (TemplateSpecFn)cantFail(Interp->getSymbolAddress(MangledName));
EXPECT_EQ(42, fn(NewA));
}
} // end anonymous namespace