Left: | ||
Right: |
LEFT | RIGHT |
---|---|
1 //===-- ThreadSanitizer.cpp - race detector -------------------------------===// | 1 //===-- ThreadSanitizer.cpp - race detector -------------------------------===// |
2 // | 2 // |
3 // The LLVM Compiler Infrastructure | 3 // The LLVM Compiler Infrastructure |
4 // | 4 // |
5 // This file is distributed under the University of Illinois Open Source | 5 // This file is distributed under the University of Illinois Open Source |
6 // License. See LICENSE.TXT for details. | 6 // License. See LICENSE.TXT for details. |
7 // | 7 // |
8 //===----------------------------------------------------------------------===// | 8 //===----------------------------------------------------------------------===// |
9 // | 9 // |
10 // This file is a part of ThreadSanitizer, a race detector. | 10 // This file is a part of ThreadSanitizer, a race detector. |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
56 STATISTIC(NumInstrumentedVtableWrites, "Number of vtable ptr writes"); | 56 STATISTIC(NumInstrumentedVtableWrites, "Number of vtable ptr writes"); |
57 STATISTIC(NumOmittedReadsFromConstantGlobals, | 57 STATISTIC(NumOmittedReadsFromConstantGlobals, |
58 "Number of reads from constant globals"); | 58 "Number of reads from constant globals"); |
59 STATISTIC(NumOmittedReadsFromVtable, "Number of vtable reads"); | 59 STATISTIC(NumOmittedReadsFromVtable, "Number of vtable reads"); |
60 | 60 |
61 namespace { | 61 namespace { |
62 | 62 |
63 /// ThreadSanitizer: instrument the code in module to find races. | 63 /// ThreadSanitizer: instrument the code in module to find races. |
64 struct ThreadSanitizer : public FunctionPass { | 64 struct ThreadSanitizer : public FunctionPass { |
65 ThreadSanitizer(); | 65 ThreadSanitizer(); |
66 const char *getPassName() const; | |
66 bool runOnFunction(Function &F); | 67 bool runOnFunction(Function &F); |
67 bool doInitialization(Module &M); | 68 bool doInitialization(Module &M); |
68 static char ID; // Pass identification, replacement for typeid. | 69 static char ID; // Pass identification, replacement for typeid. |
69 | 70 |
70 private: | 71 private: |
71 virtual const char *getPassName() const; | |
glider
2012/04/24 13:46:20
This should be a public one.
dvyukov
2012/04/24 16:42:15
Done.
| |
72 bool instrumentLoadOrStore(Instruction *I); | 72 bool instrumentLoadOrStore(Instruction *I); |
73 bool instrumentAtomic(Instruction *I); | 73 bool instrumentAtomic(Instruction *I); |
74 void choseInstructionsToInstrument(SmallVectorImpl<Instruction*> &Local, | 74 void choseInstructionsToInstrument(SmallVectorImpl<Instruction*> &Local, |
75 SmallVectorImpl<Instruction*> &All); | 75 SmallVectorImpl<Instruction*> &All); |
76 bool addrPointsToConstantData(Value *Addr); | 76 bool addrPointsToConstantData(Value *Addr); |
77 int getFuncIndex(Value *Addr); | 77 int getMemoryAccessFuncIndex(Value *Addr); |
78 | 78 |
79 TargetData *TD; | 79 TargetData *TD; |
80 OwningPtr<FunctionBlackList> BL; | 80 OwningPtr<FunctionBlackList> BL; |
81 IntegerType *OrdTy; | |
81 // Callbacks to run-time library are computed in doInitialization. | 82 // Callbacks to run-time library are computed in doInitialization. |
82 Value *TsanFuncEntry; | 83 Function *TsanFuncEntry; |
83 Value *TsanFuncExit; | 84 Function *TsanFuncExit; |
84 // Accesses sizes are powers of two: 1, 2, 4, 8, 16. | 85 // Accesses sizes are powers of two: 1, 2, 4, 8, 16. |
85 static const size_t kNumberOfAccessSizes = 5; | 86 static const size_t kNumberOfAccessSizes = 5; |
86 IntegerType *IntTy[kNumberOfAccessSizes]; | 87 Function *TsanRead[kNumberOfAccessSizes]; |
87 IntegerType *OrdTy; | 88 Function *TsanWrite[kNumberOfAccessSizes]; |
88 Value *TsanRead[kNumberOfAccessSizes]; | 89 Function *TsanAtomicLoad[kNumberOfAccessSizes]; |
89 Value *TsanWrite[kNumberOfAccessSizes]; | 90 Function *TsanAtomicStore[kNumberOfAccessSizes]; |
90 Value *TsanAtomicLoad[kNumberOfAccessSizes]; | 91 Function *TsanVptrUpdate; |
glider
2012/04/24 13:46:20
s/Value */Constant */
In fact you may want to che
dvyukov
2012/04/24 16:42:15
Done.
| |
91 Value *TsanAtomicStore[kNumberOfAccessSizes]; | |
92 Value *TsanVptrUpdate; | |
93 }; | 92 }; |
94 } // namespace | 93 } // namespace |
95 | 94 |
96 char ThreadSanitizer::ID = 0; | 95 char ThreadSanitizer::ID = 0; |
97 INITIALIZE_PASS(ThreadSanitizer, "tsan", | 96 INITIALIZE_PASS(ThreadSanitizer, "tsan", |
98 "ThreadSanitizer: detects data races.", | 97 "ThreadSanitizer: detects data races.", |
99 false, false) | 98 false, false) |
100 | 99 |
101 const char *ThreadSanitizer::getPassName() const { | 100 const char *ThreadSanitizer::getPassName() const { |
102 return "ThreadSanitizer"; | 101 return "ThreadSanitizer"; |
103 } | 102 } |
104 | 103 |
105 ThreadSanitizer::ThreadSanitizer() | 104 ThreadSanitizer::ThreadSanitizer() |
106 : FunctionPass(ID), | 105 : FunctionPass(ID), |
107 TD(NULL) { | 106 TD(NULL) { |
108 } | 107 } |
109 | 108 |
110 FunctionPass *llvm::createThreadSanitizerPass() { | 109 FunctionPass *llvm::createThreadSanitizerPass() { |
111 return new ThreadSanitizer(); | 110 return new ThreadSanitizer(); |
111 } | |
112 | |
113 static Function *checkInterfaceFunction(Constant *FuncOrBitcast) { | |
114 if (Function *F = dyn_cast<Function>(FuncOrBitcast)) | |
115 return F; | |
116 FuncOrBitcast->dump(); | |
117 report_fatal_error("ThreadSanitizer interface function redefined"); | |
112 } | 118 } |
113 | 119 |
114 bool ThreadSanitizer::doInitialization(Module &M) { | 120 bool ThreadSanitizer::doInitialization(Module &M) { |
115 TD = getAnalysisIfAvailable<TargetData>(); | 121 TD = getAnalysisIfAvailable<TargetData>(); |
116 if (!TD) | 122 if (!TD) |
117 return false; | 123 return false; |
118 BL.reset(new FunctionBlackList(ClBlackListFile)); | 124 BL.reset(new FunctionBlackList(ClBlackListFile)); |
119 | 125 |
120 // Always insert a call to __tsan_init into the module's CTORs. | 126 // Always insert a call to __tsan_init into the module's CTORs. |
121 IRBuilder<> IRB(M.getContext()); | 127 IRBuilder<> IRB(M.getContext()); |
122 Value *TsanInit = M.getOrInsertFunction("__tsan_init", | 128 Value *TsanInit = M.getOrInsertFunction("__tsan_init", |
123 IRB.getVoidTy(), NULL); | 129 IRB.getVoidTy(), NULL); |
124 appendToGlobalCtors(M, cast<Function>(TsanInit), 0); | 130 appendToGlobalCtors(M, cast<Function>(TsanInit), 0); |
125 | 131 |
126 // Initialize the callbacks. | 132 // Initialize the callbacks. |
127 TsanFuncEntry = M.getOrInsertFunction("__tsan_func_entry", IRB.getVoidTy(), | 133 TsanFuncEntry = checkInterfaceFunction(M.getOrInsertFunction( |
128 IRB.getInt8PtrTy(), NULL); | 134 "__tsan_func_entry", IRB.getVoidTy(), IRB.getInt8PtrTy(), NULL)); |
129 TsanFuncExit = M.getOrInsertFunction("__tsan_func_exit", IRB.getVoidTy(), | 135 TsanFuncExit = checkInterfaceFunction(M.getOrInsertFunction( |
130 NULL); | 136 "__tsan_func_exit", IRB.getVoidTy(), NULL)); |
131 IntTy[0] = IRB.getInt8Ty(); | |
132 IntTy[1] = IRB.getInt16Ty(); | |
133 IntTy[2] = IRB.getInt32Ty(); | |
134 IntTy[3] = IRB.getInt64Ty(); | |
135 IntTy[4] = Type::getIntNTy(M.getContext(), 128); | |
136 OrdTy = IRB.getInt32Ty(); | 137 OrdTy = IRB.getInt32Ty(); |
glider
2012/04/24 13:46:20
I wonder if it is not better to use Int64Ty on x64
dvyukov
2012/04/24 16:42:15
Int32Ty is OK on IA-32.
| |
137 for (size_t i = 0; i < kNumberOfAccessSizes; ++i) { | 138 for (size_t i = 0; i < kNumberOfAccessSizes; ++i) { |
138 SmallString<32> ReadName("__tsan_read"); | 139 const size_t ByteSize = 1 << i; |
139 ReadName += itostr(1 << i); | 140 const size_t BitSize = ByteSize * 8; |
140 TsanRead[i] = M.getOrInsertFunction(ReadName, IRB.getVoidTy(), | 141 SmallString<32> ReadName("__tsan_read" + itostr(ByteSize)); |
141 IRB.getInt8PtrTy(), NULL); | 142 TsanRead[i] = checkInterfaceFunction(M.getOrInsertFunction( |
142 SmallString<32> WriteName("__tsan_write"); | 143 ReadName, IRB.getVoidTy(), IRB.getInt8PtrTy(), NULL)); |
143 WriteName += itostr(1 << i); | 144 |
144 TsanWrite[i] = M.getOrInsertFunction(WriteName, IRB.getVoidTy(), | 145 SmallString<32> WriteName("__tsan_write" + itostr(ByteSize)); |
145 IRB.getInt8PtrTy(), NULL); | 146 TsanWrite[i] = checkInterfaceFunction(M.getOrInsertFunction( |
146 } | 147 WriteName, IRB.getVoidTy(), IRB.getInt8PtrTy(), NULL)); |
147 for (size_t i = 0; i < kNumberOfAccessSizes; ++i) { | 148 |
148 SmallString<32> Name("__tsan_atomic" + itostr(1 << i) + "_load"); | 149 Type *Ty = Type::getIntNTy(M.getContext(), BitSize); |
149 TsanAtomicLoad[i] = M.getOrInsertFunction(Name, | 150 Type *PtrTy = Ty->getPointerTo(); |
150 IntTy[i], | 151 SmallString<32> AtomicLoadName("__tsan_atomic" + itostr(BitSize) + |
151 IRB.getInt8PtrTy(), | 152 "_load"); |
152 OrdTy, NULL); | 153 TsanAtomicLoad[i] = checkInterfaceFunction(M.getOrInsertFunction( |
153 } | 154 AtomicLoadName, Ty, PtrTy, OrdTy, NULL)); |
154 for (size_t i = 0; i < kNumberOfAccessSizes; ++i) { | 155 |
155 SmallString<32> Name("__tsan_atomic" + itostr(1 << i) + "_store"); | 156 SmallString<32> AtomicStoreName("__tsan_atomic" + itostr(BitSize) + |
156 TsanAtomicStore[i] = M.getOrInsertFunction(Name, | 157 "_store"); |
157 IRB.getVoidTy(), | 158 TsanAtomicStore[i] = checkInterfaceFunction(M.getOrInsertFunction( |
158 IRB.getInt8PtrTy(), | 159 AtomicStoreName, IRB.getVoidTy(), PtrTy, Ty, OrdTy, |
159 IntTy[i], | 160 NULL)); |
160 OrdTy, NULL); | 161 } |
161 } | 162 TsanVptrUpdate = checkInterfaceFunction(M.getOrInsertFunction( |
162 TsanVptrUpdate = M.getOrInsertFunction("__tsan_vptr_update", IRB.getVoidTy(), | 163 "__tsan_vptr_update", IRB.getVoidTy(), IRB.getInt8PtrTy(), |
163 IRB.getInt8PtrTy(), IRB.getInt8PtrTy(), | 164 IRB.getInt8PtrTy(), NULL)); |
164 NULL); | |
165 return true; | 165 return true; |
166 } | 166 } |
167 | 167 |
168 static bool isVtableAccess(Instruction *I) { | 168 static bool isVtableAccess(Instruction *I) { |
169 if (MDNode *Tag = I->getMetadata(LLVMContext::MD_tbaa)) { | 169 if (MDNode *Tag = I->getMetadata(LLVMContext::MD_tbaa)) { |
170 if (Tag->getNumOperands() < 1) return false; | 170 if (Tag->getNumOperands() < 1) return false; |
171 if (MDString *Tag1 = dyn_cast<MDString>(Tag->getOperand(0))) { | 171 if (MDString *Tag1 = dyn_cast<MDString>(Tag->getOperand(0))) { |
172 if (Tag1->getString() == "vtable pointer") return true; | 172 if (Tag1->getString() == "vtable pointer") return true; |
173 } | 173 } |
174 } | 174 } |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
229 // Addr points to some constant data -- it can not race with any writes. | 229 // Addr points to some constant data -- it can not race with any writes. |
230 continue; | 230 continue; |
231 } | 231 } |
232 } | 232 } |
233 All.push_back(I); | 233 All.push_back(I); |
234 } | 234 } |
235 Local.clear(); | 235 Local.clear(); |
236 } | 236 } |
237 | 237 |
238 static bool isAtomic(Instruction *I) { | 238 static bool isAtomic(Instruction *I) { |
239 if (LoadInst *LI = dyn_cast<LoadInst>(I)) | 239 if (LoadInst *LI = dyn_cast<LoadInst>(I)) |
glider
2012/04/24 13:46:20
isa<LoadInst>
dvyukov
2012/04/24 16:42:15
The style guide says:
if (AllocationInst *AI = dyn
| |
240 return LI->isAtomic() && LI->getSynchScope() == CrossThread; | 240 return LI->isAtomic() && LI->getSynchScope() == CrossThread; |
241 else if (StoreInst *SI = dyn_cast<StoreInst>(I)) | 241 else if (StoreInst *SI = dyn_cast<StoreInst>(I)) |
242 return SI->isAtomic() && SI->getSynchScope() == CrossThread; | 242 return SI->isAtomic() && SI->getSynchScope() == CrossThread; |
243 else if (isa<AtomicRMWInst>(I)) | 243 else if (isa<AtomicRMWInst>(I)) |
244 return true; | 244 return true; |
245 else if (isa<AtomicCmpXchgInst>(I)) | 245 else if (isa<AtomicCmpXchgInst>(I)) |
246 return true; | 246 return true; |
247 else if (FenceInst *FI = dyn_cast<FenceInst>(I)) | 247 else if (FenceInst *FI = dyn_cast<FenceInst>(I)) |
248 return FI->getSynchScope() == CrossThread; | 248 return FI->getSynchScope() == CrossThread; |
249 return false; | 249 return false; |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
308 } | 308 } |
309 return Res; | 309 return Res; |
310 } | 310 } |
311 | 311 |
312 bool ThreadSanitizer::instrumentLoadOrStore(Instruction *I) { | 312 bool ThreadSanitizer::instrumentLoadOrStore(Instruction *I) { |
313 IRBuilder<> IRB(I); | 313 IRBuilder<> IRB(I); |
314 bool IsWrite = isa<StoreInst>(*I); | 314 bool IsWrite = isa<StoreInst>(*I); |
315 Value *Addr = IsWrite | 315 Value *Addr = IsWrite |
316 ? cast<StoreInst>(I)->getPointerOperand() | 316 ? cast<StoreInst>(I)->getPointerOperand() |
317 : cast<LoadInst>(I)->getPointerOperand(); | 317 : cast<LoadInst>(I)->getPointerOperand(); |
318 int Idx = getFuncIndex(Addr); | 318 int Idx = getMemoryAccessFuncIndex(Addr); |
319 if (Idx < 0) | 319 if (Idx < 0) |
320 return false; | 320 return false; |
321 if (IsWrite && isVtableAccess(I)) { | 321 if (IsWrite && isVtableAccess(I)) { |
322 Value *StoredValue = cast<StoreInst>(I)->getValueOperand(); | 322 Value *StoredValue = cast<StoreInst>(I)->getValueOperand(); |
323 IRB.CreateCall2(TsanVptrUpdate, | 323 IRB.CreateCall2(TsanVptrUpdate, |
324 IRB.CreatePointerCast(Addr, IRB.getInt8PtrTy()), | 324 IRB.CreatePointerCast(Addr, IRB.getInt8PtrTy()), |
325 IRB.CreatePointerCast(StoredValue, IRB.getInt8PtrTy())); | 325 IRB.CreatePointerCast(StoredValue, IRB.getInt8PtrTy())); |
326 NumInstrumentedVtableWrites++; | 326 NumInstrumentedVtableWrites++; |
327 return true; | 327 return true; |
328 } | 328 } |
329 Value *OnAccessFunc = IsWrite ? TsanWrite[Idx] : TsanRead[Idx]; | 329 Value *OnAccessFunc = IsWrite ? TsanWrite[Idx] : TsanRead[Idx]; |
330 IRB.CreateCall(OnAccessFunc, IRB.CreatePointerCast(Addr, IRB.getInt8PtrTy())); | 330 IRB.CreateCall(OnAccessFunc, IRB.CreatePointerCast(Addr, IRB.getInt8PtrTy())); |
331 if (IsWrite) NumInstrumentedWrites++; | 331 if (IsWrite) NumInstrumentedWrites++; |
332 else NumInstrumentedReads++; | 332 else NumInstrumentedReads++; |
333 return true; | 333 return true; |
334 } | 334 } |
335 | 335 |
336 static ConstantInt *createOrdering(IRBuilder<> *IRB, AtomicOrdering ord) { | 336 static ConstantInt *createOrdering(IRBuilder<> *IRB, AtomicOrdering ord) { |
337 return IRB->getInt32(static_cast<uint32_t>(ord)); | 337 uint32_t v = 0; |
338 switch (ord) { | |
339 case NotAtomic: assert(false); | |
340 case Unordered: // Fall-through. | |
341 case Monotonic: v = 1 << 0; break; | |
342 // case Consume: v = 1 << 1; break; // Not specified yet. | |
343 case Acquire: v = 1 << 2; break; | |
344 case Release: v = 1 << 3; break; | |
345 case AcquireRelease: v = 1 << 4; break; | |
346 case SequentiallyConsistent: v = 1 << 5; break; | |
347 } | |
348 return IRB->getInt32(v); | |
338 } | 349 } |
339 | 350 |
340 bool ThreadSanitizer::instrumentAtomic(Instruction *I) { | 351 bool ThreadSanitizer::instrumentAtomic(Instruction *I) { |
341 IRBuilder<> IRB(I); | 352 IRBuilder<> IRB(I); |
342 if (LoadInst *LI = dyn_cast<LoadInst>(I)) { | 353 if (LoadInst *LI = dyn_cast<LoadInst>(I)) { |
glider
2012/04/24 13:46:20
if (isa<LoadInst>(I))
See also http://llvm.org/do
| |
343 Value *Addr = LI->getPointerOperand(); | 354 Value *Addr = LI->getPointerOperand(); |
344 int Idx = getFuncIndex(Addr); | 355 int Idx = getMemoryAccessFuncIndex(Addr); |
345 if (Idx < 0) | 356 if (Idx < 0) |
346 return false; | 357 return false; |
347 Value *Args[] = {IRB.CreatePointerCast(Addr, IRB.getInt8PtrTy()), | 358 const size_t ByteSize = 1 << Idx; |
359 const size_t BitSize = ByteSize * 8; | |
360 Type *Ty = Type::getIntNTy(IRB.getContext(), BitSize); | |
361 Type *PtrTy = Ty->getPointerTo(); | |
362 Value *Args[] = {IRB.CreatePointerCast(Addr, PtrTy), | |
348 createOrdering(&IRB, LI->getOrdering())}; | 363 createOrdering(&IRB, LI->getOrdering())}; |
349 CallInst *C = CallInst::Create(TsanAtomicLoad[Idx], | 364 CallInst *C = CallInst::Create(TsanAtomicLoad[Idx], |
350 ArrayRef<Value*>(Args)); | 365 ArrayRef<Value*>(Args)); |
351 ReplaceInstWithInst(I, C); | 366 ReplaceInstWithInst(I, C); |
352 | 367 |
353 } else if (StoreInst *SI = dyn_cast<StoreInst>(I)) { | 368 } else if (StoreInst *SI = dyn_cast<StoreInst>(I)) { |
354 Value *Addr = SI->getPointerOperand(); | 369 Value *Addr = SI->getPointerOperand(); |
355 int Idx = getFuncIndex(Addr); | 370 int Idx = getMemoryAccessFuncIndex(Addr); |
356 if (Idx < 0) | 371 if (Idx < 0) |
357 return false; | 372 return false; |
358 Value *Args[] = {IRB.CreatePointerCast(Addr, IRB.getInt8PtrTy()), | 373 const size_t ByteSize = 1 << Idx; |
359 SI->getValueOperand(), | 374 const size_t BitSize = ByteSize * 8; |
375 Type *Ty = Type::getIntNTy(IRB.getContext(), BitSize); | |
376 Type *PtrTy = Ty->getPointerTo(); | |
377 Value *Args[] = {IRB.CreatePointerCast(Addr, PtrTy), | |
378 IRB.CreateIntCast(SI->getValueOperand(), Ty, false), | |
360 createOrdering(&IRB, SI->getOrdering())}; | 379 createOrdering(&IRB, SI->getOrdering())}; |
361 CallInst *C = CallInst::Create(TsanAtomicStore[Idx], | 380 CallInst *C = CallInst::Create(TsanAtomicStore[Idx], |
362 ArrayRef<Value*>(Args)); | 381 ArrayRef<Value*>(Args)); |
363 ReplaceInstWithInst(I, C); | 382 ReplaceInstWithInst(I, C); |
364 } else if (AtomicRMWInst *AI = dyn_cast<AtomicRMWInst>(I)) { | 383 } else if (isa<AtomicRMWInst>(I)) { |
365 (void)AI; | 384 // FIXME: Not yet supported. |
glider
2012/04/24 13:46:20
Do you need this line?
| |
366 } else if (AtomicCmpXchgInst *AI = dyn_cast<AtomicCmpXchgInst>(I)) { | 385 } else if (isa<AtomicCmpXchgInst>(I)) { |
367 (void)AI; | 386 // FIXME: Not yet supported. |
368 } else if (FenceInst *FI = dyn_cast<FenceInst>(I)) { | 387 } else if (isa<FenceInst>(I)) { |
369 (void)FI; | 388 // FIXME: Not yet supported. |
370 } | 389 } |
371 return true; | 390 return true; |
372 } | 391 } |
373 | 392 |
374 int ThreadSanitizer::getFuncIndex(Value *Addr) { | 393 int ThreadSanitizer::getMemoryAccessFuncIndex(Value *Addr) { |
glider
2012/04/24 13:46:20
getFuncIdx is perhaps not the best name for a func
| |
375 Type *OrigPtrTy = Addr->getType(); | 394 Type *OrigPtrTy = Addr->getType(); |
376 Type *OrigTy = cast<PointerType>(OrigPtrTy)->getElementType(); | 395 Type *OrigTy = cast<PointerType>(OrigPtrTy)->getElementType(); |
377 assert(OrigTy->isSized()); | 396 assert(OrigTy->isSized()); |
378 uint32_t TypeSize = TD->getTypeStoreSizeInBits(OrigTy); | 397 uint32_t TypeSize = TD->getTypeStoreSizeInBits(OrigTy); |
379 if (TypeSize != 8 && TypeSize != 16 && | 398 if (TypeSize != 8 && TypeSize != 16 && |
380 TypeSize != 32 && TypeSize != 64 && TypeSize != 128) { | 399 TypeSize != 32 && TypeSize != 64 && TypeSize != 128) { |
381 NumAccessesWithBadSize++; | 400 NumAccessesWithBadSize++; |
382 // Ignore all unusual sizes. | 401 // Ignore all unusual sizes. |
383 return -1; | 402 return -1; |
384 } | 403 } |
385 size_t Idx = CountTrailingZeros_32(TypeSize / 8); | 404 size_t Idx = CountTrailingZeros_32(TypeSize / 8); |
386 assert(Idx < kNumberOfAccessSizes); | 405 assert(Idx < kNumberOfAccessSizes); |
387 return Idx; | 406 return Idx; |
388 } | 407 } |
LEFT | RIGHT |