Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(164)

Side by Side Diff: lib/Transforms/NaCl/ResolvePNaClIntrinsics.cpp

Issue 939073008: Rebased PNaCl localmods in LLVM to 223109 (Closed)
Patch Set: undo localmod Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « lib/Transforms/NaCl/ReplacePtrsWithInts.cpp ('k') | lib/Transforms/NaCl/RewriteAtomics.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 //===- ResolvePNaClIntrinsics.cpp - Resolve calls to PNaCl intrinsics ----====//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This pass resolves calls to PNaCl stable bitcode intrinsics. It is
11 // normally run in the PNaCl translator.
12 //
13 // Running AddPNaClExternalDeclsPass is a precondition for running this
14 // pass. They are separate because one is a ModulePass and the other is
15 // a FunctionPass.
16 //
17 //===----------------------------------------------------------------------===//
18
19 #include "llvm/ADT/SmallVector.h"
20 #include "llvm/ADT/Triple.h"
21 #include "llvm/IR/Constant.h"
22 #include "llvm/IR/DerivedTypes.h"
23 #include "llvm/IR/IRBuilder.h"
24 #include "llvm/IR/InlineAsm.h"
25 #include "llvm/IR/Instructions.h"
26 #include "llvm/IR/IntrinsicInst.h"
27 #include "llvm/IR/Intrinsics.h"
28 #include "llvm/IR/Module.h"
29 #include "llvm/IR/NaClAtomicIntrinsics.h"
30 #include "llvm/IR/Value.h"
31 #include "llvm/Pass.h"
32 #include "llvm/Support/Compiler.h"
33 #include "llvm/Transforms/NaCl.h"
34 #include "llvm/Transforms/Utils/Local.h"
35 #if defined(PNACL_BROWSER_TRANSLATOR)
36 #include "native_client/src/untrusted/nacl/pnacl.h"
37 #endif
38
39 using namespace llvm;
40
41 namespace {
42 class ResolvePNaClIntrinsics : public FunctionPass {
43 public:
44 ResolvePNaClIntrinsics() : FunctionPass(ID) {
45 initializeResolvePNaClIntrinsicsPass(*PassRegistry::getPassRegistry());
46 }
47
48 static char ID;
49 virtual bool runOnFunction(Function &F);
50
51 /// Interface specifying how intrinsic calls should be resolved. Each
52 /// intrinsic call handled by the implementor will be visited by the
53 /// doResolve method.
54 class CallResolver {
55 public:
56 /// Called once per \p Call to the intrinsic in the module.
57 /// Returns true if the Function was changed.
58 bool resolve(IntrinsicInst *Call) {
59 // To be a well-behaving FunctionPass, don't touch uses in other
60 // functions. These will be handled when the pass manager gets to
61 // those functions.
62 if (Call->getParent()->getParent() == &F)
63 return doResolve(Call);
64 return false;
65 }
66 Function *getDeclaration() const { return doGetDeclaration(); }
67 std::string getName() { return Intrinsic::getName(IntrinsicID); }
68
69 protected:
70 Function &F;
71 Module *M;
72 Intrinsic::ID IntrinsicID;
73
74 CallResolver(Function &F, Intrinsic::ID IntrinsicID)
75 : F(F), M(F.getParent()), IntrinsicID(IntrinsicID) {}
76 virtual ~CallResolver() {}
77
78 /// The following pure virtual methods must be defined by
79 /// implementors, and will be called once per intrinsic call.
80 /// NOTE: doGetDeclaration() should only "get" the intrinsic declaration
81 /// and not *add* decls to the module. Declarations should be added
82 /// up front by the AddPNaClExternalDecls module pass.
83 virtual Function *doGetDeclaration() const = 0;
84 /// Returns true if the Function was changed.
85 virtual bool doResolve(IntrinsicInst *Call) = 0;
86
87 private:
88 CallResolver(const CallResolver &) LLVM_DELETED_FUNCTION;
89 CallResolver &operator=(const CallResolver &) LLVM_DELETED_FUNCTION;
90 };
91
92 private:
93 /// Visit all calls matching the \p Resolver's declaration, and invoke
94 /// the CallResolver methods on each of them.
95 bool visitCalls(CallResolver &Resolver);
96 };
97
98 /// Rewrite intrinsic calls to another function.
99 class IntrinsicCallToFunctionCall :
100 public ResolvePNaClIntrinsics::CallResolver {
101 public:
102 IntrinsicCallToFunctionCall(Function &F, Intrinsic::ID IntrinsicID,
103 const char *TargetFunctionName)
104 : CallResolver(F, IntrinsicID),
105 TargetFunction(M->getFunction(TargetFunctionName)) {
106 // Expect to find the target function for this intrinsic already
107 // declared, even if it is never used.
108 if (!TargetFunction)
109 report_fatal_error(std::string(
110 "Expected to find external declaration of ") + TargetFunctionName);
111 }
112 virtual ~IntrinsicCallToFunctionCall() {}
113
114 private:
115 Function *TargetFunction;
116
117 virtual Function *doGetDeclaration() const {
118 return Intrinsic::getDeclaration(M, IntrinsicID);
119 }
120
121 virtual bool doResolve(IntrinsicInst *Call) {
122 Call->setCalledFunction(TargetFunction);
123 if (IntrinsicID == Intrinsic::nacl_setjmp) {
124 // The "returns_twice" attribute is required for correctness,
125 // otherwise the backend will reuse stack slots in a way that is
126 // incorrect for setjmp(). See:
127 // https://code.google.com/p/nativeclient/issues/detail?id=3733
128 Call->setCanReturnTwice();
129 }
130 return true;
131 }
132
133 IntrinsicCallToFunctionCall(const IntrinsicCallToFunctionCall &)
134 LLVM_DELETED_FUNCTION;
135 IntrinsicCallToFunctionCall &operator=(const IntrinsicCallToFunctionCall &)
136 LLVM_DELETED_FUNCTION;
137 };
138
139 /// Rewrite intrinsic calls to a constant whose value is determined by a
140 /// functor. This functor is called once per Call, and returns a
141 /// Constant that should replace the Call.
142 template <class Callable>
143 class ConstantCallResolver : public ResolvePNaClIntrinsics::CallResolver {
144 public:
145 ConstantCallResolver(Function &F, Intrinsic::ID IntrinsicID,
146 Callable Functor)
147 : CallResolver(F, IntrinsicID), Functor(Functor) {}
148 virtual ~ConstantCallResolver() {}
149
150 private:
151 Callable Functor;
152
153 virtual Function *doGetDeclaration() const {
154 return Intrinsic::getDeclaration(M, IntrinsicID);
155 }
156
157 virtual bool doResolve(IntrinsicInst *Call) {
158 Constant *C = Functor(Call);
159 Call->replaceAllUsesWith(C);
160 Call->eraseFromParent();
161 return true;
162 }
163
164 ConstantCallResolver(const ConstantCallResolver &) LLVM_DELETED_FUNCTION;
165 ConstantCallResolver &operator=(const ConstantCallResolver &)
166 LLVM_DELETED_FUNCTION;
167 };
168
169 /// Resolve __nacl_atomic_is_lock_free to true/false at translation
170 /// time. PNaCl's currently supported platforms all support lock-free
171 /// atomics at byte sizes {1,2,4,8} except for MIPS arch that supports
172 /// lock-free atomics at byte sizes {1,2,4}, and the alignment of the
173 /// pointer is always expected to be natural (as guaranteed by C11 and
174 /// C++11). PNaCl's Module-level ABI verification checks that the byte
175 /// size is constant and in {1,2,4,8}.
176 struct IsLockFreeToConstant {
177 Constant *operator()(CallInst *Call) {
178 uint64_t MaxLockFreeByteSize = 8;
179 const APInt &ByteSize =
180 cast<Constant>(Call->getOperand(0))->getUniqueInteger();
181
182 # if defined(PNACL_BROWSER_TRANSLATOR)
183 switch (__builtin_nacl_target_arch()) {
184 case PnaclTargetArchitectureX86_32:
185 case PnaclTargetArchitectureX86_64:
186 case PnaclTargetArchitectureARM_32:
187 break;
188 case PnaclTargetArchitectureMips_32:
189 MaxLockFreeByteSize = 4;
190 break;
191 default:
192 report_fatal_error("Unhandled arch from __builtin_nacl_target_arch()");
193 }
194 # elif defined(__i386__) || defined(__x86_64__) || defined(__arm__) || \
195 defined(__pnacl__)
196 // TODO(jfb): MaxLockFreeByteSize should actually be selected at runtime.
197 // Continue.
198 # elif defined(__mips__)
199 MaxLockFreeByteSize = 4;
200 # else
201 # error "Unknown architecture"
202 # endif
203
204 bool IsLockFree = ByteSize.ule(MaxLockFreeByteSize);
205 Constant *C = ConstantInt::get(Call->getType(), IsLockFree);
206 return C;
207 }
208 };
209
210 /// Rewrite atomic intrinsics to LLVM IR instructions.
211 class AtomicCallResolver : public ResolvePNaClIntrinsics::CallResolver {
212 public:
213 AtomicCallResolver(Function &F,
214 const NaCl::AtomicIntrinsics::AtomicIntrinsic *I)
215 : CallResolver(F, I->ID), I(I) {}
216 virtual ~AtomicCallResolver() {}
217
218 private:
219 const NaCl::AtomicIntrinsics::AtomicIntrinsic *I;
220
221 virtual Function *doGetDeclaration() const { return I->getDeclaration(M); }
222
223 virtual bool doResolve(IntrinsicInst *Call) {
224 // Assume the @llvm.nacl.atomic.* intrinsics follow the PNaCl ABI:
225 // this should have been checked by the verifier.
226 bool isVolatile = false;
227 SynchronizationScope SS = CrossThread;
228 Instruction *I;
229 SmallVector<Instruction *, 16> MaybeDead;
230
231 switch (Call->getIntrinsicID()) {
232 default:
233 llvm_unreachable("unknown atomic intrinsic");
234 case Intrinsic::nacl_atomic_load:
235 I = new LoadInst(Call->getArgOperand(0), "", isVolatile,
236 alignmentFromPointer(Call->getArgOperand(0)),
237 thawMemoryOrder(Call->getArgOperand(1)), SS, Call);
238 break;
239 case Intrinsic::nacl_atomic_store:
240 I = new StoreInst(Call->getArgOperand(0), Call->getArgOperand(1),
241 isVolatile,
242 alignmentFromPointer(Call->getArgOperand(1)),
243 thawMemoryOrder(Call->getArgOperand(2)), SS, Call);
244 break;
245 case Intrinsic::nacl_atomic_rmw:
246 I = new AtomicRMWInst(thawRMWOperation(Call->getArgOperand(0)),
247 Call->getArgOperand(1), Call->getArgOperand(2),
248 thawMemoryOrder(Call->getArgOperand(3)), SS, Call);
249 break;
250 case Intrinsic::nacl_atomic_cmpxchg:
251 I = new AtomicCmpXchgInst(
252 Call->getArgOperand(0), Call->getArgOperand(1),
253 Call->getArgOperand(2), thawMemoryOrder(Call->getArgOperand(3)),
254 thawMemoryOrder(Call->getArgOperand(4)), SS, Call);
255
256 // cmpxchg returns struct { T loaded, i1 success } whereas the PNaCl
257 // intrinsic only returns the loaded value. The Call can't simply be
258 // replaced. Identify loaded+success structs that can be replaced by the
259 // cmxpchg's returned struct.
260 {
261 Instruction *Loaded = nullptr;
262 Instruction *Success = nullptr;
263 for (User *CallUser : Call->users()) {
264 if (auto ICmp = dyn_cast<ICmpInst>(CallUser)) {
265 // Identify comparisons for cmpxchg's success.
266 if (ICmp->getPredicate() != CmpInst::ICMP_EQ)
267 continue;
268 Value *LHS = ICmp->getOperand(0);
269 Value *RHS = ICmp->getOperand(1);
270 Value *Old = I->getOperand(1);
271 if (RHS != Old && LHS != Old) // Call is either RHS or LHS.
272 continue; // The comparison isn't checking for cmpxchg's success.
273
274 // Recognize the pattern creating struct { T loaded, i1 success }:
275 // it can be replaced by cmpxchg's result.
276 for (User *InsUser : ICmp->users()) {
277 if (!isa<Instruction>(InsUser) ||
278 cast<Instruction>(InsUser)->getParent() != Call->getParent())
279 continue; // Different basic blocks, don't be clever.
280 auto Ins = dyn_cast<InsertValueInst>(InsUser);
281 if (!Ins)
282 continue;
283 auto InsTy = dyn_cast<StructType>(Ins->getType());
284 if (!InsTy)
285 continue;
286 if (!InsTy->isLayoutIdentical(cast<StructType>(I->getType())))
287 continue; // Not a struct { T loaded, i1 success }.
288 if (Ins->getNumIndices() != 1 || Ins->getIndices()[0] != 1)
289 continue; // Not an insert { T, i1 } %something, %success, 1.
290 auto TIns = dyn_cast<InsertValueInst>(Ins->getAggregateOperand());
291 if (!TIns)
292 continue; // T wasn't inserted into the struct, don't be clever.
293 if (!isa<UndefValue>(TIns->getAggregateOperand()))
294 continue; // Not an insert into an undef value, don't be clever.
295 if (TIns->getInsertedValueOperand() != Call)
296 continue; // Not inserting the loaded value.
297 if (TIns->getNumIndices() != 1 || TIns->getIndices()[0] != 0)
298 continue; // Not an insert { T, i1 } undef, %loaded, 0.
299 // Hooray! This is the struct you're looking for.
300
301 // Keep track of values extracted from the struct, instead of
302 // recreating them.
303 for (User *StructUser : Ins->users()) {
304 if (auto Extract = dyn_cast<ExtractValueInst>(StructUser)) {
305 MaybeDead.push_back(Extract);
306 if (!Loaded && Extract->getIndices()[0] == 0) {
307 Loaded = cast<Instruction>(StructUser);
308 Loaded->moveBefore(Call);
309 } else if (!Success && Extract->getIndices()[0] == 1) {
310 Success = cast<Instruction>(StructUser);
311 Success->moveBefore(Call);
312 }
313 }
314 }
315
316 MaybeDead.push_back(Ins);
317 MaybeDead.push_back(TIns);
318 Ins->replaceAllUsesWith(I);
319 }
320
321 MaybeDead.push_back(ICmp);
322 if (!Success)
323 Success = ExtractValueInst::Create(I, 1, "success", Call);
324 ICmp->replaceAllUsesWith(Success);
325 }
326 }
327
328 // Clean up remaining uses of the loaded value, if any. Later code will
329 // try to replace Call with I, make sure the types match.
330 if (Call->hasNUsesOrMore(1)) {
331 if (!Loaded)
332 Loaded = ExtractValueInst::Create(I, 0, "loaded", Call);
333 I = Loaded;
334 } else {
335 I = nullptr;
336 }
337
338 if (Loaded)
339 MaybeDead.push_back(Loaded);
340 if (Success)
341 MaybeDead.push_back(Success);
342 }
343 break;
344 case Intrinsic::nacl_atomic_fence:
345 I = new FenceInst(M->getContext(),
346 thawMemoryOrder(Call->getArgOperand(0)), SS, Call);
347 break;
348 case Intrinsic::nacl_atomic_fence_all: {
349 FunctionType *FTy =
350 FunctionType::get(Type::getVoidTy(M->getContext()), false);
351 std::string AsmString; // Empty.
352 std::string Constraints("~{memory}");
353 bool HasSideEffect = true;
354 CallInst *Asm = CallInst::Create(
355 InlineAsm::get(FTy, AsmString, Constraints, HasSideEffect), "", Call);
356 Asm->setDebugLoc(Call->getDebugLoc());
357 I = new FenceInst(M->getContext(), SequentiallyConsistent, SS, Asm);
358 Asm = CallInst::Create(
359 InlineAsm::get(FTy, AsmString, Constraints, HasSideEffect), "", I);
360 Asm->setDebugLoc(Call->getDebugLoc());
361 } break;
362 }
363
364 if (I) {
365 I->setName(Call->getName());
366 I->setDebugLoc(Call->getDebugLoc());
367 Call->replaceAllUsesWith(I);
368 }
369 Call->eraseFromParent();
370
371 // Remove dead code.
372 for (auto Kill : MaybeDead)
373 if (isInstructionTriviallyDead(Kill))
374 Kill->eraseFromParent();
375
376 return true;
377 }
378
379 unsigned alignmentFromPointer(const Value *Ptr) const {
380 const PointerType *PtrType = cast<PointerType>(Ptr->getType());
381 unsigned BitWidth = PtrType->getElementType()->getIntegerBitWidth();
382 return BitWidth / 8;
383 }
384
385 AtomicOrdering thawMemoryOrder(const Value *MemoryOrder) const {
386 NaCl::MemoryOrder MO = (NaCl::MemoryOrder)
387 cast<Constant>(MemoryOrder)->getUniqueInteger().getLimitedValue();
388 switch (MO) {
389 // Only valid values should pass validation.
390 default: llvm_unreachable("unknown memory order");
391 case NaCl::MemoryOrderRelaxed: return Monotonic;
392 // TODO Consume is unspecified by LLVM's internal IR.
393 case NaCl::MemoryOrderConsume: return SequentiallyConsistent;
394 case NaCl::MemoryOrderAcquire: return Acquire;
395 case NaCl::MemoryOrderRelease: return Release;
396 case NaCl::MemoryOrderAcquireRelease: return AcquireRelease;
397 case NaCl::MemoryOrderSequentiallyConsistent: return SequentiallyConsistent;
398 }
399 }
400
401 AtomicRMWInst::BinOp thawRMWOperation(const Value *Operation) const {
402 NaCl::AtomicRMWOperation Op = (NaCl::AtomicRMWOperation)
403 cast<Constant>(Operation)->getUniqueInteger().getLimitedValue();
404 switch (Op) {
405 // Only valid values should pass validation.
406 default: llvm_unreachable("unknown atomic RMW operation");
407 case NaCl::AtomicAdd: return AtomicRMWInst::Add;
408 case NaCl::AtomicSub: return AtomicRMWInst::Sub;
409 case NaCl::AtomicOr: return AtomicRMWInst::Or;
410 case NaCl::AtomicAnd: return AtomicRMWInst::And;
411 case NaCl::AtomicXor: return AtomicRMWInst::Xor;
412 case NaCl::AtomicExchange: return AtomicRMWInst::Xchg;
413 }
414 }
415
416 AtomicCallResolver(const AtomicCallResolver &);
417 AtomicCallResolver &operator=(const AtomicCallResolver &);
418 };
419 }
420
421 bool ResolvePNaClIntrinsics::visitCalls(
422 ResolvePNaClIntrinsics::CallResolver &Resolver) {
423 bool Changed = false;
424 Function *IntrinsicFunction = Resolver.getDeclaration();
425 if (!IntrinsicFunction)
426 return false;
427
428 SmallVector<IntrinsicInst *, 64> Calls;
429 for (User *U : IntrinsicFunction->users()) {
430 // At this point, the only uses of the intrinsic can be calls, since we
431 // assume this pass runs on bitcode that passed ABI verification.
432 IntrinsicInst *Call = dyn_cast<IntrinsicInst>(U);
433 if (!Call)
434 report_fatal_error("Expected use of intrinsic to be a call: " +
435 Resolver.getName());
436 Calls.push_back(Call);
437 }
438
439 for (auto Call : Calls)
440 Changed |= Resolver.resolve(Call);
441
442 return Changed;
443 }
444
445 bool ResolvePNaClIntrinsics::runOnFunction(Function &F) {
446 LLVMContext &C = F.getParent()->getContext();
447 bool Changed = false;
448
449 IntrinsicCallToFunctionCall SetJmpResolver(F, Intrinsic::nacl_setjmp,
450 "setjmp");
451 IntrinsicCallToFunctionCall LongJmpResolver(F, Intrinsic::nacl_longjmp,
452 "longjmp");
453 Changed |= visitCalls(SetJmpResolver);
454 Changed |= visitCalls(LongJmpResolver);
455
456 NaCl::AtomicIntrinsics AI(C);
457 NaCl::AtomicIntrinsics::View V = AI.allIntrinsicsAndOverloads();
458 for (NaCl::AtomicIntrinsics::View::iterator I = V.begin(), E = V.end();
459 I != E; ++I) {
460 AtomicCallResolver AtomicResolver(F, I);
461 Changed |= visitCalls(AtomicResolver);
462 }
463
464 ConstantCallResolver<IsLockFreeToConstant> IsLockFreeResolver(
465 F, Intrinsic::nacl_atomic_is_lock_free, IsLockFreeToConstant());
466 Changed |= visitCalls(IsLockFreeResolver);
467
468 return Changed;
469 }
470
471 char ResolvePNaClIntrinsics::ID = 0;
472 INITIALIZE_PASS(ResolvePNaClIntrinsics, "resolve-pnacl-intrinsics",
473 "Resolve PNaCl intrinsic calls", false, false)
474
475 FunctionPass *llvm::createResolvePNaClIntrinsicsPass() {
476 return new ResolvePNaClIntrinsics();
477 }
OLDNEW
« no previous file with comments | « lib/Transforms/NaCl/ReplacePtrsWithInts.cpp ('k') | lib/Transforms/NaCl/RewriteAtomics.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698