Author: Aaron Jomy
Date: 2026-10-23T08:22:53+03:00
New Revision: aa6e40c073e669c0a79250ca4333e48b9d274831

URL: 
https://github.com/llvm/llvm-project/commit/aa6e40c073e669c0a79250ca4333e48b9d274831
DIFF: 
https://github.com/llvm/llvm-project/commit/aa6e40c073e669c0a79250ca4333e48b9d274831.diff

LOG: [clang-repl][CodeGen] Add setOptLevel, #pragma clang repl optimize(N)  
(#214793)

clang-repl fixes its CodeGenOptions when the interpreter is created, so
the optimization level is the same for every input. This adds a way to
change it, similar to Cling:

- Add a new `CodeGenerator::StartModule` overload that takes a
CodeGenOptions, so the incremental path can start a module with
different codegen options than the ones fixed at construction.
- `Interpreter::setOptLevel` uses it to re-stage the pending module at
the new level.
- `#pragma clang repl optimize(N)` exposes this in source, the level
applies to the input the pragma appears in and stays in effect until
changed again.

assisted by claude-opus-5-5

Added: 
    clang/test/Interpreter/pragma-optimize-diagnostics.cpp
    clang/test/Interpreter/pragma-optimize.cpp

Modified: 
    clang/include/clang/Interpreter/Interpreter.h
    clang/lib/Interpreter/Interpreter.cpp
    clang/unittests/Interpreter/InterpreterTest.cpp

Removed: 
    


################################################################################
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..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
@@ -575,6 +645,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/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 17cc6fc9b6ca8e..41c1facc8f2f08 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,81 @@ 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, 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