From 458f1aae8d2da5bf786ba53ea200e94a918ff55a Mon Sep 17 00:00:00 2001 From: Michael Kruse Date: Sat, 28 Mar 2026 23:55:05 +0100 Subject: [PATCH] [Polly] Forward VFS from PassBuilder for IO sandboxing (#188657) #184545 default-enables the IO sandbox in assert-builds. This causes Clang using Polly to crash (#188568). The issue is that `PassBuilder` uses `vfs::getRealFileSystem()` by default which is considered a IO sandbox violation in the Clang process. With this PR store the VFS from the `PassBuilder` from the original `registerPollyPasses` call for creating other `PassBuilder` instances. This PR also adds infrastructure for running Polly in `clang` (in addition in `opt`). `opt` does not enable the sandbox such that we need separate tests using Clang. Closes: #188568 --- polly/ci/polly-x86_64-linux-plugin.py | 2 +- polly/ci/polly-x86_64-linux.py | 2 +- polly/include/polly/ScopInliner.h | 11 +++++- polly/lib/Support/PollyPasses.def | 2 +- polly/lib/Support/RegisterPasses.cpp | 37 +++++++++++++------ polly/lib/Transform/ScopInliner.cpp | 15 ++++++-- polly/test/.clang-format | 1 + polly/test/CMakeLists.txt | 6 +++ .../combine_different_values.c | 23 ------------ polly/test/lit.cfg | 21 +++++++++++ polly/test/lit.site.cfg.in | 2 + polly/test/polly.c | 9 +++++ 12 files changed, 89 insertions(+), 42 deletions(-) create mode 100644 polly/test/.clang-format delete mode 100644 polly/test/CodeGen/RuntimeDebugBuilder/combine_different_values.c create mode 100644 polly/test/polly.c diff --git a/polly/ci/polly-x86_64-linux-plugin.py b/polly/ci/polly-x86_64-linux-plugin.py index 8933d25fa719..1e992027649b 100755 --- a/polly/ci/polly-x86_64-linux-plugin.py +++ b/polly/ci/polly-x86_64-linux-plugin.py @@ -29,7 +29,7 @@ with worker.run( "-DCMAKE_BUILD_TYPE=Release", "-DCMAKE_C_COMPILER_LAUNCHER=ccache", "-DCMAKE_CXX_COMPILER_LAUNCHER=ccache", - "-DLLVM_ENABLE_PROJECTS=polly", + "-DLLVM_ENABLE_PROJECTS=polly;clang", "-DLLVM_TARGETS_TO_BUILD=X86", "-DLLVM_ENABLE_LLD=ON", "-DLLVM_ENABLE_ASSERTIONS=ON", diff --git a/polly/ci/polly-x86_64-linux.py b/polly/ci/polly-x86_64-linux.py index 169f303f3956..dfa04c2922ff 100755 --- a/polly/ci/polly-x86_64-linux.py +++ b/polly/ci/polly-x86_64-linux.py @@ -30,7 +30,7 @@ with worker.run( "-DCMAKE_BUILD_TYPE=Release", "-DCMAKE_C_COMPILER_LAUNCHER=ccache", "-DCMAKE_CXX_COMPILER_LAUNCHER=ccache", - "-DLLVM_ENABLE_PROJECTS=polly", + "-DLLVM_ENABLE_PROJECTS=polly;clang", "-DLLVM_TARGETS_TO_BUILD=X86", "-DLLVM_ENABLE_LLD=ON", "-DLLVM_ENABLE_ASSERTIONS=ON", diff --git a/polly/include/polly/ScopInliner.h b/polly/include/polly/ScopInliner.h index ae1938f03ac7..ae3437701182 100644 --- a/polly/include/polly/ScopInliner.h +++ b/polly/include/polly/ScopInliner.h @@ -9,14 +9,23 @@ #ifndef POLLY_POLLYINLINER_H #define POLLY_POLLYINLINER_H +#include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/Analysis/CGSCCPassManager.h" #include "llvm/Analysis/LazyCallGraph.h" #include "llvm/IR/PassManager.h" +namespace llvm { +namespace vfs { +class FileSystem; +} +} // namespace llvm + namespace polly { class ScopInlinerPass : public llvm::PassInfoMixin { + llvm::IntrusiveRefCntPtr FS; + public: - ScopInlinerPass(); + explicit ScopInlinerPass(llvm::IntrusiveRefCntPtr FS); llvm::PreservedAnalyses run(llvm::LazyCallGraph::SCC &C, llvm::CGSCCAnalysisManager &AM, diff --git a/polly/lib/Support/PollyPasses.def b/polly/lib/Support/PollyPasses.def index c95ffa36db48..ed975b01fe7d 100644 --- a/polly/lib/Support/PollyPasses.def +++ b/polly/lib/Support/PollyPasses.def @@ -8,7 +8,7 @@ MODULE_PASS("polly-custom", createModuleToFunctionPassAdaptor(PollyFunctionPass( #ifndef CGSCC_PASS #define CGSCC_PASS(NAME, CREATE_PASS, PARSER) #endif -CGSCC_PASS("polly-inline", ScopInlinerPass(), parseNoOptions) +CGSCC_PASS("polly-inline", ScopInlinerPass(FS), parseNoOptions) #undef CGSCC_PASS #ifndef FUNCTION_PASS diff --git a/polly/lib/Support/RegisterPasses.cpp b/polly/lib/Support/RegisterPasses.cpp index ca96feec9bcf..b1c0e3c32a91 100644 --- a/polly/lib/Support/RegisterPasses.cpp +++ b/polly/lib/Support/RegisterPasses.cpp @@ -459,8 +459,13 @@ parsePollyCustomOptions(StringRef Params) { /// The IR may still be modified. static void buildCommonPollyPipeline(FunctionPassManager &PM, OptimizationLevel Level, + IntrusiveRefCntPtr FS, bool EnableForOpt) { - PassBuilder PB; + PassBuilder PB( + /*TM=*/nullptr, + /*PipelineTuningOptions=*/{}, + /*PGOOpt=*/{}, + /*PIC=*/nullptr, std::move(FS)); ExitOnError Err("Inconsistent Polly configuration: "); PollyPassOptions &&Opts = @@ -475,7 +480,8 @@ static void buildCommonPollyPipeline(FunctionPassManager &PM, } static void buildEarlyPollyPipeline(llvm::ModulePassManager &MPM, - llvm::OptimizationLevel Level) { + llvm::OptimizationLevel Level, + IntrusiveRefCntPtr FS) { bool EnableForOpt = shouldEnablePollyForOptimization() && Level.isOptimizingForSpeed(); if (!shouldEnablePollyForDiagnostic() && !EnableForOpt) @@ -494,7 +500,7 @@ static void buildEarlyPollyPipeline(llvm::ModulePassManager &MPM, FPM = FunctionPassManager(); } - buildCommonPollyPipeline(FPM, Level, EnableForOpt); + buildCommonPollyPipeline(FPM, Level, std::move(FS), EnableForOpt); MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); if (DumpAfter) @@ -504,7 +510,8 @@ static void buildEarlyPollyPipeline(llvm::ModulePassManager &MPM, } static void buildLatePollyPipeline(FunctionPassManager &PM, - llvm::OptimizationLevel Level) { + llvm::OptimizationLevel Level, + IntrusiveRefCntPtr FS) { bool EnableForOpt = shouldEnablePollyForOptimization() && Level.isOptimizingForSpeed(); if (!shouldEnablePollyForDiagnostic() && !EnableForOpt) @@ -518,7 +525,7 @@ static void buildLatePollyPipeline(FunctionPassManager &PM, "not supported with NPM", false); - buildCommonPollyPipeline(PM, Level, EnableForOpt); + buildCommonPollyPipeline(PM, Level, std::move(FS), EnableForOpt); if (DumpAfter) PM.addPass(DumpFunctionPass("-after")); @@ -542,7 +549,8 @@ static llvm::Expected parseNoOptions(StringRef Params) { static llvm::Expected parseCGPipeline(StringRef Name, llvm::CGSCCPassManager &CGPM, PassInstrumentationCallbacks *PIC, - ArrayRef Pipeline) { + ArrayRef Pipeline, + IntrusiveRefCntPtr FS) { #define CGSCC_PASS(NAME, CREATE_PASS, PARSER) \ if (PassBuilder::checkParametrizedPassName(Name, NAME)) { \ auto Params = PassBuilder::parsePassParameters(PARSER, Name, NAME); \ @@ -624,6 +632,7 @@ parseModulePipeline(StringRef Name, llvm::ModulePassManager &MPM, /// handle LICMed code to make it useful. void registerPollyPasses(PassBuilder &PB) { PassInstrumentationCallbacks *PIC = PB.getPassInstrumentationCallbacks(); + IntrusiveRefCntPtr FS = PB.getVirtualFileSystemPtr(); #define MODULE_PASS(NAME, CREATE_PASS, PARSER) \ { \ @@ -652,10 +661,10 @@ void registerPollyPasses(PassBuilder &PB) { return Err(parseFunctionPipeline(Name, FPM, PIC, Pipeline)); }); PB.registerPipelineParsingCallback( - [PIC](StringRef Name, CGSCCPassManager &CGPM, - ArrayRef Pipeline) -> bool { + [PIC, FS](StringRef Name, CGSCCPassManager &CGPM, + ArrayRef Pipeline) -> bool { ExitOnError Err("Unable to parse Polly call graph pass: "); - return Err(parseCGPipeline(Name, CGPM, PIC, Pipeline)); + return Err(parseCGPipeline(Name, CGPM, PIC, Pipeline, FS)); }); PB.registerPipelineParsingCallback( [PIC](StringRef Name, ModulePassManager &MPM, @@ -666,10 +675,16 @@ void registerPollyPasses(PassBuilder &PB) { switch (PassPosition) { case POSITION_EARLY: - PB.registerPipelineStartEPCallback(buildEarlyPollyPipeline); + PB.registerPipelineStartEPCallback( + [FS](ModulePassManager &MPM, OptimizationLevel Level) { + buildEarlyPollyPipeline(MPM, Level, FS); + }); break; case POSITION_BEFORE_VECTORIZER: - PB.registerVectorizerStartEPCallback(buildLatePollyPipeline); + PB.registerVectorizerStartEPCallback( + [FS](FunctionPassManager &FPM, OptimizationLevel Level) { + buildLatePollyPipeline(FPM, Level, FS); + }); break; } } diff --git a/polly/lib/Transform/ScopInliner.cpp b/polly/lib/Transform/ScopInliner.cpp index 794ba98dc543..50082abf55a0 100644 --- a/polly/lib/Transform/ScopInliner.cpp +++ b/polly/lib/Transform/ScopInliner.cpp @@ -34,7 +34,9 @@ namespace { /// Inliner implementation that works with both, LPM (using SCC_t=CallGraph) and /// NPM (using SCC_t=LazyCallGraph::SCC) -template bool runScopInlinerImpl(Function *F, SCC_t &SCC) { +template +bool runScopInlinerImpl(Function *F, SCC_t &SCC, + IntrusiveRefCntPtr FS) { // We do not try to inline non-trivial SCCs because this would lead to // "infinite" inlining if we are not careful. if (SCC.size() > 1) @@ -50,7 +52,11 @@ template bool runScopInlinerImpl(Function *F, SCC_t &SCC) { return false; } - PassBuilder PB; + PassBuilder PB( + /*TM=*/nullptr, + /*PipelineTuningOptions=*/{}, + /*PGOOpt=*/{}, + /*PIC=*/nullptr, std::move(FS)); // Populate analysis managers and register Polly-specific analyses. LoopAnalysisManager LAM; FunctionAnalysisManager FAM; @@ -96,7 +102,8 @@ template bool runScopInlinerImpl(Function *F, SCC_t &SCC) { } } // namespace -polly::ScopInlinerPass::ScopInlinerPass() { +polly::ScopInlinerPass::ScopInlinerPass(IntrusiveRefCntPtr FS) + : FS(std::move(FS)) { if (!polly::PollyAllowFullFunction) { report_fatal_error( "Aborting from ScopInliner because it only makes sense to run with " @@ -113,6 +120,6 @@ PreservedAnalyses polly::ScopInlinerPass::run(llvm::LazyCallGraph::SCC &SCC, llvm::LazyCallGraph &CG, llvm::CGSCCUpdateResult &UR) { Function *F = &SCC.begin()->getFunction(); - bool Changed = runScopInlinerImpl(F, SCC); + bool Changed = runScopInlinerImpl(F, SCC, FS); return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all(); } diff --git a/polly/test/.clang-format b/polly/test/.clang-format new file mode 100644 index 000000000000..594a340e6afe --- /dev/null +++ b/polly/test/.clang-format @@ -0,0 +1 @@ +ColumnLimit: 0 diff --git a/polly/test/CMakeLists.txt b/polly/test/CMakeLists.txt index 4548f01d925a..b2a34164ee6c 100644 --- a/polly/test/CMakeLists.txt +++ b/polly/test/CMakeLists.txt @@ -3,6 +3,7 @@ set(LLVM_SHLIBEXT "${CMAKE_SHARED_MODULE_SUFFIX}") add_custom_target(check-polly) set_target_properties(check-polly PROPERTIES FOLDER "Polly/Meta") +set(POLLY_TEST_C_COMPILER "") if(NOT LLVM_MAIN_SRC_DIR) find_program(LLVM_OPT NAMES opt HINTS ${LLVM_TOOLS_BINARY_DIR}) find_program(LLVM_FILECHECK NAMES FileCheck HINTS ${LLVM_TOOLS_BINARY_DIR}) @@ -35,6 +36,11 @@ else () set(LLVM_NOT "${LLVM_TOOLS_BINARY_DIR}/not") set(POLLY_TEST_EXTRA_PATHS "") set(POLLY_TEST_DEPS llvm-config opt LLVMPolly FileCheck not count) + + if (TARGET clang) + set(POLLY_TEST_C_COMPILER "${LLVM_TOOLS_BINARY_DIR}/clang${CMAKE_EXECUTABLE_SUFFIX}") + list(APPEND POLLY_TEST_DEPS clang) + endif () endif() if (POLLY_BUNDLED_ISL) diff --git a/polly/test/CodeGen/RuntimeDebugBuilder/combine_different_values.c b/polly/test/CodeGen/RuntimeDebugBuilder/combine_different_values.c deleted file mode 100644 index c91098706d5c..000000000000 --- a/polly/test/CodeGen/RuntimeDebugBuilder/combine_different_values.c +++ /dev/null @@ -1,23 +0,0 @@ -#define N 10 -void foo(float A[restrict], double B[restrict], char C[restrict], - int D[restrict], long E[restrict]) { - for (long i = 0; i < N; i++) - A[i] += B[i] + C[i] + D[i] + E[i]; -} - -int main() { - float A[N]; - double B[N]; - char C[N]; - int D[N]; - long E[N]; - - for (long i = 0; i < N; i++) { - __sync_synchronize(); - A[i] = B[i] = C[i] = D[i] = E[i] = 42; - } - - foo(A, B, C, D, E); - - return A[8]; -} diff --git a/polly/test/lit.cfg b/polly/test/lit.cfg index 075ebdacbdc9..fce67be3d6f9 100644 --- a/polly/test/lit.cfg +++ b/polly/test/lit.cfg @@ -4,11 +4,13 @@ import os import platform import re import subprocess +import shlex import lit.formats import lit.util from lit.llvm import llvm_config +from lit.llvm.subst import ToolSubst # Configuration file for the 'lit' test runner. @@ -55,6 +57,25 @@ llvm_config.use_default_substitutions() tool_patterns = ['opt', 'polly-isl-test'] llvm_config.add_tool_substitutions(tool_patterns) + +if config.polly_test_c_compiler: + config.suffixes += ['.c'] + llvm_polly_link_into_tools = lit.util.pythonize_bool(config.llvm_polly_link_into_tools) + clang_args = [] + if not llvm_polly_link_into_tools: + clang_args += [f'-fpass-plugin={config.polly_lib_dir}/LLVMPolly{config.llvm_shlibext}'] + llvm_config.add_tool_substitutions([ + ToolSubst("%clang", + command = config.polly_test_c_compiler, + extra_args = clang_args, + unresolved = "fatal" + ) + ]) + lit_config.note(f"Using Clang: {config.polly_test_c_compiler} {shlex.join(clang_args)}") +else: + lit_config.note("Clang tests disabled: No compatible Clang found") + + # opt knows whether it is compiled with -DNDEBUG. import subprocess try: diff --git a/polly/test/lit.site.cfg.in b/polly/test/lit.site.cfg.in index ca901b8825ce..56448f27ba6d 100644 --- a/polly/test/lit.site.cfg.in +++ b/polly/test/lit.site.cfg.in @@ -4,12 +4,14 @@ config.llvm_src_root = "@LLVM_SOURCE_DIR@" config.llvm_obj_root = "@LLVM_BINARY_DIR@" config.llvm_tools_dir = lit_config.substitute("@LLVM_TOOLS_DIR@") config.llvm_libs_dir = lit_config.substitute("@LLVM_LIBS_DIR@") +config.llvm_shlibext = "@LLVM_SHLIBEXT@" config.polly_obj_root = "@POLLY_BINARY_DIR@" config.polly_lib_dir = "@POLLY_LIB_DIR@" config.target_triple = "@LLVM_TARGET_TRIPLE@" config.llvm_polly_link_into_tools = "@LLVM_POLLY_LINK_INTO_TOOLS@" config.targets_to_build = "@TARGETS_TO_BUILD@" config.extra_paths = "@POLLY_TEST_EXTRA_PATHS@".split(";") +config.polly_test_c_compiler = "@POLLY_TEST_C_COMPILER@" ## Check the current platform with regex import re diff --git a/polly/test/polly.c b/polly/test/polly.c new file mode 100644 index 000000000000..6990f92440e1 --- /dev/null +++ b/polly/test/polly.c @@ -0,0 +1,9 @@ +// Sanity test for Polly in Clang +// RUN: %clang %s -O2 -c -mllvm -polly -mllvm -polly-process-unprofitable -mllvm -print-pipeline-passes -o %t.o | FileCheck %s + +// CHECK: ,polly, + +void foo(int *A, int *B, int n) { + for (int i = 0; i < n; ++i) + A[i] += B[i]; +}