Skip to content

Commit 10f8d3e

Browse files
[clang-repl] Expose RuntimeInterfaceBuilder for customizations
1 parent 5d33f71 commit 10f8d3e

File tree

4 files changed

+253
-109
lines changed

4 files changed

+253
-109
lines changed

clang/include/clang/Interpreter/Interpreter.h

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "clang/AST/GlobalDecl.h"
1919
#include "clang/Interpreter/PartialTranslationUnit.h"
2020
#include "clang/Interpreter/Value.h"
21+
#include "clang/Sema/Ownership.h"
2122

2223
#include "llvm/ADT/DenseMap.h"
2324
#include "llvm/ExecutionEngine/JITSymbol.h"
@@ -75,17 +76,26 @@ class IncrementalCompilerBuilder {
7576
llvm::StringRef CudaSDKPath;
7677
};
7778

79+
/// Generate glue code between the Interpreter's built-in runtime and user code.
80+
class RuntimeInterfaceBuilder {
81+
public:
82+
virtual ~RuntimeInterfaceBuilder() = default;
83+
84+
using TransformExprFunction = ExprResult(RuntimeInterfaceBuilder *Builder,
85+
Expr *, ArrayRef<Expr *>);
86+
virtual TransformExprFunction *getPrintValueTransformer() = 0;
87+
};
88+
7889
/// Provides top-level interfaces for incremental compilation and execution.
7990
class Interpreter {
8091
std::unique_ptr<llvm::orc::ThreadSafeContext> TSCtx;
8192
std::unique_ptr<IncrementalParser> IncrParser;
8293
std::unique_ptr<IncrementalExecutor> IncrExecutor;
94+
std::unique_ptr<RuntimeInterfaceBuilder> RuntimeIB;
8395

8496
// An optional parser for CUDA offloading
8597
std::unique_ptr<IncrementalParser> DeviceParser;
8698

87-
Interpreter(std::unique_ptr<CompilerInstance> CI, llvm::Error &Err);
88-
8999
llvm::Error CreateExecutor();
90100
unsigned InitPTUSize = 0;
91101

@@ -94,8 +104,25 @@ class Interpreter {
94104
// printing happens, it's in an invalid state.
95105
Value LastValue;
96106

107+
// Add a call to an Expr to report its result. We query the function from
108+
// RuntimeInterfaceBuilder once and store it as a function pointer to avoid
109+
// frequent virtual function calls.
110+
RuntimeInterfaceBuilder::TransformExprFunction *AddPrintValueCall = nullptr;
111+
112+
protected:
113+
// Derived classes can make use an extended interface of the Interpreter.
114+
// That's useful for testing and out-of-tree clients.
115+
Interpreter(std::unique_ptr<CompilerInstance> CI, llvm::Error &Err);
116+
117+
// Lazily construct the RuntimeInterfaceBuilder. The provided instance will be
118+
// used for the entire lifetime of the interpreter. The default implementation
119+
// targets the in-process __clang_Interpreter runtime. Override this to use a
120+
// custom runtime.
121+
virtual std::unique_ptr<RuntimeInterfaceBuilder> FindRuntimeInterface();
122+
97123
public:
98-
~Interpreter();
124+
virtual ~Interpreter();
125+
99126
static llvm::Expected<std::unique_ptr<Interpreter>>
100127
create(std::unique_ptr<CompilerInstance> CI);
101128
static llvm::Expected<std::unique_ptr<Interpreter>>
@@ -143,8 +170,6 @@ class Interpreter {
143170
private:
144171
size_t getEffectivePTUSize() const;
145172

146-
bool FindRuntimeInterface();
147-
148173
llvm::DenseMap<CXXRecordDecl *, llvm::orc::ExecutorAddr> Dtors;
149174

150175
llvm::SmallVector<Expr *, 4> ValuePrintingInfo;

clang/lib/Interpreter/Interpreter.cpp

Lines changed: 143 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -507,9 +507,13 @@ static constexpr llvm::StringRef MagicRuntimeInterface[] = {
507507
"__clang_Interpreter_SetValueWithAlloc",
508508
"__clang_Interpreter_SetValueCopyArr", "__ci_newtag"};
509509

510-
bool Interpreter::FindRuntimeInterface() {
510+
static std::unique_ptr<RuntimeInterfaceBuilder>
511+
createInProcessRuntimeInterfaceBuilder(Interpreter &Interp, ASTContext &Ctx,
512+
Sema &S);
513+
514+
std::unique_ptr<RuntimeInterfaceBuilder> Interpreter::FindRuntimeInterface() {
511515
if (llvm::all_of(ValuePrintingInfo, [](Expr *E) { return E != nullptr; }))
512-
return true;
516+
return nullptr;
513517

514518
Sema &S = getCompilerInstance()->getSema();
515519
ASTContext &Ctx = S.getASTContext();
@@ -528,120 +532,34 @@ bool Interpreter::FindRuntimeInterface() {
528532

529533
if (!LookupInterface(ValuePrintingInfo[NoAlloc],
530534
MagicRuntimeInterface[NoAlloc]))
531-
return false;
535+
return nullptr;
532536
if (!LookupInterface(ValuePrintingInfo[WithAlloc],
533537
MagicRuntimeInterface[WithAlloc]))
534-
return false;
538+
return nullptr;
535539
if (!LookupInterface(ValuePrintingInfo[CopyArray],
536540
MagicRuntimeInterface[CopyArray]))
537-
return false;
541+
return nullptr;
538542
if (!LookupInterface(ValuePrintingInfo[NewTag],
539543
MagicRuntimeInterface[NewTag]))
540-
return false;
541-
return true;
544+
return nullptr;
545+
546+
return createInProcessRuntimeInterfaceBuilder(*this, Ctx, S);
542547
}
543548

544549
namespace {
545550

546-
class RuntimeInterfaceBuilder
547-
: public TypeVisitor<RuntimeInterfaceBuilder, Interpreter::InterfaceKind> {
548-
clang::Interpreter &Interp;
551+
class InterfaceKindVisitor
552+
: public TypeVisitor<InterfaceKindVisitor, Interpreter::InterfaceKind> {
553+
friend class InProcessRuntimeInterfaceBuilder;
554+
549555
ASTContext &Ctx;
550556
Sema &S;
551557
Expr *E;
552558
llvm::SmallVector<Expr *, 3> Args;
553559

554560
public:
555-
RuntimeInterfaceBuilder(clang::Interpreter &In, ASTContext &C, Sema &SemaRef,
556-
Expr *VE, ArrayRef<Expr *> FixedArgs)
557-
: Interp(In), Ctx(C), S(SemaRef), E(VE) {
558-
// The Interpreter* parameter and the out parameter `OutVal`.
559-
for (Expr *E : FixedArgs)
560-
Args.push_back(E);
561-
562-
// Get rid of ExprWithCleanups.
563-
if (auto *EWC = llvm::dyn_cast_if_present<ExprWithCleanups>(E))
564-
E = EWC->getSubExpr();
565-
}
566-
567-
ExprResult getCall() {
568-
QualType Ty = E->getType();
569-
QualType DesugaredTy = Ty.getDesugaredType(Ctx);
570-
571-
// For lvalue struct, we treat it as a reference.
572-
if (DesugaredTy->isRecordType() && E->isLValue()) {
573-
DesugaredTy = Ctx.getLValueReferenceType(DesugaredTy);
574-
Ty = Ctx.getLValueReferenceType(Ty);
575-
}
576-
577-
Expr *TypeArg =
578-
CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)Ty.getAsOpaquePtr());
579-
// The QualType parameter `OpaqueType`, represented as `void*`.
580-
Args.push_back(TypeArg);
581-
582-
// We push the last parameter based on the type of the Expr. Note we need
583-
// special care for rvalue struct.
584-
Interpreter::InterfaceKind Kind = Visit(&*DesugaredTy);
585-
switch (Kind) {
586-
case Interpreter::InterfaceKind::WithAlloc:
587-
case Interpreter::InterfaceKind::CopyArray: {
588-
// __clang_Interpreter_SetValueWithAlloc.
589-
ExprResult AllocCall = S.ActOnCallExpr(
590-
/*Scope=*/nullptr,
591-
Interp.getValuePrintingInfo()[Interpreter::InterfaceKind::WithAlloc],
592-
E->getBeginLoc(), Args, E->getEndLoc());
593-
assert(!AllocCall.isInvalid() && "Can't create runtime interface call!");
594-
595-
TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ty, SourceLocation());
596-
597-
// Force CodeGen to emit destructor.
598-
if (auto *RD = Ty->getAsCXXRecordDecl()) {
599-
auto *Dtor = S.LookupDestructor(RD);
600-
Dtor->addAttr(UsedAttr::CreateImplicit(Ctx));
601-
Interp.getCompilerInstance()->getASTConsumer().HandleTopLevelDecl(
602-
DeclGroupRef(Dtor));
603-
}
604-
605-
// __clang_Interpreter_SetValueCopyArr.
606-
if (Kind == Interpreter::InterfaceKind::CopyArray) {
607-
const auto *ConstantArrTy =
608-
cast<ConstantArrayType>(DesugaredTy.getTypePtr());
609-
size_t ArrSize = Ctx.getConstantArrayElementCount(ConstantArrTy);
610-
Expr *ArrSizeExpr = IntegerLiteralExpr(Ctx, ArrSize);
611-
Expr *Args[] = {E, AllocCall.get(), ArrSizeExpr};
612-
return S.ActOnCallExpr(
613-
/*Scope *=*/nullptr,
614-
Interp
615-
.getValuePrintingInfo()[Interpreter::InterfaceKind::CopyArray],
616-
SourceLocation(), Args, SourceLocation());
617-
}
618-
Expr *Args[] = {
619-
AllocCall.get(),
620-
Interp.getValuePrintingInfo()[Interpreter::InterfaceKind::NewTag]};
621-
ExprResult CXXNewCall = S.BuildCXXNew(
622-
E->getSourceRange(),
623-
/*UseGlobal=*/true, /*PlacementLParen=*/SourceLocation(), Args,
624-
/*PlacementRParen=*/SourceLocation(),
625-
/*TypeIdParens=*/SourceRange(), TSI->getType(), TSI, std::nullopt,
626-
E->getSourceRange(), E);
627-
628-
assert(!CXXNewCall.isInvalid() &&
629-
"Can't create runtime placement new call!");
630-
631-
return S.ActOnFinishFullExpr(CXXNewCall.get(),
632-
/*DiscardedValue=*/false);
633-
}
634-
// __clang_Interpreter_SetValueNoAlloc.
635-
case Interpreter::InterfaceKind::NoAlloc: {
636-
return S.ActOnCallExpr(
637-
/*Scope=*/nullptr,
638-
Interp.getValuePrintingInfo()[Interpreter::InterfaceKind::NoAlloc],
639-
E->getBeginLoc(), Args, E->getEndLoc());
640-
}
641-
default:
642-
llvm_unreachable("Unhandled Interpreter::InterfaceKind");
643-
}
644-
}
561+
InterfaceKindVisitor(ASTContext &Ctx, Sema &S, Expr *E)
562+
: Ctx(Ctx), S(S), E(E) {}
645563

646564
Interpreter::InterfaceKind VisitRecordType(const RecordType *Ty) {
647565
return Interpreter::InterfaceKind::WithAlloc;
@@ -713,8 +631,124 @@ class RuntimeInterfaceBuilder
713631
Args.push_back(CastedExpr.get());
714632
}
715633
};
634+
635+
class InProcessRuntimeInterfaceBuilder : public RuntimeInterfaceBuilder {
636+
Interpreter &Interp;
637+
ASTContext &Ctx;
638+
Sema &S;
639+
640+
public:
641+
InProcessRuntimeInterfaceBuilder(Interpreter &Interp, ASTContext &C, Sema &S)
642+
: Interp(Interp), Ctx(C), S(S) {}
643+
644+
TransformExprFunction *getPrintValueTransformer() override {
645+
return &transformForValuePrinting;
646+
}
647+
648+
private:
649+
static ExprResult transformForValuePrinting(RuntimeInterfaceBuilder *Builder,
650+
Expr *E,
651+
ArrayRef<Expr *> FixedArgs) {
652+
auto *B = static_cast<InProcessRuntimeInterfaceBuilder *>(Builder);
653+
654+
// Get rid of ExprWithCleanups.
655+
if (auto *EWC = llvm::dyn_cast_if_present<ExprWithCleanups>(E))
656+
E = EWC->getSubExpr();
657+
658+
InterfaceKindVisitor Visitor(B->Ctx, B->S, E);
659+
660+
// The Interpreter* parameter and the out parameter `OutVal`.
661+
for (Expr *E : FixedArgs)
662+
Visitor.Args.push_back(E);
663+
664+
QualType Ty = E->getType();
665+
QualType DesugaredTy = Ty.getDesugaredType(B->Ctx);
666+
667+
// For lvalue struct, we treat it as a reference.
668+
if (DesugaredTy->isRecordType() && E->isLValue()) {
669+
DesugaredTy = B->Ctx.getLValueReferenceType(DesugaredTy);
670+
Ty = B->Ctx.getLValueReferenceType(Ty);
671+
}
672+
673+
Expr *TypeArg = CStyleCastPtrExpr(B->S, B->Ctx.VoidPtrTy,
674+
(uintptr_t)Ty.getAsOpaquePtr());
675+
// The QualType parameter `OpaqueType`, represented as `void*`.
676+
Visitor.Args.push_back(TypeArg);
677+
678+
// We push the last parameter based on the type of the Expr. Note we need
679+
// special care for rvalue struct.
680+
Interpreter::InterfaceKind Kind = Visitor.Visit(&*DesugaredTy);
681+
switch (Kind) {
682+
case Interpreter::InterfaceKind::WithAlloc:
683+
case Interpreter::InterfaceKind::CopyArray: {
684+
// __clang_Interpreter_SetValueWithAlloc.
685+
ExprResult AllocCall = B->S.ActOnCallExpr(
686+
/*Scope=*/nullptr,
687+
B->Interp
688+
.getValuePrintingInfo()[Interpreter::InterfaceKind::WithAlloc],
689+
E->getBeginLoc(), Visitor.Args, E->getEndLoc());
690+
assert(!AllocCall.isInvalid() && "Can't create runtime interface call!");
691+
692+
TypeSourceInfo *TSI =
693+
B->Ctx.getTrivialTypeSourceInfo(Ty, SourceLocation());
694+
695+
// Force CodeGen to emit destructor.
696+
if (auto *RD = Ty->getAsCXXRecordDecl()) {
697+
auto *Dtor = B->S.LookupDestructor(RD);
698+
Dtor->addAttr(UsedAttr::CreateImplicit(B->Ctx));
699+
B->Interp.getCompilerInstance()->getASTConsumer().HandleTopLevelDecl(
700+
DeclGroupRef(Dtor));
701+
}
702+
703+
// __clang_Interpreter_SetValueCopyArr.
704+
if (Kind == Interpreter::InterfaceKind::CopyArray) {
705+
const auto *ConstantArrTy =
706+
cast<ConstantArrayType>(DesugaredTy.getTypePtr());
707+
size_t ArrSize = B->Ctx.getConstantArrayElementCount(ConstantArrTy);
708+
Expr *ArrSizeExpr = IntegerLiteralExpr(B->Ctx, ArrSize);
709+
Expr *Args[] = {E, AllocCall.get(), ArrSizeExpr};
710+
return B->S.ActOnCallExpr(
711+
/*Scope *=*/nullptr,
712+
B->Interp
713+
.getValuePrintingInfo()[Interpreter::InterfaceKind::CopyArray],
714+
SourceLocation(), Args, SourceLocation());
715+
}
716+
Expr *Args[] = {
717+
AllocCall.get(),
718+
B->Interp.getValuePrintingInfo()[Interpreter::InterfaceKind::NewTag]};
719+
ExprResult CXXNewCall = B->S.BuildCXXNew(
720+
E->getSourceRange(),
721+
/*UseGlobal=*/true, /*PlacementLParen=*/SourceLocation(), Args,
722+
/*PlacementRParen=*/SourceLocation(),
723+
/*TypeIdParens=*/SourceRange(), TSI->getType(), TSI, std::nullopt,
724+
E->getSourceRange(), E);
725+
726+
assert(!CXXNewCall.isInvalid() &&
727+
"Can't create runtime placement new call!");
728+
729+
return B->S.ActOnFinishFullExpr(CXXNewCall.get(),
730+
/*DiscardedValue=*/false);
731+
}
732+
// __clang_Interpreter_SetValueNoAlloc.
733+
case Interpreter::InterfaceKind::NoAlloc: {
734+
return B->S.ActOnCallExpr(
735+
/*Scope=*/nullptr,
736+
B->Interp.getValuePrintingInfo()[Interpreter::InterfaceKind::NoAlloc],
737+
E->getBeginLoc(), Visitor.Args, E->getEndLoc());
738+
}
739+
default:
740+
llvm_unreachable("Unhandled Interpreter::InterfaceKind");
741+
}
742+
}
743+
};
716744
} // namespace
717745

746+
static std::unique_ptr<RuntimeInterfaceBuilder>
747+
createInProcessRuntimeInterfaceBuilder(Interpreter &Interp, ASTContext &Ctx,
748+
Sema &S) {
749+
return std::make_unique<InProcessRuntimeInterfaceBuilder>(Interp, Ctx, S);
750+
}
751+
718752
// This synthesizes a call expression to a speciall
719753
// function that is responsible for generating the Value.
720754
// In general, we transform:
@@ -733,8 +767,13 @@ Expr *Interpreter::SynthesizeExpr(Expr *E) {
733767
Sema &S = getCompilerInstance()->getSema();
734768
ASTContext &Ctx = S.getASTContext();
735769

736-
if (!FindRuntimeInterface())
737-
llvm_unreachable("We can't find the runtime iterface for pretty print!");
770+
if (!RuntimeIB) {
771+
RuntimeIB = FindRuntimeInterface();
772+
AddPrintValueCall = RuntimeIB->getPrintValueTransformer();
773+
}
774+
775+
assert(AddPrintValueCall &&
776+
"We don't have a runtime interface for pretty print!");
738777

739778
// Create parameter `ThisInterp`.
740779
auto *ThisInterp = CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)this);
@@ -743,9 +782,9 @@ Expr *Interpreter::SynthesizeExpr(Expr *E) {
743782
auto *OutValue = CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)&LastValue);
744783

745784
// Build `__clang_Interpreter_SetValue*` call.
746-
RuntimeInterfaceBuilder Builder(*this, Ctx, S, E, {ThisInterp, OutValue});
785+
ExprResult Result =
786+
AddPrintValueCall(RuntimeIB.get(), E, {ThisInterp, OutValue});
747787

748-
ExprResult Result = Builder.getCall();
749788
// It could fail, like printing an array type in C. (not supported)
750789
if (Result.isInvalid())
751790
return E;

clang/unittests/Interpreter/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ add_clang_unittest(ClangReplInterpreterTests
1010
IncrementalCompilerBuilderTest.cpp
1111
IncrementalProcessingTest.cpp
1212
InterpreterTest.cpp
13+
InterpreterExtensionsTest.cpp
1314
CodeCompletionTest.cpp
1415
)
1516
target_link_libraries(ClangReplInterpreterTests PUBLIC

0 commit comments

Comments
 (0)