Skip to content

Commit ae3bd5b

Browse files
committed
Add a pass to propagate addrspace information
Some LLVM passes don't handle addrspacecast too well, so try to minimize addrspace cast transitions where legal according to our invariants.
1 parent cc19a7e commit ae3bd5b

File tree

6 files changed

+261
-2
lines changed

6 files changed

+261
-2
lines changed

src/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ endif
5353
LLVMLINK :=
5454

5555
ifeq ($(JULIACODEGEN),LLVM)
56-
SRCS += codegen jitlayers disasm debuginfo llvm-simdloop llvm-ptls llvm-late-gc-lowering llvm-lower-handlers llvm-gc-invariant-verifier cgmemmgr
56+
SRCS += codegen jitlayers disasm debuginfo llvm-simdloop llvm-ptls llvm-late-gc-lowering llvm-lower-handlers llvm-gc-invariant-verifier llvm-propagate-addrspaces cgmemmgr
5757
FLAGS += -I$(shell $(LLVM_CONFIG_HOST) --includedir)
5858
LLVM_LIBS := all
5959
ifeq ($(USE_POLLY),1)

src/disasm.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@
2828

2929
#include "llvm-version.h"
3030
#include <llvm/Object/ObjectFile.h>
31+
#if JL_LLVM_VERSION < 50000
3132
#include <llvm/Support/MachO.h>
3233
#include <llvm/Support/COFF.h>
34+
#endif
3335
#include <llvm/MC/MCInst.h>
3436
#include <llvm/MC/MCStreamer.h>
3537
#include <llvm/MC/MCSubtargetInfo.h>

src/jitlayers.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ void addOptimizationPasses(PassManager *PM, int opt_level)
154154
#endif
155155
return;
156156
}
157+
PM->add(createPropagateJuliaAddrspaces());
157158
#if JL_LLVM_VERSION >= 30700
158159
PM->add(createTargetTransformInfoWrapperPass(jl_TargetMachine->getTargetIRAnalysis()));
159160
#else
@@ -173,6 +174,7 @@ void addOptimizationPasses(PassManager *PM, int opt_level)
173174
}
174175
// list of passes from vmkit
175176
PM->add(createCFGSimplificationPass()); // Clean up disgusting code
177+
PM->add(createDeadInstEliminationPass());
176178
PM->add(createPromoteMemoryToRegisterPass()); // Kill useless allocas
177179

178180
// Due to bugs and missing features LLVM < 5.0, does not properly propagate
@@ -1441,7 +1443,7 @@ class JuliaPipeline : public ModulePass {
14411443
#else
14421444
PassManager PM;
14431445
#endif
1444-
addOptimizationPasses(&PM);
1446+
addOptimizationPasses(&PM, 3);
14451447
PM.run(M);
14461448
return true;
14471449
}

src/jitlayers.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ Pass *createLowerPTLSPass(bool imaging_mode);
250250
Pass *createLateLowerGCFramePass();
251251
Pass *createLowerExcHandlersPass();
252252
Pass *createGCInvariantVerifierPass(bool Strong);
253+
Pass *createPropagateJuliaAddrspaces();
253254
// Whether the Function is an llvm or julia intrinsic.
254255
static inline bool isIntrinsicFunction(Function *F)
255256
{

src/llvm-propagate-addrspaces.cpp

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
// This file is a part of Julia. License is MIT: https://julialang.org/license
2+
3+
#include <llvm/ADT/SmallPtrSet.h>
4+
#include <llvm/Analysis/CFG.h>
5+
#include <llvm/IR/Value.h>
6+
#include <llvm/IR/ValueMap.h>
7+
#include <llvm/IR/Constants.h>
8+
#include <llvm/IR/Dominators.h>
9+
#include <llvm/IR/Function.h>
10+
#include <llvm/IR/Instructions.h>
11+
#include <llvm/IR/IntrinsicInst.h>
12+
#include <llvm/IR/InstVisitor.h>
13+
#include <llvm/IR/CallSite.h>
14+
#include <llvm/IR/Module.h>
15+
#include <llvm/IR/IRBuilder.h>
16+
#include <llvm/IR/Verifier.h>
17+
#include <llvm/Pass.h>
18+
#include <llvm/Support/Debug.h>
19+
20+
#include "llvm-version.h"
21+
#include "codegen_shared.h"
22+
#include "julia.h"
23+
24+
#define DEBUG_TYPE "propagate_julia_addrspaces"
25+
26+
using namespace llvm;
27+
28+
/* This pass performs propagation of addrspace information that is legal from
29+
the frontend definition, but illegal by general IR semantics. In particular,
30+
this includes:
31+
- Changing the address space of a load/store if the base pointer is
32+
in an untracked address space
33+
- Commuting GEPs and addrspace casts
34+
35+
This is most useful for removing superflous casts that can inhibit LLVM
36+
optimizations.
37+
*/
38+
39+
struct PropagateJuliaAddrspaces : public FunctionPass, public InstVisitor<PropagateJuliaAddrspaces> {
40+
static char ID;
41+
DenseMap<Value *, Value *> LiftingMap;
42+
SmallPtrSet<Value *, 4> Visited;
43+
std::vector<Instruction *> ToDelete;
44+
std::vector<std::pair<Instruction *, Instruction *>> ToInsert;
45+
PropagateJuliaAddrspaces() : FunctionPass(ID) {};
46+
47+
public:
48+
bool runOnFunction(Function &F) override;
49+
Value *LiftPointer(Value *V, Type *LocTy = nullptr, Instruction *InsertPt=nullptr);
50+
void visitStoreInst(StoreInst &SI);
51+
void visitLoadInst(LoadInst &LI);
52+
void visitMemSetInst(MemSetInst &MI);
53+
void visitMemTransferInst(MemTransferInst &MTI);
54+
};
55+
56+
bool PropagateJuliaAddrspaces::runOnFunction(Function &F) {
57+
visit(F);
58+
for (auto it : ToInsert)
59+
it.first->insertBefore(it.second);
60+
for (Instruction *I : ToDelete)
61+
I->eraseFromParent();
62+
ToInsert.clear();
63+
ToDelete.clear();
64+
LiftingMap.clear();
65+
Visited.clear();
66+
return true;
67+
}
68+
69+
static unsigned getValueAddrSpace(Value *V) {
70+
return cast<PointerType>(V->getType())->getAddressSpace();
71+
}
72+
73+
static bool isSpecialAS(unsigned AS) {
74+
return AddressSpace::FirstSpecial <= AS && AS <= AddressSpace::LastSpecial;
75+
}
76+
77+
Value *PropagateJuliaAddrspaces::LiftPointer(Value *V, Type *LocTy, Instruction *InsertPt) {
78+
SmallVector<Value *, 4> Stack;
79+
Value *CurrentV = V;
80+
// Follow pointer casts back, see if we're based on a pointer in
81+
// an untracked address space, in which case we're allowed to drop
82+
// intermediate addrspace casts.
83+
while (true) {
84+
Stack.push_back(CurrentV);
85+
if (isa<BitCastInst>(CurrentV))
86+
CurrentV = cast<BitCastInst>(CurrentV)->getOperand(0);
87+
else if (isa<AddrSpaceCastInst>(CurrentV)) {
88+
CurrentV = cast<AddrSpaceCastInst>(CurrentV)->getOperand(0);
89+
if (!isSpecialAS(getValueAddrSpace(CurrentV)))
90+
break;
91+
}
92+
else if (isa<GetElementPtrInst>(CurrentV)) {
93+
if (LiftingMap.count(CurrentV)) {
94+
CurrentV = LiftingMap[CurrentV];
95+
break;
96+
} else if (Visited.count(CurrentV)) {
97+
return nullptr;
98+
}
99+
Visited.insert(CurrentV);
100+
CurrentV = cast<GetElementPtrInst>(CurrentV)->getOperand(0);
101+
} else
102+
break;
103+
}
104+
if (!CurrentV->getType()->isPointerTy())
105+
return nullptr;
106+
if (isSpecialAS(getValueAddrSpace(CurrentV)))
107+
return nullptr;
108+
// Ok, we're allowed to change the address space of this load, go back and
109+
// reconstitute any GEPs in the new address space.
110+
for (Value *V : llvm::reverse(Stack)) {
111+
GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(V);
112+
if (!GEP)
113+
continue;
114+
if (LiftingMap.count(GEP)) {
115+
CurrentV = LiftingMap[GEP];
116+
continue;
117+
}
118+
GetElementPtrInst *NewGEP = cast<GetElementPtrInst>(GEP->clone());
119+
ToInsert.push_back(std::make_pair(NewGEP, GEP));
120+
Type *GEPTy = GEP->getSourceElementType();
121+
NewGEP->setSourceElementType(GEPTy);
122+
SmallVector<Value *, 4> Operands;
123+
auto it = GEP->op_begin(); ++it; // skip pointer operand
124+
for (;it != GEP->op_end(); ++it) {
125+
Operands.push_back(*it);
126+
}
127+
NewGEP->mutateType(GetElementPtrInst::getGEPReturnType(GEPTy, CurrentV, Operands));
128+
if (cast<PointerType>(CurrentV->getType())->getElementType() != GEPTy) {
129+
auto *BCI = new BitCastInst(CurrentV, GEPTy->getPointerTo());
130+
ToInsert.push_back(std::make_pair(BCI, NewGEP));
131+
CurrentV = BCI;
132+
}
133+
NewGEP->setOperand(GetElementPtrInst::getPointerOperandIndex(), CurrentV);
134+
LiftingMap[GEP] = NewGEP;
135+
CurrentV = NewGEP;
136+
}
137+
if (LocTy && cast<PointerType>(CurrentV->getType())->getElementType() != LocTy) {
138+
auto *BCI = new BitCastInst(CurrentV, LocTy->getPointerTo());
139+
ToInsert.push_back(std::make_pair(BCI, InsertPt));
140+
CurrentV = BCI;
141+
}
142+
return CurrentV;
143+
}
144+
145+
void PropagateJuliaAddrspaces::visitLoadInst(LoadInst &LI) {
146+
unsigned AS = LI.getPointerAddressSpace();
147+
if (!isSpecialAS(AS))
148+
return;
149+
Value *Replacement = LiftPointer(LI.getPointerOperand(), LI.getType(), &LI);
150+
if (!Replacement)
151+
return;
152+
LI.setOperand(LoadInst::getPointerOperandIndex(), Replacement);
153+
}
154+
155+
void PropagateJuliaAddrspaces::visitStoreInst(StoreInst &SI) {
156+
unsigned AS = SI.getPointerAddressSpace();
157+
if (!isSpecialAS(AS))
158+
return;
159+
Value *Replacement = LiftPointer(SI.getPointerOperand(), SI.getValueOperand()->getType(), &SI);
160+
if (!Replacement)
161+
return;
162+
SI.setOperand(StoreInst::getPointerOperandIndex(), Replacement);
163+
}
164+
165+
void PropagateJuliaAddrspaces::visitMemSetInst(MemSetInst &MI) {
166+
unsigned AS = MI.getDestAddressSpace();
167+
if (!isSpecialAS(AS))
168+
return;
169+
Value *Replacement = LiftPointer(MI.getRawDest());
170+
if (!Replacement)
171+
return;
172+
Value *TheFn = Intrinsic::getDeclaration(MI.getModule(), Intrinsic::memset,
173+
{Replacement->getType(), MI.getOperand(1)->getType()});
174+
CallInst *CI = CallInst::Create(TheFn, {Replacement, MI.getOperand(1)}, "");
175+
CI->takeName(&MI);
176+
#if JL_LLVM_VERSION >= 50000
177+
CI->copyMetadata(MI);
178+
#else
179+
SmallVector<std::pair<unsigned, MDNode *>, 4> TheMDs;
180+
MI.getAllMetadataOtherThanDebugLoc(TheMDs);
181+
for (const auto &MD : TheMDs)
182+
CI->setMetadata(MD.first, MD.second);
183+
CI->setDebugLoc(MI.getDebugLoc());
184+
#endif
185+
ToInsert.push_back(std::make_pair(CI, &MI));
186+
ToDelete.push_back(&MI);
187+
}
188+
189+
void PropagateJuliaAddrspaces::visitMemTransferInst(MemTransferInst &MTI) {
190+
unsigned DestAS = MTI.getDestAddressSpace();
191+
unsigned SrcAS = MTI.getSourceAddressSpace();
192+
if (!isSpecialAS(DestAS) && !isSpecialAS(SrcAS))
193+
return;
194+
Value *Dest = MTI.getRawDest();
195+
if (isSpecialAS(DestAS)) {
196+
Value *Replacement = LiftPointer(Dest, cast<PointerType>(Dest->getType())->getElementType(), &MTI);
197+
if (Replacement)
198+
Dest = Replacement;
199+
}
200+
Value *Src = MTI.getRawSource();
201+
if (isSpecialAS(SrcAS)) {
202+
Value *Replacement = LiftPointer(Src, cast<PointerType>(Src->getType())->getElementType(), &MTI);
203+
if (Replacement)
204+
Src = Replacement;
205+
}
206+
if (Dest == MTI.getRawDest() && Src == MTI.getRawSource())
207+
return;
208+
Value *TheFn = Intrinsic::getDeclaration(MTI.getModule(), MTI.getIntrinsicID(),
209+
{Dest->getType(), Src->getType(),
210+
MTI.getOperand(2)->getType()});
211+
CallInst *CI = CallInst::Create(TheFn, {Dest, Src,
212+
MTI.getOperand(2), MTI.getOperand(3), MTI.getOperand(4)},
213+
"");
214+
CI->takeName(&MTI);
215+
#if JL_LLVM_VERSION >= 50000
216+
CI->copyMetadata(MTI);
217+
#else
218+
SmallVector<std::pair<unsigned, MDNode *>, 4> TheMDs;
219+
MTI.getAllMetadataOtherThanDebugLoc(TheMDs);
220+
for (const auto &MD : TheMDs)
221+
CI->setMetadata(MD.first, MD.second);
222+
CI->setDebugLoc(MTI.getDebugLoc());
223+
#endif
224+
ToInsert.push_back(std::make_pair(CI, &MTI));
225+
ToDelete.push_back(&MTI);
226+
}
227+
228+
char PropagateJuliaAddrspaces::ID = 0;
229+
static RegisterPass<PropagateJuliaAddrspaces> X("PropagateJuliaAddrspaces", "Propagate (non-)rootedness information", false, false);
230+
231+
Pass *createPropagateJuliaAddrspaces() {
232+
return new PropagateJuliaAddrspaces();
233+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
; RUN: opt -load libjulia.so -PropagateJuliaAddrspaces -dce -S %s | FileCheck %s
2+
3+
define i64 @simple() {
4+
; CHECK-LABEL: @simple
5+
; CHECK-NOT: addrspace(11)
6+
%stack = alloca i64
7+
%casted = addrspacecast i64 *%stack to i64 addrspace(11)*
8+
%loaded = load i64, i64 addrspace(11)* %casted
9+
ret i64 %loaded
10+
}
11+
12+
define i64 @twogeps() {
13+
; CHECK-LABEL: @twogeps
14+
; CHECK-NOT: addrspace(11)
15+
%stack = alloca i64
16+
%casted = addrspacecast i64 *%stack to i64 addrspace(11)*
17+
%gep1 = getelementptr i64, i64 addrspace(11)* %casted, i64 1
18+
%gep2 = getelementptr i64, i64 addrspace(11)* %gep1, i64 1
19+
%loaded = load i64, i64 addrspace(11)* %gep2
20+
ret i64 %loaded
21+
}

0 commit comments

Comments
 (0)