Skip to content

Commit

Permalink
[clang-repl] Add a interpreter-specific overload of operator new for …
Browse files Browse the repository at this point in the history
…C++ (#76218)

This patch brings back the basic support for C by inserting the required
for value printing runtime only when we are in C++ mode. Additionally,
it defines a new overload of operator placement new because we can't
really forward declare it in a library-agnostic way.

Fixes the issue described in #69072.
  • Loading branch information
vgvassilev committed Jan 18, 2024
1 parent e6a6a90 commit 1566f1f
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 33 deletions.
4 changes: 2 additions & 2 deletions clang/include/clang/Interpreter/Interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ class Interpreter {
llvm::Expected<llvm::orc::ExecutorAddr>
getSymbolAddressFromLinkerName(llvm::StringRef LinkerName) const;

enum InterfaceKind { NoAlloc, WithAlloc, CopyArray };
enum InterfaceKind { NoAlloc, WithAlloc, CopyArray, NewTag };

const llvm::SmallVectorImpl<Expr *> &getValuePrintingInfo() const {
return ValuePrintingInfo;
Expand All @@ -144,7 +144,7 @@ class Interpreter {

llvm::DenseMap<CXXRecordDecl *, llvm::orc::ExecutorAddr> Dtors;

llvm::SmallVector<Expr *, 3> ValuePrintingInfo;
llvm::SmallVector<Expr *, 4> ValuePrintingInfo;
};
} // namespace clang

Expand Down
33 changes: 27 additions & 6 deletions clang/lib/Interpreter/Interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -249,23 +249,26 @@ Interpreter::~Interpreter() {
// can't find the precise resource directory in unittests so we have to hard
// code them.
const char *const Runtimes = R"(
void* operator new(__SIZE_TYPE__, void* __p) noexcept;
#ifdef __cplusplus
void *__clang_Interpreter_SetValueWithAlloc(void*, void*, void*);
void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*);
void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*, void*);
void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*, float);
void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*, double);
void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*, long double);
void __clang_Interpreter_SetValueNoAlloc(void*,void*,void*,unsigned long long);
struct __clang_Interpreter_NewTag{} __ci_newtag;
void* operator new(__SIZE_TYPE__, void* __p, __clang_Interpreter_NewTag) noexcept;
template <class T, class = T (*)() /*disable for arrays*/>
void __clang_Interpreter_SetValueCopyArr(T* Src, void* Placement, unsigned long Size) {
for (auto Idx = 0; Idx < Size; ++Idx)
new ((void*)(((T*)Placement) + Idx)) T(Src[Idx]);
new ((void*)(((T*)Placement) + Idx), __ci_newtag) T(Src[Idx]);
}
template <class T, unsigned long N>
void __clang_Interpreter_SetValueCopyArr(const T (*Src)[N], void* Placement, unsigned long Size) {
__clang_Interpreter_SetValueCopyArr(Src[0], Placement, Size);
}
#endif // __cplusplus
)";

llvm::Expected<std::unique_ptr<Interpreter>>
Expand All @@ -280,7 +283,7 @@ Interpreter::create(std::unique_ptr<CompilerInstance> CI) {
if (!PTU)
return PTU.takeError();

Interp->ValuePrintingInfo.resize(3);
Interp->ValuePrintingInfo.resize(4);
// FIXME: This is a ugly hack. Undo command checks its availability by looking
// at the size of the PTU list. However we have parsed something in the
// beginning of the REPL so we have to mark them as 'Irrevocable'.
Expand Down Expand Up @@ -501,7 +504,7 @@ Interpreter::CompileDtorCall(CXXRecordDecl *CXXRD) {
static constexpr llvm::StringRef MagicRuntimeInterface[] = {
"__clang_Interpreter_SetValueNoAlloc",
"__clang_Interpreter_SetValueWithAlloc",
"__clang_Interpreter_SetValueCopyArr"};
"__clang_Interpreter_SetValueCopyArr", "__ci_newtag"};

bool Interpreter::FindRuntimeInterface() {
if (llvm::all_of(ValuePrintingInfo, [](Expr *E) { return E != nullptr; }))
Expand Down Expand Up @@ -531,6 +534,9 @@ bool Interpreter::FindRuntimeInterface() {
if (!LookupInterface(ValuePrintingInfo[CopyArray],
MagicRuntimeInterface[CopyArray]))
return false;
if (!LookupInterface(ValuePrintingInfo[NewTag],
MagicRuntimeInterface[NewTag]))
return false;
return true;
}

Expand Down Expand Up @@ -608,7 +614,9 @@ class RuntimeInterfaceBuilder
.getValuePrintingInfo()[Interpreter::InterfaceKind::CopyArray],
SourceLocation(), Args, SourceLocation());
}
Expr *Args[] = {AllocCall.get()};
Expr *Args[] = {
AllocCall.get(),
Interp.getValuePrintingInfo()[Interpreter::InterfaceKind::NewTag]};
ExprResult CXXNewCall = S.BuildCXXNew(
E->getSourceRange(),
/*UseGlobal=*/true, /*PlacementLParen=*/SourceLocation(), Args,
Expand All @@ -629,8 +637,9 @@ class RuntimeInterfaceBuilder
Interp.getValuePrintingInfo()[Interpreter::InterfaceKind::NoAlloc],
E->getBeginLoc(), Args, E->getEndLoc());
}
default:
llvm_unreachable("Unhandled Interpreter::InterfaceKind");
}
llvm_unreachable("Unhandled Interpreter::InterfaceKind");
}

Interpreter::InterfaceKind VisitRecordType(const RecordType *Ty) {
Expand Down Expand Up @@ -815,3 +824,15 @@ __clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType,
VRef = Value(static_cast<Interpreter *>(This), OpaqueType);
VRef.setLongDouble(Val);
}

// A trampoline to work around the fact that operator placement new cannot
// really be forward declared due to libc++ and libstdc++ declaration mismatch.
// FIXME: __clang_Interpreter_NewTag is ODR violation because we get the same
// definition in the interpreter runtime. We should move it in a runtime header
// which gets included by the interpreter and here.
struct __clang_Interpreter_NewTag {};
REPL_EXTERNAL_VISIBILITY void *
operator new(size_t __sz, void *__p, __clang_Interpreter_NewTag) noexcept {
// Just forward to the standard operator placement new.
return operator new(__sz, __p);
}
3 changes: 2 additions & 1 deletion clang/test/Interpreter/incremental-mode.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// RUN: clang-repl -Xcc -E
// RUN: clang-repl -Xcc -emit-llvm
// RUN: clang-repl -Xcc -emit-llvm
// RUN: clang-repl -Xcc -xc
// expected-no-diagnostics
29 changes: 5 additions & 24 deletions clang/unittests/Interpreter/InterpreterTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -248,28 +248,10 @@ TEST(IncrementalProcessing, FindMangledNameSymbol) {
#endif // _WIN32
}

static void *AllocateObject(TypeDecl *TD, Interpreter &Interp) {
static Value 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) {
free(Addr);
return nullptr;
}

Value Addr;
cantFail(Interp.ParseAndExecute("new " + Name + "()", &Addr));
return Addr;
}

Expand Down Expand Up @@ -317,7 +299,7 @@ TEST(IncrementalProcessing, InstantiateTemplate) {
}

TypeDecl *TD = cast<TypeDecl>(LookupSingleName(*Interp, "A"));
void *NewA = AllocateObject(TD, *Interp);
Value NewA = AllocateObject(TD, *Interp);

// Find back the template specialization
VarDecl *VD = static_cast<VarDecl *>(*PTUDeclRange.begin());
Expand All @@ -328,8 +310,7 @@ TEST(IncrementalProcessing, InstantiateTemplate) {
typedef int (*TemplateSpecFn)(void *);
auto fn =
cantFail(Interp->getSymbolAddress(MangledName)).toPtr<TemplateSpecFn>();
EXPECT_EQ(42, fn(NewA));
free(NewA);
EXPECT_EQ(42, fn(NewA.getPtr()));
}

#ifdef CLANG_INTERPRETER_NO_SUPPORT_EXEC
Expand Down

0 comments on commit 1566f1f

Please sign in to comment.