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
