Skip to content

Commit 2293235

Browse files
[Driver][Frontend] Introduce load-pass-plugin option
Allow dynamic loading of LLVM passes via `load-pass-plugin` option passed to the Swift compiler driver.
1 parent 903c1e5 commit 2293235

File tree

13 files changed

+200
-23
lines changed

13 files changed

+200
-23
lines changed

include/swift/AST/DiagnosticsIRGen.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,5 +65,8 @@ ERROR(temporary_allocation_alignment_not_power_of_2,none,
6565
ERROR(explosion_size_oveflow,none,
6666
"explosion size too large", ())
6767

68+
ERROR(unable_to_load_pass_plugin,none,
69+
"unable to load plugin '%0': '%1'", (StringRef, StringRef))
70+
6871
#define UNDEFINE_DIAGNOSTIC_MACROS
6972
#include "DefineDiagnosticMacros.h"

include/swift/AST/IRGenOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,9 @@ class IRGenOptions {
491491
/// The calling convention used to perform non-swift calls.
492492
llvm::CallingConv::ID PlatformCCallingConvention;
493493

494+
/// Paths to the pass plugins registered via -load-pass-plugin.
495+
std::vector<std::string> LLVMPassPlugins;
496+
494497
IRGenOptions()
495498
: DWARFVersion(2),
496499
OutputKind(IRGenOutputKind::LLVMAssemblyAfterOptimization),

include/swift/Option/Options.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1698,6 +1698,11 @@ def use_interface_for_module: Separate<["-", "--"], "use-interface-for-module">,
16981698
HelpText<"Prefer loading these modules via interface">,
16991699
MetaVarName<"<name>">;
17001700

1701+
def load_pass_plugin_EQ : Joined<["-"], "load-pass-plugin=">,
1702+
Flags<[FrontendOption, ArgumentIsPath]>,
1703+
HelpText<"Load LLVM pass plugin from a dynamic shared object file.">,
1704+
MetaVarName<"<path>">;
1705+
17011706
// ONLY SUPPORTED IN NEW DRIVER
17021707

17031708
// These flags only exist here so that the old driver doesn't fail with unknown

include/swift/Subsystems.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,10 @@ namespace swift {
252252
/// Given an already created LLVM module, construct a pass pipeline and run
253253
/// the Swift LLVM Pipeline upon it. This will include the emission of LLVM IR
254254
/// if requested (\out is not null).
255-
void performLLVMOptimizations(const IRGenOptions &Opts, llvm::Module *Module,
255+
void performLLVMOptimizations(const IRGenOptions &Opts,
256+
DiagnosticEngine &Diags,
257+
llvm::sys::Mutex *DiagMutex,
258+
llvm::Module *Module,
256259
llvm::TargetMachine *TargetMachine,
257260
llvm::raw_pwrite_stream *out);
258261

lib/Driver/ToolChains.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI,
320320
inputArgs.AddLastArg(arguments, options::OPT_enable_experimental_cxx_interop);
321321
inputArgs.AddLastArg(arguments, options::OPT_cxx_interoperability_mode);
322322
inputArgs.AddLastArg(arguments, options::OPT_enable_builtin_module);
323+
inputArgs.AddLastArg(arguments, options::OPT_load_pass_plugin_EQ);
323324

324325
// Pass on any build config options
325326
inputArgs.AddAllArgs(arguments, options::OPT_D);

lib/DriverTool/swift_llvm_opt_main.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,10 @@ int swift_llvm_opt_main(ArrayRef<const char *> argv, void *MainAddr) {
315315
Opts.OutputKind = IRGenOutputKind::LLVMAssemblyAfterOptimization;
316316

317317
// Then perform the optimizations.
318-
performLLVMOptimizations(Opts, M.get(), TM.get(), &Out->os());
318+
SourceManager SM;
319+
DiagnosticEngine Diags(SM);
320+
performLLVMOptimizations(Opts, Diags, nullptr, M.get(), TM.get(),
321+
&Out->os());
319322
} else {
320323
runSpecificPasses(argv[0], M.get(), TM.get(), ModuleTriple, options);
321324
// Finally dump the output.

lib/Frontend/CompilerInvocation.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2602,6 +2602,10 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args,
26022602
}
26032603
}
26042604

2605+
for (const Arg *A : Args.filtered(OPT_load_pass_plugin_EQ)) {
2606+
Opts.LLVMPassPlugins.push_back(A->getValue());
2607+
}
2608+
26052609
for (const Arg *A : Args.filtered(OPT_verify_type_layout)) {
26062610
Opts.VerifyTypeLayoutNames.push_back(A->getValue());
26072611
}

lib/IRGen/IRGen.cpp

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
#include "llvm/MC/TargetRegistry.h"
6868
#include "llvm/Object/ObjectFile.h"
6969
#include "llvm/Passes/PassBuilder.h"
70+
#include "llvm/Passes/PassPlugin.h"
7071
#include "llvm/Passes/StandardInstrumentations.h"
7172
#include "llvm/Support/CommandLine.h"
7273
#include "llvm/Support/Debug.h"
@@ -188,7 +189,23 @@ static void align(llvm::Module *Module) {
188189
}
189190
}
190191

192+
template <typename... ArgTypes>
193+
void diagnoseSync(
194+
DiagnosticEngine &Diags, llvm::sys::Mutex *DiagMutex, SourceLoc Loc,
195+
Diag<ArgTypes...> ID,
196+
typename swift::detail::PassArgument<ArgTypes>::type... Args) {
197+
if (DiagMutex)
198+
DiagMutex->lock();
199+
200+
Diags.diagnose(Loc, ID, std::move(Args)...);
201+
202+
if (DiagMutex)
203+
DiagMutex->unlock();
204+
}
205+
191206
void swift::performLLVMOptimizations(const IRGenOptions &Opts,
207+
DiagnosticEngine &Diags,
208+
llvm::sys::Mutex *DiagMutex,
192209
llvm::Module *Module,
193210
llvm::TargetMachine *TargetMachine,
194211
llvm::raw_pwrite_stream *out) {
@@ -238,6 +255,18 @@ void swift::performLLVMOptimizations(const IRGenOptions &Opts,
238255

239256
PassBuilder PB(TargetMachine, PTO, PGOOpt, &PIC);
240257

258+
// Attempt to load pass plugins and register their callbacks with PB.
259+
for (const auto &PluginFile : Opts.LLVMPassPlugins) {
260+
Expected<PassPlugin> PassPlugin = PassPlugin::Load(PluginFile);
261+
if (PassPlugin) {
262+
PassPlugin->registerPassBuilderCallbacks(PB);
263+
} else {
264+
diagnoseSync(Diags, DiagMutex, SourceLoc(),
265+
diag::unable_to_load_pass_plugin, PluginFile,
266+
toString(PassPlugin.takeError()));
267+
}
268+
}
269+
241270
// Register the AA manager first so that our version is the one used.
242271
FAM.registerPass([&] {
243272
auto AA = PB.buildDefaultAAPipeline();
@@ -512,20 +541,6 @@ static void countStatsPostIRGen(UnifiedStatsReporter &Stats,
512541
}
513542
}
514543

515-
template<typename ...ArgTypes>
516-
void
517-
diagnoseSync(DiagnosticEngine &Diags, llvm::sys::Mutex *DiagMutex,
518-
SourceLoc Loc, Diag<ArgTypes...> ID,
519-
typename swift::detail::PassArgument<ArgTypes>::type... Args) {
520-
if (DiagMutex)
521-
DiagMutex->lock();
522-
523-
Diags.diagnose(Loc, ID, std::move(Args)...);
524-
525-
if (DiagMutex)
526-
DiagMutex->unlock();
527-
}
528-
529544
/// Run the LLVM passes. In multi-threaded compilation this will be done for
530545
/// multiple LLVM modules in parallel.
531546
bool swift::performLLVM(const IRGenOptions &Opts,
@@ -594,7 +609,7 @@ bool swift::performLLVM(const IRGenOptions &Opts,
594609
assert(Opts.OutputKind == IRGenOutputKind::Module && "no output specified");
595610
}
596611

597-
performLLVMOptimizations(Opts, Module, TargetMachine,
612+
performLLVMOptimizations(Opts, Diags, DiagMutex, Module, TargetMachine,
598613
OutputFile ? &OutputFile->getOS() : nullptr);
599614

600615
if (Stats) {
@@ -1683,7 +1698,7 @@ GeneratedModule OptimizedIRRequest::evaluate(Evaluator &evaluator,
16831698
if (!irMod)
16841699
return irMod;
16851700

1686-
performLLVMOptimizations(desc.Opts, irMod.getModule(),
1701+
performLLVMOptimizations(desc.Opts, ctx.Diags, nullptr, irMod.getModule(),
16871702
irMod.getTargetMachine(), desc.out);
16881703
return irMod;
16891704
}

unittests/Driver/CMakeLists.txt

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,27 @@
1-
add_swift_unittest(SwiftDriverTests
1+
set(SWIFT_DRIVER_TESTS_SOURCE_FILES
22
FineGrainedDependencyGraphTests.cpp
33
MockingFineGrainedDependencyGraphs.cpp
44
UnitTestSourceFileDepGraphFactory.cpp
55
)
66

7+
set(SWIFT_DRIVER_TESTS_LIBRARIES
8+
swiftAST
9+
swiftClangImporter
10+
swiftSema
11+
swiftDriver
12+
)
13+
14+
# PassPlugin not supported on Windows
15+
if(NOT SWIFT_HOST_VARIANT STREQUAL "windows")
16+
list(APPEND SWIFT_DRIVER_TESTS_SOURCE_FILES
17+
PassPluginTest.cpp)
18+
list(APPEND SWIFT_DRIVER_TESTS_LIBRARIES
19+
LLVMTestingSupport)
20+
add_subdirectory(PassPluginInput)
21+
endif()
22+
23+
add_swift_unittest(SwiftDriverTests
24+
${SWIFT_DRIVER_TESTS_SOURCE_FILES})
25+
726
target_link_libraries(SwiftDriverTests PRIVATE
8-
swiftAST
9-
swiftClangImporter
10-
swiftSema
11-
swiftDriver)
27+
${SWIFT_DRIVER_TESTS_LIBRARIES})
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
add_library(InputTestPlugin MODULE
2+
TestPlugin.cpp)
3+
4+
# Put PLUGIN next to the unit test executable.
5+
set_output_directory(InputTestPlugin
6+
BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/../
7+
LIBRARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/../
8+
)
9+
10+
llvm_map_components_to_libnames(LLVM_LIBRARIES Core)
11+
target_link_libraries(InputTestPlugin PRIVATE ${LLVM_LIBRARIES})
12+
13+
set_target_properties(InputTestPlugin PROPERTIES FOLDER "Tests")
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//===---------------------- TestPlugin.cpp ----------------------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "llvm/Passes/PassBuilder.h"
14+
#include "llvm/Passes/PassPlugin.h"
15+
16+
#include "../TestPlugin.h"
17+
18+
using namespace llvm;
19+
20+
namespace {
21+
22+
struct TestPluginPass : PassInfoMixin<TestPluginPass> {
23+
PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM) {
24+
return PreservedAnalyses::all();
25+
}
26+
};
27+
28+
} // namespace
29+
30+
PassPluginLibraryInfo getTestPluginInfo() {
31+
return {LLVM_PLUGIN_API_VERSION, TEST_PLUGIN_NAME, TEST_PLUGIN_VERSION,
32+
[](PassBuilder &PB) {
33+
PB.registerPipelineParsingCallback(
34+
[](StringRef Name, ModulePassManager &PM,
35+
ArrayRef<PassBuilder::PipelineElement> InnerPipeline) {
36+
if (Name == "plugin-pass") {
37+
PM.addPass(TestPluginPass());
38+
return true;
39+
}
40+
return false;
41+
});
42+
}};
43+
}
44+
45+
extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo
46+
llvmGetPassPluginInfo() {
47+
return getTestPluginInfo();
48+
}

unittests/Driver/PassPluginTest.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//===-------------------- PassPluginTest.cpp --------------------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "llvm/Config/config.h"
14+
#include "llvm/IR/PassManager.h"
15+
#include "llvm/Passes/PassBuilder.h"
16+
#include "llvm/Passes/PassPlugin.h"
17+
#include "llvm/Testing/Support/Error.h"
18+
#include "gtest/gtest.h"
19+
20+
#include "TestPlugin.h"
21+
22+
using namespace llvm;
23+
24+
static std::string LibPath(const std::string &Name) {
25+
const auto &Argvs = testing::internal::GetArgvs();
26+
const char *Argv0 = Argvs.size() > 0 ? Argvs[0].c_str() : "PassPluginTest";
27+
std::string Path = sys::fs::getMainExecutable(Argv0, nullptr);
28+
llvm::SmallString<256> Buf{sys::path::parent_path(Path)};
29+
sys::path::append(Buf, (Name + LLVM_PLUGIN_EXT).c_str());
30+
return std::string(Buf.str());
31+
}
32+
33+
TEST(PassPluginTest, LoadPlugin) {
34+
auto PluginPath = LibPath("libInputTestPlugin");
35+
ASSERT_NE("", PluginPath);
36+
37+
Expected<PassPlugin> Plugin = PassPlugin::Load(PluginPath);
38+
ASSERT_TRUE(!!Plugin) << "Plugin path: " << PluginPath;
39+
40+
ASSERT_EQ(TEST_PLUGIN_NAME, Plugin->getPluginName());
41+
ASSERT_EQ(TEST_PLUGIN_VERSION, Plugin->getPluginVersion());
42+
43+
PassBuilder PB;
44+
ModulePassManager PM;
45+
ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, "plugin-pass"), Failed());
46+
47+
Plugin->registerPassBuilderCallbacks(PB);
48+
ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, "plugin-pass"), Succeeded());
49+
}

unittests/Driver/TestPlugin.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//===----------------------- TestPlugin.h -----------------------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#define TEST_PLUGIN_NAME "TestPlugin"
14+
#define TEST_PLUGIN_VERSION "0.1-unit"

0 commit comments

Comments
 (0)