Skip to content

Commit 4ea4825

Browse files
authored
Add LICM pass to hoist julia intrinsics. (#36809)
Fix #36803
1 parent 537664f commit 4ea4825

File tree

4 files changed

+138
-1
lines changed

4 files changed

+138
-1
lines changed

src/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ RUNTIME_SRCS += jitlayers aotcompile debuginfo disasm llvm-simdloop llvm-muladd
5656
llvm-final-gc-lowering llvm-pass-helpers llvm-late-gc-lowering \
5757
llvm-lower-handlers llvm-gc-invariant-verifier llvm-propagate-addrspaces \
5858
llvm-multiversioning llvm-alloc-opt cgmemmgr llvm-api llvm-remove-addrspaces \
59-
llvm-remove-ni
59+
llvm-remove-ni llvm-julia-licm
6060
FLAGS += -I$(shell $(LLVM_CONFIG_HOST) --includedir)
6161
LLVM_LIBS := all
6262
ifeq ($(USE_POLLY),1)

src/aotcompile.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,8 +694,10 @@ void addOptimizationPasses(legacy::PassManagerBase *PM, int opt_level,
694694
// LoopRotate strips metadata from terminator, so run LowerSIMD afterwards
695695
PM->add(createLowerSimdLoopPass()); // Annotate loop marked with "loopinfo" as LLVM parallel loop
696696
PM->add(createLICMPass());
697+
PM->add(createJuliaLICMPass());
697698
PM->add(createLoopUnswitchPass());
698699
PM->add(createLICMPass());
700+
PM->add(createJuliaLICMPass());
699701
// Subsequent passes not stripping metadata from terminator
700702
PM->add(createInstSimplifyLegacyPass());
701703
PM->add(createIndVarSimplifyPass());

src/jitlayers.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ Pass *createGCInvariantVerifierPass(bool Strong);
238238
Pass *createPropagateJuliaAddrspaces();
239239
Pass *createRemoveJuliaAddrspacesPass();
240240
Pass *createRemoveNIPass();
241+
Pass *createJuliaLICMPass();
241242
Pass *createMultiVersioningPass();
242243
Pass *createAllocOptPass();
243244
// Whether the Function is an llvm or julia intrinsic.

src/llvm-julia-licm.cpp

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// This file is a part of Julia. License is MIT: https://julialang.org/license
2+
3+
#include "llvm-version.h"
4+
5+
#include <llvm/Analysis/LoopInfo.h>
6+
#include <llvm/Analysis/LoopPass.h>
7+
#include "llvm/Analysis/LoopIterator.h"
8+
#include <llvm/IR/Dominators.h>
9+
#include <llvm/IR/LegacyPassManager.h>
10+
#include <llvm/Transforms/Utils/LoopUtils.h>
11+
12+
#include "llvm-pass-helpers.h"
13+
14+
#define DEBUG_TYPE "julia-licm"
15+
16+
using namespace llvm;
17+
18+
/*
19+
* Julia LICM pass.
20+
* This takes care of some julia intrinsics that is safe to move around/out of loops but
21+
* can't be handled by LLVM's LICM. These intrinsics can be moved outside of
22+
* loop context as well but it is inside a loop where they matter the most.
23+
*/
24+
25+
namespace {
26+
27+
struct JuliaLICMPass : public LoopPass, public JuliaPassContext {
28+
static char ID;
29+
JuliaLICMPass() : LoopPass(ID) {};
30+
31+
bool runOnLoop(Loop *L, LPPassManager &LPM) override
32+
{
33+
// Get the preheader block to move instructions into,
34+
// required to run this pass.
35+
BasicBlock *preheader = L->getLoopPreheader();
36+
if (!preheader)
37+
return false;
38+
BasicBlock *header = L->getHeader();
39+
initFunctions(*header->getModule());
40+
// Also require `gc_preserve_begin_func` whereas
41+
// `gc_preserve_end_func` is optional since the input to
42+
// `gc_preserve_end_func` must be from `gc_preserve_begin_func`.
43+
if (!gc_preserve_begin_func)
44+
return false;
45+
auto LI = &getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
46+
auto DT = &getAnalysis<DominatorTreeWrapperPass>().getDomTree();
47+
48+
// Lazy initialization of exit blocks insertion points.
49+
bool exit_pts_init = false;
50+
SmallVector<Instruction*, 8> _exit_pts;
51+
auto get_exit_pts = [&] () -> ArrayRef<Instruction*> {
52+
if (!exit_pts_init) {
53+
exit_pts_init = true;
54+
SmallVector<BasicBlock*, 8> exit_bbs;
55+
L->getUniqueExitBlocks(exit_bbs);
56+
for (BasicBlock *bb: exit_bbs) {
57+
_exit_pts.push_back(&*bb->getFirstInsertionPt());
58+
}
59+
}
60+
return _exit_pts;
61+
};
62+
63+
bool changed = false;
64+
// Scan in the right order so that we'll hoist the `begin`
65+
// before we consider sinking `end`.
66+
LoopBlocksRPO worklist(L);
67+
worklist.perform(LI);
68+
for (auto *bb : worklist) {
69+
for (BasicBlock::iterator II = bb->begin(), E = bb->end(); II != E;) {
70+
auto call = dyn_cast<CallInst>(&*II++);
71+
if (!call)
72+
continue;
73+
auto callee = call->getCalledValue();
74+
assert(callee);
75+
// It is always legal to extend the preserve period
76+
// so we only need to make sure it is legal to move/clone
77+
// the calls.
78+
// If all the input arguments dominates the whole loop we can
79+
// hoist the `begin` and if a `begin` dominates the loop the
80+
// corresponding `end` can be moved to the loop exit.
81+
if (callee == gc_preserve_begin_func) {
82+
bool canhoist = true;
83+
for (Use &U : call->arg_operands()) {
84+
// Check if all arguments are generated outside the loop
85+
auto origin = dyn_cast<Instruction>(U.get());
86+
if (!origin)
87+
continue;
88+
if (!DT->properlyDominates(origin->getParent(), header)) {
89+
canhoist = false;
90+
break;
91+
}
92+
}
93+
if (!canhoist)
94+
continue;
95+
call->moveBefore(preheader->getTerminator());
96+
changed = true;
97+
}
98+
else if (callee == gc_preserve_end_func) {
99+
auto begin = cast<Instruction>(call->getArgOperand(0));
100+
if (!DT->properlyDominates(begin->getParent(), header))
101+
continue;
102+
changed = true;
103+
auto exit_pts = get_exit_pts();
104+
if (exit_pts.empty()) {
105+
call->eraseFromParent();
106+
continue;
107+
}
108+
call->moveBefore(exit_pts[0]);
109+
for (unsigned i = 1; i < exit_pts.size(); i++) {
110+
// Clone exit
111+
CallInst::Create(call, {}, exit_pts[i]);
112+
}
113+
}
114+
}
115+
}
116+
return changed;
117+
}
118+
119+
void getAnalysisUsage(AnalysisUsage &AU) const override
120+
{
121+
getLoopAnalysisUsage(AU);
122+
}
123+
};
124+
125+
char JuliaLICMPass::ID = 0;
126+
static RegisterPass<JuliaLICMPass>
127+
Y("JuliaLICM", "LICM for julia specific intrinsics.",
128+
false, false);
129+
}
130+
131+
Pass *createJuliaLICMPass()
132+
{
133+
return new JuliaLICMPass();
134+
}

0 commit comments

Comments
 (0)