[clang] [clang-repl] Add setOptLevel and #pragma clang repl optimize(N) (PR #214793)
Aaron Jomy via cfe-commits
cfe-commits at lists.llvm.org
Thu Aug 27 01:58:03 PDT 2026
https://github.com/aaronj0 updated https://github.com/llvm/llvm-project/pull/214793
>From daa2ac12efa7b390c19962facb4ddeec66686e64 Mon Sep 17 00:00:00 2001
From: Aaron Jomy <aaronjomyjoseph at gmail.com>
Date: Fri, 7 Aug 2026 19:05:11 +0200
Subject: [PATCH 1/2] [clang][CodeGen] Add a StartModule overload taking
CodeGenOptions
---
clang/include/clang/CodeGen/ModuleBuilder.h | 3 +++
clang/lib/CodeGen/ModuleBuilder.cpp | 15 ++++++++++++++-
2 files changed, 17 insertions(+), 1 deletion(-)
diff --git a/clang/include/clang/CodeGen/ModuleBuilder.h b/clang/include/clang/CodeGen/ModuleBuilder.h
index cd93ef7cc654a..8fe7b388197b7 100644
--- a/clang/include/clang/CodeGen/ModuleBuilder.h
+++ b/clang/include/clang/CodeGen/ModuleBuilder.h
@@ -116,6 +116,9 @@ class CodeGenerator : public ASTConsumer {
/// Create a new \c llvm::Module after calling HandleTranslationUnit. This
/// enable codegen in interactive processing environments.
llvm::Module* StartModule(llvm::StringRef ModuleName, llvm::LLVMContext &C);
+
+ llvm::Module *StartModule(llvm::StringRef ModuleName, llvm::LLVMContext &C,
+ const CodeGenOptions &CGO);
};
/// CreateLLVMCodeGen - Create a CodeGenerator instance.
diff --git a/clang/lib/CodeGen/ModuleBuilder.cpp b/clang/lib/CodeGen/ModuleBuilder.cpp
index 0b00362487d2a..57d5ac3ebdbdf 100644
--- a/clang/lib/CodeGen/ModuleBuilder.cpp
+++ b/clang/lib/CodeGen/ModuleBuilder.cpp
@@ -39,7 +39,7 @@ namespace {
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS; // Only used for debug info.
const HeaderSearchOptions &HeaderSearchOpts; // Only used for debug info.
const PreprocessorOptions &PreprocessorOpts; // Only used for debug info.
- const CodeGenOptions &CodeGenOpts;
+ CodeGenOptions CodeGenOpts; // copied in so caller can modify
unsigned HandlingTopLevelDecls;
@@ -155,6 +155,12 @@ namespace {
return M.get();
}
+ llvm::Module *StartModule(llvm::StringRef ModuleName, llvm::LLVMContext &C,
+ const CodeGenOptions &CGO) {
+ CodeGenOpts = CGO;
+ return StartModule(ModuleName, C);
+ }
+
void Initialize(ASTContext &Context) override {
Ctx = &Context;
@@ -394,6 +400,13 @@ llvm::Module *CodeGenerator::StartModule(llvm::StringRef ModuleName,
return static_cast<CodeGeneratorImpl*>(this)->StartModule(ModuleName, C);
}
+llvm::Module *CodeGenerator::StartModule(llvm::StringRef ModuleName,
+ llvm::LLVMContext &C,
+ const CodeGenOptions &CGO) {
+ return static_cast<CodeGeneratorImpl *>(this)->StartModule(ModuleName, C,
+ CGO);
+}
+
std::unique_ptr<CodeGenerator>
clang::CreateLLVMCodeGen(DiagnosticsEngine &Diags, llvm::StringRef ModuleName,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
>From d9a2bafaa5b7f8cb05fe33da2e10da3b0a7b6893 Mon Sep 17 00:00:00 2001
From: Aaron Jomy <aaronjomyjoseph at gmail.com>
Date: Fri, 7 Aug 2026 19:05:11 +0200
Subject: [PATCH 2/2] [clang-repl] Add setOptLevel and #pragma clang repl
optimize(N)
---
clang/include/clang/Interpreter/Interpreter.h | 7 ++
clang/lib/Interpreter/Interpreter.cpp | 74 +++++++++++++++++++
.../pragma-optimize-diagnostics.cpp | 9 +++
clang/test/Interpreter/pragma-optimize.cpp | 16 ++++
.../unittests/Interpreter/InterpreterTest.cpp | 48 ++++++++++++
5 files changed, 154 insertions(+)
create mode 100644 clang/test/Interpreter/pragma-optimize-diagnostics.cpp
create mode 100644 clang/test/Interpreter/pragma-optimize.cpp
diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h
index c2622b23d5d9c..828f76eba0dfd 100644
--- a/clang/include/clang/Interpreter/Interpreter.h
+++ b/clang/include/clang/Interpreter/Interpreter.h
@@ -45,6 +45,7 @@ class CXXRecordDecl;
class Decl;
class IncrementalParser;
class IncrementalCUDADeviceParser;
+class PragmaHandler;
/// Create a pre-configured \c CompilerInstance for incremental processing.
class IncrementalCompilerBuilder {
@@ -105,6 +106,8 @@ class Interpreter {
std::unique_ptr<IncrementalParser> IncrParser;
std::unique_ptr<IncrementalExecutor> IncrExecutor;
+ std::unique_ptr<PragmaHandler> ReplPragma;
+
// An optional parser for CUDA offloading
std::unique_ptr<IncrementalCUDADeviceParser> DeviceParser;
@@ -160,6 +163,10 @@ class Interpreter {
llvm::Error Execute(PartialTranslationUnit &T);
llvm::Error ParseAndExecute(llvm::StringRef Code, Value *V = nullptr);
+ /// Set the optimization level for input parsed after this call. Affects
+ /// modules emitted subsequently; does not modify the shared CompilerInstance.
+ void setOptLevel(unsigned OptLevel);
+
/// Undo N previous incremental inputs.
llvm::Error Undo(unsigned N = 1);
diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp
index 092f3ede771f6..f0b2a237b3362 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -20,6 +20,7 @@
#include "clang/AST/ASTContext.h"
#include "clang/AST/Mangle.h"
#include "clang/AST/TypeVisitor.h"
+#include "clang/Basic/DiagnosticParse.h"
#include "clang/Basic/DiagnosticSema.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/TargetInfo.h"
@@ -38,6 +39,8 @@
#include "clang/Interpreter/IncrementalExecutor.h"
#include "clang/Interpreter/Interpreter.h"
#include "clang/Interpreter/Value.h"
+#include "clang/Lex/Pragma.h"
+#include "clang/Lex/Preprocessor.h"
#include "clang/Lex/PreprocessorOptions.h"
#include "clang/Options/OptionUtils.h"
#include "clang/Options/Options.h"
@@ -216,6 +219,58 @@ static llvm::Error ExecuteIncrementalAction(CompilerInstance &CI,
return llvm::Error::success();
}
+// '#pragma clang repl optimize(N)' sets the optimization level (0-3) used to
+// emit modules for input parsed after the pragma.
+struct PragmaReplHandler : public PragmaHandler {
+ Interpreter &Interp;
+
+ PragmaReplHandler(Interpreter &Interp)
+ : PragmaHandler("repl"), Interp(Interp) {}
+
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &FirstToken) override {
+ Token Tok;
+ PP.LexUnexpandedToken(Tok);
+ if (Tok.isNot(tok::identifier) ||
+ !Tok.getIdentifierInfo()->isStr("optimize")) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_identifier)
+ << "clang repl";
+ return;
+ }
+
+ PP.LexUnexpandedToken(Tok);
+ if (Tok.isNot(tok::l_paren)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_lparen)
+ << "clang repl optimize";
+ return;
+ }
+
+ PP.LexUnexpandedToken(Tok);
+ uint64_t OptLevel;
+ if (Tok.isNot(tok::numeric_constant) ||
+ !PP.parseSimpleIntegerLiteral(Tok, OptLevel) || OptLevel > 3) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_integer)
+ << 0 << 3 << "clang repl optimize";
+ return;
+ }
+
+ if (Tok.isNot(tok::r_paren)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_rparen)
+ << "clang repl optimize";
+ return;
+ }
+
+ PP.LexUnexpandedToken(Tok);
+ if (Tok.isNot(tok::eod)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
+ << "clang repl optimize";
+ return;
+ }
+
+ Interp.setOptLevel(OptLevel);
+ }
+};
+
} // anonymous namespace
namespace clang {
@@ -370,6 +425,9 @@ Interpreter::Interpreter(std::unique_ptr<CompilerInstance> Instance,
if (ErrOut)
return;
+ ReplPragma = std::make_unique<PragmaReplHandler>(*this);
+ CI->getPreprocessor().AddPragmaHandler("clang", ReplPragma.get());
+
if (Act->getCodeGen()) {
Act->CacheCodeGenModule();
// The initial PTU is filled by `-include`/`-include-pch` or by CUDA
@@ -402,6 +460,8 @@ Interpreter::Interpreter(std::unique_ptr<CompilerInstance> Instance,
}
Interpreter::~Interpreter() {
+ if (ReplPragma)
+ CI->getPreprocessor().RemovePragmaHandler("clang", ReplPragma.get());
IncrParser.reset();
Act->FinalizeAction();
if (DeviceParser)
@@ -533,6 +593,20 @@ llvm::Expected<IncrementalExecutor &> Interpreter::getExecutionEngine() {
return *IncrExecutor.get();
}
+void Interpreter::setOptLevel(unsigned OptLevel) {
+ CodeGenerator *CG = Act->getCodeGen();
+ if (!CG)
+ return;
+
+ CodeGenOptions CGO = getCompilerInstance()->getCodeGenOpts();
+ CGO.OptimizationLevel = OptLevel;
+
+ // The next (empty) module was already staged with the old options; re-stage
+ // it so the new options apply to the next parsed input.
+ std::unique_ptr<llvm::Module> Empty(CG->ReleaseModule());
+ CG->StartModule("incr_module_opt", Empty->getContext(), CGO);
+}
+
ASTContext &Interpreter::getASTContext() {
return getCompilerInstance()->getASTContext();
}
diff --git a/clang/test/Interpreter/pragma-optimize-diagnostics.cpp b/clang/test/Interpreter/pragma-optimize-diagnostics.cpp
new file mode 100644
index 0000000000000..ea6af462e54d0
--- /dev/null
+++ b/clang/test/Interpreter/pragma-optimize-diagnostics.cpp
@@ -0,0 +1,9 @@
+// REQUIRES: host-supports-jit
+//
+// RUN: cat %s | clang-repl -Xcc -Xclang -Xcc -verify
+
+#pragma clang repl optimise // expected-warning {{expected identifier in '#pragma clang repl' - ignored}}
+#pragma clang repl optimize // expected-warning {{missing '(' after '#pragma clang repl optimize' - ignoring}}
+#pragma clang repl optimize(9) // expected-warning {{expected integer between 0 and 3 inclusive in '#pragma clang repl optimize' - ignored}}
+#pragma clang repl optimize(2 // expected-warning {{missing ')' after '#pragma clang repl optimize' - ignoring}}
+#pragma clang repl optimize(2) extra // expected-warning {{extra tokens at end of '#pragma clang repl optimize' - ignored}}
diff --git a/clang/test/Interpreter/pragma-optimize.cpp b/clang/test/Interpreter/pragma-optimize.cpp
new file mode 100644
index 0000000000000..ab262fc6db214
--- /dev/null
+++ b/clang/test/Interpreter/pragma-optimize.cpp
@@ -0,0 +1,16 @@
+// REQUIRES: host-supports-jit
+//
+// '#pragma clang repl optimize(N)' sets the optimization level used to emit
+// subsequent input: 'optnone' is present at -O0 and gone once the level is
+// raised.
+//
+// RUN: cat %s | clang-repl -Xcc -Xclang -Xcc -emit-llvm | FileCheck %s
+
+extern "C" int f_optnone() { return 0; }
+// CHECK: Function Attrs:{{.*}} optnone
+// CHECK-NEXT: define {{.*}} @f_optnone()
+
+#pragma clang repl optimize(2)
+extern "C" int f_optimized() { return 1; }
+// CHECK: define {{.*}} @f_optimized()
+// CHECK-NOT: optnone
diff --git a/clang/unittests/Interpreter/InterpreterTest.cpp b/clang/unittests/Interpreter/InterpreterTest.cpp
index 450be2a25a12f..ff72baf7314a4 100644
--- a/clang/unittests/Interpreter/InterpreterTest.cpp
+++ b/clang/unittests/Interpreter/InterpreterTest.cpp
@@ -22,6 +22,8 @@
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Sema.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/Module.h"
#include "llvm/TargetParser/Host.h"
#include "gmock/gmock.h"
@@ -551,6 +553,52 @@ TEST_F(InterpreterTest, ValueMoveSemantics) {
Interp.reset();
}
+TEST_F(InterpreterTest, SetOptLevel) {
+ std::unique_ptr<Interpreter> Interp = createInterpreter();
+
+ // -O0 emits functions with the 'optnone' attribute.
+ auto &PTU0 = cantFail(Interp->Parse("extern \"C\" int f0() { return 0; }"));
+ llvm::Function *F0 = PTU0.TheModule->getFunction("f0");
+ ASSERT_NE(F0, nullptr);
+ EXPECT_TRUE(F0->hasFnAttribute(llvm::Attribute::OptimizeNone));
+
+ // -O2 does not; the change must reach the next emitted module.
+ Interp->setOptLevel(2);
+
+ auto &PTU1 = cantFail(Interp->Parse("extern \"C\" int f1() { return 1; }"));
+ llvm::Function *F1 = PTU1.TheModule->getFunction("f1");
+ ASSERT_NE(F1, nullptr);
+ EXPECT_FALSE(F1->hasFnAttribute(llvm::Attribute::OptimizeNone));
+}
+
+TEST_F(InterpreterTest, PragmaOptimizeLevel) {
+ std::unique_ptr<Interpreter> Interp = createInterpreter();
+
+ auto HasOptNone = [](PartialTranslationUnit &PTU, const char *Name) {
+ llvm::Function *F = PTU.TheModule->getFunction(Name);
+ EXPECT_NE(F, nullptr);
+ return F && F->hasFnAttribute(llvm::Attribute::OptimizeNone);
+ };
+
+ // Default -O0: 'optnone' present.
+ auto &PTU0 = cantFail(Interp->Parse("extern \"C\" int f0() { return 0; }"));
+ EXPECT_TRUE(HasOptNone(PTU0, "f0"));
+
+ // The pragma governs the whole input it appears in; f1 is emitted at -O2.
+ auto &PTU1 = cantFail(Interp->Parse("#pragma clang repl optimize(2)\n"
+ "extern \"C\" int f1() { return 1; }"));
+ EXPECT_FALSE(HasOptNone(PTU1, "f1"));
+
+ // The level is sticky across inputs.
+ auto &PTU2 = cantFail(Interp->Parse("extern \"C\" int f2() { return 2; }"));
+ EXPECT_FALSE(HasOptNone(PTU2, "f2"));
+
+ // optimize(0) restores 'optnone'.
+ auto &PTU3 = cantFail(Interp->Parse("#pragma clang repl optimize(0)\n"
+ "extern \"C\" int f3() { return 3; }"));
+ EXPECT_TRUE(HasOptNone(PTU3, "f3"));
+}
+
TEST_F(InterpreterTest, TranslationUnit_CanonicalDecl) {
std::vector<const char *> Args;
std::unique_ptr<Interpreter> Interp = createInterpreter(Args);
More information about the cfe-commits
mailing list