Skip to content

Commit

Permalink
Add marker passes to various pipeline stages (JuliaLang#50111)
Browse files Browse the repository at this point in the history
  • Loading branch information
pchintalapudi authored Jun 15, 2023
1 parent a595274 commit 5db2c27
Show file tree
Hide file tree
Showing 5 changed files with 458 additions and 18 deletions.
32 changes: 31 additions & 1 deletion doc/src/devdocs/llvm.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,39 @@ Here are example settings using `bash` syntax:
* `export JULIA_LLVM_ARGS=-debug-only=loop-vectorize` dumps LLVM `DEBUG(...)` diagnostics for
loop vectorizer. If you get warnings about "Unknown command line argument", rebuild LLVM with
`LLVM_ASSERTIONS = 1`.
* `export JULIA_LLVM_ARGS=-help` shows a list of available options.
* `export JULIA_LLVM_ARGS=-help` shows a list of available options. `export JULIA_LLVM_ARGS=-help-hidden` shows even more.
* `export JULIA_LLVM_ARGS="-fatal-warnings -print-options"` is an example how to use multiple options.

### Useful `JULIA_LLVM_ARGS` parameters
* `-print-after=PASS`: prints the IR after any execution of `PASS`, useful for checking changes done by a pass.
* `-print-before=PASS`: prints the IR before any execution of `PASS`, useful for checking the input to a pass.
* `-print-changed`: prints the IR whenever a pass changes the IR, useful for narrowing down which passes are causing problems.
* `-print-(before|after)=MARKER-PASS`: the Julia pipeline ships with a number of marker passes in the pipeline, which can be used to identify where problems or optimizations are occurring. A marker pass is defined as a pass which appears once in the pipeline and performs no transformations on the IR, and is only useful for targeting print-before/print-after. Currently, the following marker passes exist in the pipeline:
* BeforeOptimization
* BeforeEarlySimplification
* AfterEarlySimplification
* BeforeEarlyOptimization
* AfterEarlyOptimization
* BeforeLoopOptimization
* BeforeLICM
* AfterLICM
* BeforeLoopSimplification
* AfterLoopSimplification
* AfterLoopOptimization
* BeforeScalarOptimization
* AfterScalarOptimization
* BeforeVectorization
* AfterVectorization
* BeforeIntrinsicLowering
* AfterIntrinsicLowering
* BeforeCleanup
* AfterCleanup
* AfterOptimization
* `-time-passes`: prints the time spent in each pass, useful for identifying which passes are taking a long time.
* `-print-module-scope`: used in conjunction with `-print-(before|after)`, gets the entire module rather than the IR unit received by the pass
* `-debug`: prints out a lot of debugging information throughout LLVM
* `-debug-only=NAME`, prints out debugging statements from files with `DEBUG_TYPE` defined to `NAME`, useful for getting additional context about a problem

## Debugging LLVM transformations in isolation

On occasion, it can be useful to debug LLVM's transformations in isolation from
Expand Down
8 changes: 6 additions & 2 deletions src/llvm-demote-float16.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,12 @@ namespace {

static bool have_fp16(Function &caller, const Triple &TT) {
Attribute FSAttr = caller.getFnAttribute("target-features");
StringRef FS =
FSAttr.isValid() ? FSAttr.getValueAsString() : jl_ExecutionEngine->getTargetFeatureString();
StringRef FS = "";
if (FSAttr.isValid())
FS = FSAttr.getValueAsString();
else if (jl_ExecutionEngine)
FS = jl_ExecutionEngine->getTargetFeatureString();
// else probably called from opt, just do nothing
if (TT.isAArch64()) {
if (FS.find("+fp16fml") != llvm::StringRef::npos || FS.find("+fullfp16") != llvm::StringRef::npos){
return true;
Expand Down
43 changes: 43 additions & 0 deletions src/passes.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,47 @@ struct JuliaLICMPass : PassInfoMixin<JuliaLICMPass> {
LoopStandardAnalysisResults &AR, LPMUpdater &U) JL_NOTSAFEPOINT;
};

#define MODULE_MARKER_PASS(NAME) \
struct NAME##MarkerPass : PassInfoMixin<NAME##MarkerPass> { \
PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM) JL_NOTSAFEPOINT { return PreservedAnalyses::all(); } \
static bool isRequired() { return true; } \
};

#define FUNCTION_MARKER_PASS(NAME) \
struct NAME##MarkerPass : PassInfoMixin<NAME##MarkerPass> { \
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) JL_NOTSAFEPOINT { return PreservedAnalyses::all(); } \
static bool isRequired() { return true; } \
};

#define LOOP_MARKER_PASS(NAME) \
struct NAME##MarkerPass : PassInfoMixin<NAME##MarkerPass> { \
PreservedAnalyses run(Loop &L, LoopAnalysisManager &AM, \
LoopStandardAnalysisResults &AR, LPMUpdater &U) JL_NOTSAFEPOINT { \
return PreservedAnalyses::all(); \
} \
static bool isRequired() { return true; } \
};

// These are useful for debugging with --print-before/after
MODULE_MARKER_PASS(BeforeOptimization)
MODULE_MARKER_PASS(BeforeEarlySimplification)
MODULE_MARKER_PASS(AfterEarlySimplification)
MODULE_MARKER_PASS(BeforeEarlyOptimization)
MODULE_MARKER_PASS(AfterEarlyOptimization)
FUNCTION_MARKER_PASS(BeforeLoopOptimization)
LOOP_MARKER_PASS(BeforeLICM)
LOOP_MARKER_PASS(AfterLICM)
LOOP_MARKER_PASS(BeforeLoopSimplification)
LOOP_MARKER_PASS(AfterLoopSimplification)
FUNCTION_MARKER_PASS(AfterLoopOptimization)
FUNCTION_MARKER_PASS(BeforeScalarOptimization)
FUNCTION_MARKER_PASS(AfterScalarOptimization)
FUNCTION_MARKER_PASS(BeforeVectorization)
FUNCTION_MARKER_PASS(AfterVectorization)
MODULE_MARKER_PASS(BeforeIntrinsicLowering)
MODULE_MARKER_PASS(AfterIntrinsicLowering)
MODULE_MARKER_PASS(BeforeCleanup)
MODULE_MARKER_PASS(AfterCleanup)
MODULE_MARKER_PASS(AfterOptimization)

#endif
79 changes: 64 additions & 15 deletions src/pipeline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ namespace {
#define JULIA_PASS(ADD_PASS) if (!options.llvm_only) { ADD_PASS; } else do { } while (0)

static void buildEarlySimplificationPipeline(ModulePassManager &MPM, PassBuilder *PB, OptimizationLevel O, const OptimizationOptions &options) JL_NOTSAFEPOINT {
MPM.addPass(BeforeEarlySimplificationMarkerPass());
#ifdef JL_DEBUG_BUILD
addVerificationPasses(MPM, options.llvm_only);
#endif
Expand All @@ -349,9 +350,11 @@ static void buildEarlySimplificationPipeline(ModulePassManager &MPM, PassBuilder
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
}
invokeEarlySimplificationCallbacks(MPM, PB, O);
MPM.addPass(AfterEarlySimplificationMarkerPass());
}

static void buildEarlyOptimizerPipeline(ModulePassManager &MPM, PassBuilder *PB, OptimizationLevel O, const OptimizationOptions &options) JL_NOTSAFEPOINT {
MPM.addPass(BeforeEarlyOptimizationMarkerPass());
invokeOptimizerEarlyCallbacks(MPM, PB, O);
{
CGSCCPassManager CGPM;
Expand Down Expand Up @@ -387,9 +390,11 @@ static void buildEarlyOptimizerPipeline(ModulePassManager &MPM, PassBuilder *PB,
invokePeepholeEPCallbacks(FPM, PB, O);
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
}
MPM.addPass(AfterEarlyOptimizationMarkerPass());
}

static void buildLoopOptimizerPipeline(FunctionPassManager &FPM, PassBuilder *PB, OptimizationLevel O, const OptimizationOptions &options) JL_NOTSAFEPOINT {
FPM.addPass(BeforeLoopOptimizationMarkerPass());
{
LoopPassManager LPM;
if (O.getSpeedupLevel() >= 2) {
Expand All @@ -401,11 +406,13 @@ static void buildLoopOptimizerPipeline(FunctionPassManager &FPM, PassBuilder *PB
}
if (O.getSpeedupLevel() >= 2) {
LoopPassManager LPM;
LPM.addPass(BeforeLICMMarkerPass());
LPM.addPass(LICMPass(LICMOptions()));
LPM.addPass(JuliaLICMPass());
LPM.addPass(SimpleLoopUnswitchPass(/*NonTrivial*/true, true));
LPM.addPass(LICMPass(LICMOptions()));
LPM.addPass(JuliaLICMPass());
LPM.addPass(AfterLICMMarkerPass());
//LICM needs MemorySSA now, so we must use it
FPM.addPass(createFunctionToLoopPassAdaptor(std::move(LPM), /*UseMemorySSA = */true));
}
Expand All @@ -414,6 +421,7 @@ static void buildLoopOptimizerPipeline(FunctionPassManager &FPM, PassBuilder *PB
}
{
LoopPassManager LPM;
LPM.addPass(BeforeLoopSimplificationMarkerPass());
if (O.getSpeedupLevel() >= 2) {
LPM.addPass(LoopInstSimplifyPass());
LPM.addPass(LoopIdiomRecognizePass());
Expand All @@ -424,12 +432,15 @@ static void buildLoopOptimizerPipeline(FunctionPassManager &FPM, PassBuilder *PB
LPM.addPass(LoopFullUnrollPass());
}
invokeLoopOptimizerEndCallbacks(LPM, PB, O);
LPM.addPass(AfterLoopSimplificationMarkerPass());
//We don't know if the loop end callbacks support MSSA
FPM.addPass(createFunctionToLoopPassAdaptor(std::move(LPM), /*UseMemorySSA = */false));
}
FPM.addPass(AfterLoopOptimizationMarkerPass());
}

static void buildScalarOptimizerPipeline(FunctionPassManager &FPM, PassBuilder *PB, OptimizationLevel O, const OptimizationOptions &options) JL_NOTSAFEPOINT {
FPM.addPass(BeforeScalarOptimizationMarkerPass());
if (O.getSpeedupLevel() >= 2) {
JULIA_PASS(FPM.addPass(AllocOptPass()));
FPM.addPass(SROAPass());
Expand Down Expand Up @@ -460,9 +471,11 @@ static void buildScalarOptimizerPipeline(FunctionPassManager &FPM, PassBuilder *
FPM.addPass(LoopDistributePass());
}
invokeScalarOptimizerCallbacks(FPM, PB, O);
FPM.addPass(AfterScalarOptimizationMarkerPass());
}

static void buildVectorPipeline(FunctionPassManager &FPM, PassBuilder *PB, OptimizationLevel O, const OptimizationOptions &options) JL_NOTSAFEPOINT {
FPM.addPass(BeforeVectorizationMarkerPass());
//TODO look into loop vectorize options
FPM.addPass(InjectTLIMappings());
FPM.addPass(LoopVectorizePass());
Expand All @@ -477,9 +490,11 @@ static void buildVectorPipeline(FunctionPassManager &FPM, PassBuilder *PB, Optim
// This unroll will unroll vectorized loops
// as well as loops that we tried but failed to vectorize
FPM.addPass(LoopUnrollPass(LoopUnrollOptions(O.getSpeedupLevel(), /*OnlyWhenForced = */ false, /*ForgetSCEV = */false)));
FPM.addPass(AfterVectorizationMarkerPass());
}

static void buildIntrinsicLoweringPipeline(ModulePassManager &MPM, PassBuilder *PB, OptimizationLevel O, const OptimizationOptions &options) JL_NOTSAFEPOINT {
MPM.addPass(BeforeIntrinsicLoweringMarkerPass());
if (options.lower_intrinsics) {
//TODO barrier pass?
{
Expand Down Expand Up @@ -510,9 +525,11 @@ static void buildIntrinsicLoweringPipeline(ModulePassManager &MPM, PassBuilder *
} else {
JULIA_PASS(MPM.addPass(RemoveNIPass()));
}
MPM.addPass(AfterIntrinsicLoweringMarkerPass());
}

static void buildCleanupPipeline(ModulePassManager &MPM, PassBuilder *PB, OptimizationLevel O, const OptimizationOptions &options) JL_NOTSAFEPOINT {
MPM.addPass(BeforeCleanupMarkerPass());
if (O.getSpeedupLevel() >= 2) {
FunctionPassManager FPM;
JULIA_PASS(FPM.addPass(CombineMulAddPass()));
Expand All @@ -530,9 +547,11 @@ static void buildCleanupPipeline(ModulePassManager &MPM, PassBuilder *PB, Optimi
}
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
}
MPM.addPass(AfterCleanupMarkerPass());
}

static void buildPipeline(ModulePassManager &MPM, PassBuilder *PB, OptimizationLevel O, const OptimizationOptions &options) JL_NOTSAFEPOINT {
MPM.addPass(BeforeOptimizationMarkerPass());
buildEarlySimplificationPipeline(MPM, PB, O, options);
MPM.addPass(AlwaysInlinerPass());
buildEarlyOptimizerPipeline(MPM, PB, O, options);
Expand All @@ -549,40 +568,41 @@ static void buildPipeline(ModulePassManager &MPM, PassBuilder *PB, OptimizationL
}
buildIntrinsicLoweringPipeline(MPM, PB, O, options);
buildCleanupPipeline(MPM, PB, O, options);
MPM.addPass(AfterOptimizationMarkerPass());
}

#undef JULIA_PASS

namespace {
auto createPIC(StandardInstrumentations &SI) JL_NOTSAFEPOINT {
auto PIC = std::make_unique<PassInstrumentationCallbacks>();

void adjustPIC(PassInstrumentationCallbacks &PIC) JL_NOTSAFEPOINT {
//Borrowed from LLVM PassBuilder.cpp:386
#define MODULE_PASS(NAME, CLASS, CREATE_PASS) \
PIC->addClassToPassName(decltype(CREATE_PASS)::name(), NAME);
PIC.addClassToPassName(decltype(CREATE_PASS)::name(), NAME);
#define MODULE_PASS_WITH_PARAMS(NAME, CLASS, CREATE_PASS, PARSER, PARAMS) \
PIC->addClassToPassName(CLASS, NAME);
PIC.addClassToPassName(CLASS, NAME);
#define MODULE_ANALYSIS(NAME, CREATE_PASS) \
PIC->addClassToPassName(decltype(CREATE_PASS)::name(), NAME);
PIC.addClassToPassName(decltype(CREATE_PASS)::name(), NAME);
#define FUNCTION_PASS(NAME, CLASS, CREATE_PASS) \
PIC->addClassToPassName(decltype(CREATE_PASS)::name(), NAME);
PIC.addClassToPassName(decltype(CREATE_PASS)::name(), NAME);
#define FUNCTION_PASS_WITH_PARAMS(NAME, CLASS, CREATE_PASS, PARSER, PARAMS) \
PIC->addClassToPassName(CLASS, NAME);
PIC.addClassToPassName(CLASS, NAME);
#define FUNCTION_ANALYSIS(NAME, CREATE_PASS) \
PIC->addClassToPassName(decltype(CREATE_PASS)::name(), NAME);
PIC.addClassToPassName(decltype(CREATE_PASS)::name(), NAME);
#define LOOPNEST_PASS(NAME, CREATE_PASS) \
PIC->addClassToPassName(decltype(CREATE_PASS)::name(), NAME);
PIC.addClassToPassName(decltype(CREATE_PASS)::name(), NAME);
#define LOOP_PASS(NAME, CLASS, CREATE_PASS) \
PIC->addClassToPassName(decltype(CREATE_PASS)::name(), NAME);
PIC.addClassToPassName(decltype(CREATE_PASS)::name(), NAME);
#define LOOP_PASS_WITH_PARAMS(NAME, CLASS, CREATE_PASS, PARSER, PARAMS) \
PIC->addClassToPassName(CLASS, NAME);
PIC.addClassToPassName(CLASS, NAME);
#define LOOP_ANALYSIS(NAME, CREATE_PASS) \
PIC->addClassToPassName(decltype(CREATE_PASS)::name(), NAME);
PIC.addClassToPassName(decltype(CREATE_PASS)::name(), NAME);
#define CGSCC_PASS(NAME, CLASS, CREATE_PASS) \
PIC->addClassToPassName(decltype(CREATE_PASS)::name(), NAME);
PIC.addClassToPassName(decltype(CREATE_PASS)::name(), NAME);
#define CGSCC_PASS_WITH_PARAMS(NAME, CLASS, CREATE_PASS, PARSER, PARAMS) \
PIC->addClassToPassName(CLASS, NAME);
PIC.addClassToPassName(CLASS, NAME);
#define CGSCC_ANALYSIS(NAME, CREATE_PASS) \
PIC->addClassToPassName(decltype(CREATE_PASS)::name(), NAME);
PIC.addClassToPassName(decltype(CREATE_PASS)::name(), NAME);

#include "llvm-julia-passes.inc"

Expand All @@ -599,7 +619,32 @@ PIC->addClassToPassName(decltype(CREATE_PASS)::name(), NAME);
#undef CGSCC_PASS
#undef CGSCC_PASS_WITH_PARAMS
#undef CGSCC_ANALYSIS
// Marker passes are set separately so that we don't export them by accident
PIC.addClassToPassName("BeforeOptimizationMarkerPass", "BeforeOptimization");
PIC.addClassToPassName("BeforeEarlySimplificationMarkerPass", "BeforeEarlySimplification");
PIC.addClassToPassName("AfterEarlySimplificationMarkerPass", "AfterEarlySimplification");
PIC.addClassToPassName("BeforeEarlyOptimizationMarkerPass", "BeforeEarlyOptimization");
PIC.addClassToPassName("AfterEarlyOptimizationMarkerPass", "AfterEarlyOptimization");
PIC.addClassToPassName("BeforeLoopOptimizationMarkerPass", "BeforeLoopOptimization");
PIC.addClassToPassName("BeforeLICMMarkerPass", "BeforeLICM");
PIC.addClassToPassName("AfterLICMMarkerPass", "AfterLICM");
PIC.addClassToPassName("BeforeLoopSimplificationMarkerPass", "BeforeLoopSimplification");
PIC.addClassToPassName("AfterLoopSimplificationMarkerPass", "AfterLoopSimplification");
PIC.addClassToPassName("AfterLoopOptimizationMarkerPass", "AfterLoopOptimization");
PIC.addClassToPassName("BeforeScalarOptimizationMarkerPass", "BeforeScalarOptimization");
PIC.addClassToPassName("AfterScalarOptimizationMarkerPass", "AfterScalarOptimization");
PIC.addClassToPassName("BeforeVectorizationMarkerPass", "BeforeVectorization");
PIC.addClassToPassName("AfterVectorizationMarkerPass", "AfterVectorization");
PIC.addClassToPassName("BeforeIntrinsicLoweringMarkerPass", "BeforeIntrinsicLowering");
PIC.addClassToPassName("AfterIntrinsicLoweringMarkerPass", "AfterIntrinsicLowering");
PIC.addClassToPassName("BeforeCleanupMarkerPass", "BeforeCleanup");
PIC.addClassToPassName("AfterCleanupMarkerPass", "AfterCleanup");
PIC.addClassToPassName("AfterOptimizationMarkerPass", "AfterOptimization");
}

auto createPIC(StandardInstrumentations &SI) JL_NOTSAFEPOINT {
auto PIC = std::make_unique<PassInstrumentationCallbacks>();
adjustPIC(*PIC);
SI.registerCallbacks(*PIC);
return PIC;
}
Expand Down Expand Up @@ -744,6 +789,10 @@ static llvm::Optional<std::pair<OptimizationLevel, OptimizationOptions>> parseJu
// forward the callbacks to the respective passes. LLVM seems to prefer this,
// and when we add the full pass builder having them directly will be helpful.
void registerCallbacks(PassBuilder &PB) JL_NOTSAFEPOINT {
auto PIC = PB.getPassInstrumentationCallbacks();
if (PIC) {
adjustPIC(*PIC);
}
PB.registerPipelineParsingCallback(
[](StringRef Name, FunctionPassManager &PM,
ArrayRef<PassBuilder::PipelineElement> InnerPipeline) {
Expand Down
Loading

0 comments on commit 5db2c27

Please sign in to comment.