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 d9a2bafaa5b7f8cb05fe33da2e10da3b0a7b6893 Mon Sep 17 00:00:00 2001 From: Aaron Jomy <[email protected]> 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); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
