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 <[email protected]>
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 b4e2a99cd05fc1184b8e308c0c6875d96e3bd70d Mon Sep 17 00:00:00 2001
From: Aaron Jomy <[email protected]>
Date: Sat, 29 Aug 2026 17:23:58 +0200
Subject: [PATCH 2/2] [clang-repl] Add setOptLevel and #pragma clang repl
 optimize(<opt>)

---
 clang/include/clang/Interpreter/Interpreter.h |  8 ++
 clang/lib/Interpreter/Interpreter.cpp         | 99 +++++++++++++++++++
 .../pragma-optimize-diagnostics.cpp           | 11 +++
 clang/test/Interpreter/pragma-optimize.cpp    | 30 ++++++
 .../unittests/Interpreter/InterpreterTest.cpp | 69 +++++++++++++
 5 files changed, 217 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..707ef0031cf61 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,11 @@ class Interpreter {
   llvm::Error Execute(PartialTranslationUnit &T);
   llvm::Error ParseAndExecute(llvm::StringRef Code, Value *V = nullptr);
 
+  /// Set the optimization level and size used to emit subsequently parsed
+  /// input. \p OptSize mirrors \c CodeGenOptions::OptimizeSize (1 for -Os,
+  /// 2 for -Oz).
+  void setOptLevel(unsigned OptLevel, unsigned OptSize = 0);
+
   /// 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..46f66ae25f4f0 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"
@@ -30,6 +31,7 @@
 #include "clang/Driver/Job.h"
 #include "clang/Driver/Tool.h"
 #include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/CompilerInvocation.h"
 #include "clang/Frontend/FrontendAction.h"
 #include "clang/Frontend/FrontendOptions.h"
 #include "clang/Frontend/MultiplexConsumer.h"
@@ -38,6 +40,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"
@@ -49,6 +53,7 @@
 #include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h"
 #include "llvm/ExecutionEngine/Orc/LLJIT.h"
 #include "llvm/IR/Module.h"
+#include "llvm/Option/ArgList.h"
 #include "llvm/Support/Errc.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/VirtualFileSystem.h"
@@ -216,6 +221,80 @@ static llvm::Error 
ExecuteIncrementalAction(CompilerInstance &CI,
   return llvm::Error::success();
 }
 
+// '#pragma clang repl optimize(<opt>)' sets the optimization level and size
+// used to emit modules for input parsed after the pragma. <opt> is an -O flag
+// spelled without the leading dash, e.g. O2 or Os.
+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;
+    }
+
+    // Route the flag through clang's own -O parsing so the pragma accepts
+    // exactly what the driver does.
+    PP.LexUnexpandedToken(Tok);
+    std::string Opt = PP.getSpelling(Tok);
+    std::string Flag = "-" + Opt;
+    const char *Argv[] = {Flag.c_str()};
+    unsigned MissingArgIndex, MissingArgCount;
+    llvm::opt::InputArgList Args = getDriverOptTable().ParseArgs(
+        Argv, MissingArgIndex, MissingArgCount,
+        llvm::opt::Visibility(options::CC1Option));
+    if (!Args.hasArg(options::OPT_O_Group)) {
+      PP.Diag(Tok.getLocation(), diag::warn_pragma_invalid_argument)
+          << Opt << "clang repl optimize" << /*Expected=*/true
+          << "an optimization flag such as 'O2' or 'Os'";
+      return;
+    }
+
+    // -Ofast also enables fast-math, which this pragma cannot express; reject
+    // it instead of silently applying only its optimization level.
+    if (Args.hasArg(options::OPT_Ofast)) {
+      PP.Diag(Tok.getLocation(), diag::warn_pragma_unsupported_action)
+          << "clang repl optimize" << Opt;
+      return;
+    }
+
+    unsigned OptLevel =
+        getOptimizationLevel(Args, InputKind(), PP.getDiagnostics());
+    unsigned OptSize = getOptimizationLevelSize(Args);
+
+    PP.LexUnexpandedToken(Tok);
+    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, OptSize);
+  }
+};
+
 } // anonymous namespace
 
 namespace clang {
@@ -370,6 +449,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 +484,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 +617,21 @@ llvm::Expected<IncrementalExecutor &> 
Interpreter::getExecutionEngine() {
   return *IncrExecutor.get();
 }
 
+void Interpreter::setOptLevel(unsigned OptLevel, unsigned OptSize) {
+  CodeGenerator *CG = Act->getCodeGen();
+  if (!CG)
+    return;
+
+  CodeGenOptions CGO = getCompilerInstance()->getCodeGenOpts();
+  CGO.OptimizationLevel = OptLevel;
+  CGO.OptimizeSize = OptSize;
+
+  // 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..b3b4ecd08e3ee
--- /dev/null
+++ b/clang/test/Interpreter/pragma-optimize-diagnostics.cpp
@@ -0,0 +1,11 @@
+// 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(2) // expected-warning {{unexpected argument '2' 
to '#pragma clang repl optimize'; expected an optimization flag such as 'O2' or 
'Os'}}
+#pragma clang repl optimize(foo) // expected-warning {{unexpected argument 
'foo' to '#pragma clang repl optimize'; expected an optimization flag such as 
'O2' or 'Os'}}
+#pragma clang repl optimize(Ofast) // expected-warning {{known but unsupported 
action 'Ofast' for '#pragma clang repl optimize' - ignored}}
+#pragma clang repl optimize(O2 // expected-warning {{missing ')' after 
'#pragma clang repl optimize' - ignoring}}
+#pragma clang repl optimize(O2) 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..fa13c4225a105
--- /dev/null
+++ b/clang/test/Interpreter/pragma-optimize.cpp
@@ -0,0 +1,30 @@
+// REQUIRES: host-supports-jit
+//
+// '#pragma clang repl optimize(<opt>)' sets the optimization level and size 
for
+// input parsed after it; <opt> is an -O flag spelled without the leading dash.
+//
+// RUN: cat %s | clang-repl -Xcc -Xclang -Xcc -emit-llvm | FileCheck %s
+
+extern "C" int f_o0() { return 0; }
+// CHECK: Function Attrs:{{.*}} optnone
+// CHECK-NEXT: define {{.*}} @f_o0()
+
+#pragma clang repl optimize(O2)
+extern "C" int f_o2() { return 1; }
+// CHECK: define {{.*}} @f_o2()
+// CHECK-NOT: optnone
+
+#pragma clang repl optimize(Os)
+extern "C" int f_os() { return 2; }
+// CHECK: Function Attrs:{{.*}} optsize
+// CHECK-NEXT: define {{.*}} @f_os()
+
+#pragma clang repl optimize(Oz)
+extern "C" int f_oz() { return 3; }
+// CHECK: Function Attrs:{{.*}} minsize
+// CHECK-NEXT: define {{.*}} @f_oz()
+
+#pragma clang repl optimize(O0)
+extern "C" int f_back() { return 4; }
+// CHECK: Function Attrs:{{.*}} optnone
+// CHECK-NEXT: define {{.*}} @f_back()
diff --git a/clang/unittests/Interpreter/InterpreterTest.cpp 
b/clang/unittests/Interpreter/InterpreterTest.cpp
index 450be2a25a12f..e1b79860b594d 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,73 @@ 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));
+
+  // A change only takes effect on the next parsed 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));
+
+  // -Oz is level 2, size 2.
+  Interp->setOptLevel(2, 2);
+
+  auto &PTU2 = cantFail(Interp->Parse("extern \"C\" int f2() { return 2; }"));
+  llvm::Function *F2 = PTU2.TheModule->getFunction("f2");
+  ASSERT_NE(F2, nullptr);
+  EXPECT_TRUE(F2->hasFnAttribute(llvm::Attribute::OptimizeForSize));
+  EXPECT_TRUE(F2->hasFnAttribute(llvm::Attribute::MinSize));
+}
+
+TEST_F(InterpreterTest, PragmaOptimizeLevel) {
+  std::unique_ptr<Interpreter> Interp = createInterpreter();
+
+  auto HasAttr = [](PartialTranslationUnit &PTU, const char *Name,
+                    llvm::Attribute::AttrKind Kind) {
+    llvm::Function *F = PTU.TheModule->getFunction(Name);
+    EXPECT_NE(F, nullptr);
+    return F && F->hasFnAttribute(Kind);
+  };
+
+  // Default -O0: 'optnone' present.
+  auto &PTU0 = cantFail(Interp->Parse("extern \"C\" int f0() { return 0; }"));
+  EXPECT_TRUE(HasAttr(PTU0, "f0", llvm::Attribute::OptimizeNone));
+
+  // The pragma governs the whole input it appears in; f1 is emitted at -O2.
+  auto &PTU1 = cantFail(Interp->Parse("#pragma clang repl optimize(O2)\n"
+                                      "extern \"C\" int f1() { return 1; }"));
+  EXPECT_FALSE(HasAttr(PTU1, "f1", llvm::Attribute::OptimizeNone));
+
+  // The level is sticky across inputs.
+  auto &PTU2 = cantFail(Interp->Parse("extern \"C\" int f2() { return 2; }"));
+  EXPECT_FALSE(HasAttr(PTU2, "f2", llvm::Attribute::OptimizeNone));
+
+  // -Os sets 'optsize' only; -Oz additionally sets 'minsize'.
+  auto &PTU3 = cantFail(Interp->Parse("#pragma clang repl optimize(Os)\n"
+                                      "extern \"C\" int f3() { return 3; }"));
+  EXPECT_TRUE(HasAttr(PTU3, "f3", llvm::Attribute::OptimizeForSize));
+  EXPECT_FALSE(HasAttr(PTU3, "f3", llvm::Attribute::MinSize));
+
+  auto &PTU4 = cantFail(Interp->Parse("#pragma clang repl optimize(Oz)\n"
+                                      "extern \"C\" int f4() { return 4; }"));
+  EXPECT_TRUE(HasAttr(PTU4, "f4", llvm::Attribute::MinSize));
+
+  // A lower level can be selected again, not just a higher one.
+  auto &PTU5 = cantFail(Interp->Parse("#pragma clang repl optimize(O0)\n"
+                                      "extern \"C\" int f5() { return 5; }"));
+  EXPECT_TRUE(HasAttr(PTU5, "f5", llvm::Attribute::OptimizeNone));
+  EXPECT_FALSE(HasAttr(PTU5, "f5", llvm::Attribute::OptimizeForSize));
+}
+
 TEST_F(InterpreterTest, TranslationUnit_CanonicalDecl) {
   std::vector<const char *> Args;
   std::unique_ptr<Interpreter> Interp = createInterpreter(Args);

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to