https://github.com/aaronj0 updated 
https://github.com/llvm/llvm-project/pull/214793

>From 42c04c07ca43570a2f57bacd43acff5c97ef18fb Mon Sep 17 00:00:00 2001
From: Aaron Jomy <[email protected]>
Date: Thu, 22 Oct 2026 23:55:57 +0200
Subject: [PATCH 1/2] [clang-repl] Add Interpreter::setOptLevel

clang-repl fixes the optimization level when the interpreter is created
and uses it for every PTU.

Add Interpreter::setOptLevel. It sets OptimizationLevel, OptimizeSize
and the inlining mode in the CompilerInvocation's CodeGenOptions, which
CodeGen and the backend hold by reference. Code emitted after the call
gets the new level. The unittest switches the level between inputs and
checks the attributes of the emitted functions.
---
 clang/include/clang/Interpreter/Interpreter.h |  5 ++++
 clang/lib/Interpreter/Interpreter.cpp         |  8 +++++
 .../unittests/Interpreter/InterpreterTest.cpp | 30 +++++++++++++++++++
 3 files changed, 43 insertions(+)

diff --git a/clang/include/clang/Interpreter/Interpreter.h 
b/clang/include/clang/Interpreter/Interpreter.h
index 64dee1cf80db8d..5228b7fe80bd32 100644
--- a/clang/include/clang/Interpreter/Interpreter.h
+++ b/clang/include/clang/Interpreter/Interpreter.h
@@ -170,6 +170,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 a195835f4fe8e2..c9159ffeda32e4 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -575,6 +575,14 @@ llvm::Expected<IncrementalExecutor &> 
Interpreter::getExecutionEngine() {
   return *IncrExecutor.get();
 }
 
+void Interpreter::setOptLevel(unsigned OptLevel, unsigned OptSize) {
+  CodeGenOptions &CGO = getCompilerInstance()->getCodeGenOpts();
+  CGO.OptimizationLevel = OptLevel;
+  CGO.OptimizeSize = OptSize;
+  CGO.setInlining(OptLevel ? CodeGenOptions::NormalInlining
+                           : CodeGenOptions::OnlyAlwaysInlining);
+}
+
 ASTContext &Interpreter::getASTContext() {
   return getCompilerInstance()->getASTContext();
 }
diff --git a/clang/unittests/Interpreter/InterpreterTest.cpp 
b/clang/unittests/Interpreter/InterpreterTest.cpp
index 17cc6fc9b6ca8e..17e00409558bef 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"
@@ -574,6 +576,34 @@ 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));
+  EXPECT_FALSE(F1->hasFnAttribute(llvm::Attribute::NoInline));
+
+  // -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, TranslationUnit_CanonicalDecl) {
   std::vector<const char *> Args;
   std::unique_ptr<Interpreter> Interp = createInterpreter(Args);

>From 3531dbc63909ea67fcef779fce5432ea9b3cae18 Mon Sep 17 00:00:00 2001
From: Aaron Jomy <[email protected]>
Date: Thu, 22 Oct 2026 23:55:58 +0200
Subject: [PATCH 2/2] [clang-repl] Add #pragma clang repl optimize(<opt>)

Cling can change the optimization level per input with
'#pragma cling optimize'. The clang-repl pragma calls
Interpreter::setOptLevel for O0, O1, O2, O3, Os and Oz and stays in
effect until the next one. The Preprocessor owns the handler.

Add a unittest for the pragma, a clang-repl test that switches from O0
to O2, Os, Oz and back and checks the attributes and the emitted
bodies, and a test for the pragma's diagnostics.
---
 clang/lib/Interpreter/Interpreter.cpp         | 70 +++++++++++++++++++
 .../pragma-optimize-diagnostics.cpp           | 20 ++++++
 clang/test/Interpreter/pragma-optimize.cpp    | 50 +++++++++++++
 .../unittests/Interpreter/InterpreterTest.cpp | 47 +++++++++++++
 4 files changed, 187 insertions(+)
 create mode 100644 clang/test/Interpreter/pragma-optimize-diagnostics.cpp
 create mode 100644 clang/test/Interpreter/pragma-optimize.cpp

diff --git a/clang/lib/Interpreter/Interpreter.cpp 
b/clang/lib/Interpreter/Interpreter.cpp
index c9159ffeda32e4..1fa0456f6b4ee8 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"
@@ -46,6 +49,7 @@
 #include "clang/Serialization/ModuleCache.h"
 #include "clang/Serialization/ObjectFilePCHContainerReader.h"
 #include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringSwitch.h"
 #include "llvm/ExecutionEngine/JITSymbol.h"
 #include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h"
 #include "llvm/ExecutionEngine/Orc/LLJIT.h"
@@ -231,6 +235,70 @@ 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: O0, O1, O2, O3, Os or Oz.
+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);
+    std::string Opt = PP.getSpelling(Tok);
+    // The OptimizationLevel and OptimizeSize that each -O flag sets.
+    std::optional<std::pair<unsigned, unsigned>> Level =
+        llvm::StringSwitch<std::optional<std::pair<unsigned, unsigned>>>(Opt)
+            .Case("O0", std::pair(0U, 0U))
+            .Case("O1", std::pair(1U, 0U))
+            .Case("O2", std::pair(2U, 0U))
+            .Case("O3", std::pair(3U, 0U))
+            .Case("Os", std::pair(2U, 1U))
+            .Case("Oz", std::pair(2U, 2U))
+            .Default(std::nullopt);
+    if (!Level) {
+      PP.Diag(Tok.getLocation(), diag::warn_pragma_invalid_argument)
+          << Opt << "clang repl optimize" << /*Expected=*/true
+          << "'O0', 'O1', 'O2', 'O3', 'Os' or 'Oz'";
+      return;
+    }
+
+    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(Level->first, Level->second);
+  }
+};
+
 } // anonymous namespace
 
 namespace clang {
@@ -397,6 +465,8 @@ Interpreter::Interpreter(std::unique_ptr<CompilerInstance> 
Instance,
   if (ErrOut)
     return;
 
+  CI->getPreprocessor().AddPragmaHandler("clang", new 
PragmaReplHandler(*this));
+
   if (Act->getCodeGen()) {
     Act->CacheCodeGenModule();
     // The initial PTU is filled by `-include`/`-include-pch` or by CUDA
diff --git a/clang/test/Interpreter/pragma-optimize-diagnostics.cpp 
b/clang/test/Interpreter/pragma-optimize-diagnostics.cpp
new file mode 100644
index 00000000000000..c091dd737e04a4
--- /dev/null
+++ b/clang/test/Interpreter/pragma-optimize-diagnostics.cpp
@@ -0,0 +1,20 @@
+// REQUIRES: host-supports-jit
+//
+// RUN: cat %s | clang-repl -Xcc -Xclang -Xcc -verify
+
+#pragma clang repl optimize(O0)
+#pragma clang repl optimize(O1)
+#pragma clang repl optimize(O2)
+#pragma clang repl optimize(O3)
+#pragma clang repl optimize(Os)
+#pragma clang repl optimize(Oz)
+
+#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 'O0', 'O1', 'O2', 'O3', 'Os' or 
'Oz'}}
+#pragma clang repl optimize(foo) // expected-warning {{unexpected argument 
'foo' to '#pragma clang repl optimize'}}
+#pragma clang repl optimize(O4) // expected-warning {{unexpected argument 'O4' 
to '#pragma clang repl optimize'}}
+#pragma clang repl optimize(Og) // expected-warning {{unexpected argument 'Og' 
to '#pragma clang repl optimize'}}
+#pragma clang repl optimize(Ofast) // expected-warning {{unexpected argument 
'Ofast' to '#pragma clang repl optimize'}}
+#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 00000000000000..1d43c54e8edb42
--- /dev/null
+++ b/clang/test/Interpreter/pragma-optimize.cpp
@@ -0,0 +1,50 @@
+// 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(int x) { return x; }
+// CHECK:      ; Function Attrs: {{.*}}noinline {{.*}}optnone
+// CHECK-NEXT: define {{.*}}i32 @f_o0(
+// CHECK-NEXT: entry:
+// CHECK-NEXT:   %x.addr = alloca i32
+
+#pragma clang repl optimize(O2)
+extern "C" int f_o2(int x) { return x; }
+// CHECK:      ; Function Attrs:
+// CHECK-NOT:  {{minsize|noinline|optnone|optsize}}
+// CHECK-SAME: {{$}}
+// CHECK-NEXT: define {{.*}}i32 @f_o2(
+// CHECK-NEXT: entry:
+// CHECK-NEXT:   ret i32 %x
+
+#pragma clang repl optimize(Os)
+extern "C" int f_os(int x) { return x; }
+// CHECK:      ; Function Attrs:
+// CHECK-NOT:  {{minsize|noinline|optnone}}
+// CHECK-SAME: optsize
+// CHECK-NEXT: define {{.*}}i32 @f_os(
+// CHECK-NEXT: entry:
+// CHECK-NEXT:   ret i32 %x
+
+#pragma clang repl optimize(Oz)
+extern "C" int f_oz(int x) { return x; }
+// CHECK:      ; Function Attrs: {{.*}}minsize
+// CHECK-NOT:  {{noinline|optnone}}
+// CHECK-SAME: optsize
+// CHECK-NEXT: define {{.*}}i32 @f_oz(
+// CHECK-NEXT: entry:
+// CHECK-NEXT:   ret i32 %x
+
+#pragma clang repl optimize(O0)
+extern "C" int f_back(int x) { return x; }
+// CHECK:      ; Function Attrs:
+// CHECK-NOT:  minsize
+// CHECK-SAME: noinline {{.*}}optnone
+// CHECK-NOT:  optsize
+// CHECK-SAME: {{$}}
+// CHECK-NEXT: define {{.*}}i32 @f_back(
+// CHECK-NEXT: entry:
+// CHECK-NEXT:   %x.addr = alloca i32
diff --git a/clang/unittests/Interpreter/InterpreterTest.cpp 
b/clang/unittests/Interpreter/InterpreterTest.cpp
index 17e00409558bef..41c1facc8f2f08 100644
--- a/clang/unittests/Interpreter/InterpreterTest.cpp
+++ b/clang/unittests/Interpreter/InterpreterTest.cpp
@@ -604,6 +604,53 @@ TEST_F(InterpreterTest, SetOptLevel) {
   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 applies to the code that follows it; 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));
+  EXPECT_FALSE(HasAttr(PTU1, "f1", llvm::Attribute::NoInline));
+
+  // 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));
+
+  // Code parsed ahead of the pragma stays in the module of its input.
+  auto &PTU6 = cantFail(Interp->Parse("extern \"C\" int f6() { return 6; }\n"
+                                      "extern \"C\" int f7() { return 7; }\n"
+                                      "#pragma clang repl optimize(O2)\n"));
+  EXPECT_TRUE(HasAttr(PTU6, "f6", llvm::Attribute::OptimizeNone));
+}
+
 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