From: bdemsky Date: Fri, 21 Jun 2019 03:38:14 +0000 (-0700) Subject: merge X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=9afe4502822f002bd97b90c2795ee7d6624e48ec;p=c11llvm.git merge --- 9afe4502822f002bd97b90c2795ee7d6624e48ec diff --cc CDSPass.cpp index 354e5bd,886621f..0cebdf0 --- a/CDSPass.cpp +++ b/CDSPass.cpp @@@ -1,7 -1,7 +1,8 @@@ //===-- CDSPass.cpp - xxx -------------------------------===// // --// The LLVM Compiler Infrastructure --// ++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. ++// See https://llvm.org/LICENSE.txt for license information. ++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // @@@ -43,27 -43,15 +44,26 @@@ #include "llvm/Transforms/IPO/PassManagerBuilder.h" #include --#define DEBUG_TYPE "CDS" using namespace llvm; -#include "getPosition.hpp" - -#define FUNCARRAYSIZE 4 ++#define DEBUG_TYPE "CDS" +#include + +Value *getPosition( Instruction * I, IRBuilder <> IRB) +{ + const DebugLoc & debug_location = I->getDebugLoc (); + std::string position_string; + { + llvm::raw_string_ostream position_stream (position_string); + debug_location . print (position_stream); + } + + return IRB . CreateGlobalStringPtr (position_string); +} - #define FUNCARRAYSIZE 4 - STATISTIC(NumInstrumentedReads, "Number of instrumented reads"); STATISTIC(NumInstrumentedWrites, "Number of instrumented writes"); ++STATISTIC(NumAccessesWithBadSize, "Number of accesses with bad size"); // STATISTIC(NumInstrumentedVtableWrites, "Number of vtable ptr writes"); // STATISTIC(NumInstrumentedVtableReads, "Number of vtable ptr reads"); @@@ -74,10 -62,10 +74,6 @@@ STATISTIC(NumOmittedReadsFromConstantGl STATISTIC(NumOmittedReadsFromVtable, "Number of vtable reads"); STATISTIC(NumOmittedNonCaptured, "Number of accesses ignored due to capturing"); --Type * Int8Ty; --Type * Int16Ty; --Type * Int32Ty; --Type * Int64Ty; Type * OrdTy; Type * Int8PtrTy; @@@ -87,18 -75,18 +83,17 @@@ Type * Int64PtrTy Type * VoidTy; --Constant * CDSLoad[FUNCARRAYSIZE]; --Constant * CDSStore[FUNCARRAYSIZE]; --Constant * CDSAtomicInit[FUNCARRAYSIZE]; --Constant * CDSAtomicLoad[FUNCARRAYSIZE]; --Constant * CDSAtomicStore[FUNCARRAYSIZE]; --Constant * CDSAtomicRMW[AtomicRMWInst::LAST_BINOP + 1][FUNCARRAYSIZE]; --Constant * CDSAtomicCAS_V1[FUNCARRAYSIZE]; --Constant * CDSAtomicCAS_V2[FUNCARRAYSIZE]; ++static const size_t kNumberOfAccessSizes = 4; ++Constant * CDSLoad[kNumberOfAccessSizes]; ++Constant * CDSStore[kNumberOfAccessSizes]; ++Constant * CDSAtomicInit[kNumberOfAccessSizes]; ++Constant * CDSAtomicLoad[kNumberOfAccessSizes]; ++Constant * CDSAtomicStore[kNumberOfAccessSizes]; ++Constant * CDSAtomicRMW[AtomicRMWInst::LAST_BINOP + 1][kNumberOfAccessSizes]; ++Constant * CDSAtomicCAS_V1[kNumberOfAccessSizes]; ++Constant * CDSAtomicCAS_V2[kNumberOfAccessSizes]; Constant * CDSAtomicThreadFence; --bool start = false; -- int getAtomicOrderIndex(AtomicOrdering order){ switch (order) { case AtomicOrdering::Monotonic: @@@ -119,32 -107,32 +114,6 @@@ } } --int getTypeSize(Type* type) { -- if (type == Int8PtrTy) { -- return sizeof(char)*8; -- } else if (type == Int16PtrTy) { -- return sizeof(short)*8; -- } else if (type == Int32PtrTy) { -- return sizeof(int)*8; -- } else if (type == Int64PtrTy) { -- return sizeof(long long int)*8; -- } else { -- return sizeof(void*)*8; -- } -- -- return -1; --} -- --static int sizetoindex(int size) { -- switch(size) { -- case 8: return 0; -- case 16: return 1; -- case 32: return 2; -- case 64: return 3; -- } -- return -1; --} -- namespace { struct CDSPass : public FunctionPass { static char ID; @@@ -170,281 -158,9 +139,331 @@@ static bool isVtableAccess(Instruction return false; } -#include "initializeCallbacks.hpp" -#include "isAtomicCall.hpp" -#include "instrumentAtomicCall.hpp" +void CDSPass::initializeCallbacks(Module &M) { + LLVMContext &Ctx = M.getContext(); + + Type * Int1Ty = Type::getInt1Ty(Ctx); - Int8Ty = Type::getInt8Ty(Ctx); - Int16Ty = Type::getInt16Ty(Ctx); - Int32Ty = Type::getInt32Ty(Ctx); - Int64Ty = Type::getInt64Ty(Ctx); + OrdTy = Type::getInt32Ty(Ctx); + + Int8PtrTy = Type::getInt8PtrTy(Ctx); + Int16PtrTy = Type::getInt16PtrTy(Ctx); + Int32PtrTy = Type::getInt32PtrTy(Ctx); + Int64PtrTy = Type::getInt64PtrTy(Ctx); + + VoidTy = Type::getVoidTy(Ctx); + + // Get the function to call from our untime library. - for (unsigned i = 0; i < FUNCARRAYSIZE; i++) { ++ for (unsigned i = 0; i < kNumberOfAccessSizes; i++) { + const unsigned ByteSize = 1U << i; + const unsigned BitSize = ByteSize * 8; + + std::string ByteSizeStr = utostr(ByteSize); + std::string BitSizeStr = utostr(BitSize); + + Type *Ty = Type::getIntNTy(Ctx, BitSize); + Type *PtrTy = Ty->getPointerTo(); + + // uint8_t cds_atomic_load8 (void * obj, int atomic_index) + // void cds_atomic_store8 (void * obj, int atomic_index, uint8_t val) + SmallString<32> LoadName("cds_load" + BitSizeStr); + SmallString<32> StoreName("cds_store" + BitSizeStr); + SmallString<32> AtomicInitName("cds_atomic_init" + BitSizeStr); + SmallString<32> AtomicLoadName("cds_atomic_load" + BitSizeStr); + SmallString<32> AtomicStoreName("cds_atomic_store" + BitSizeStr); + + CDSLoad[i] = M.getOrInsertFunction(LoadName, VoidTy, PtrTy); + CDSStore[i] = M.getOrInsertFunction(StoreName, VoidTy, PtrTy); + CDSAtomicInit[i] = M.getOrInsertFunction(AtomicInitName, + VoidTy, PtrTy, Ty, Int8PtrTy); + CDSAtomicLoad[i] = M.getOrInsertFunction(AtomicLoadName, + Ty, PtrTy, OrdTy, Int8PtrTy); + CDSAtomicStore[i] = M.getOrInsertFunction(AtomicStoreName, + VoidTy, PtrTy, Ty, OrdTy, Int8PtrTy); + + for (int op = AtomicRMWInst::FIRST_BINOP; + op <= AtomicRMWInst::LAST_BINOP; ++op) { + CDSAtomicRMW[op][i] = nullptr; + std::string NamePart; + + if (op == AtomicRMWInst::Xchg) + NamePart = "_exchange"; + else if (op == AtomicRMWInst::Add) + NamePart = "_fetch_add"; + else if (op == AtomicRMWInst::Sub) + NamePart = "_fetch_sub"; + else if (op == AtomicRMWInst::And) + NamePart = "_fetch_and"; + else if (op == AtomicRMWInst::Or) + NamePart = "_fetch_or"; + else if (op == AtomicRMWInst::Xor) + NamePart = "_fetch_xor"; + else + continue; + + SmallString<32> AtomicRMWName("cds_atomic" + NamePart + BitSizeStr); + CDSAtomicRMW[op][i] = M.getOrInsertFunction(AtomicRMWName, + Ty, PtrTy, Ty, OrdTy, Int8PtrTy); + } + + // only supportes strong version + SmallString<32> AtomicCASName_V1("cds_atomic_compare_exchange" + BitSizeStr + "_v1"); + SmallString<32> AtomicCASName_V2("cds_atomic_compare_exchange" + BitSizeStr + "_v2"); + CDSAtomicCAS_V1[i] = M.getOrInsertFunction(AtomicCASName_V1, + Ty, PtrTy, Ty, Ty, OrdTy, OrdTy, Int8PtrTy); + CDSAtomicCAS_V2[i] = M.getOrInsertFunction(AtomicCASName_V2, + Int1Ty, PtrTy, PtrTy, Ty, OrdTy, OrdTy, Int8PtrTy); + } + + CDSAtomicThreadFence = M.getOrInsertFunction("cds_atomic_thread_fence", + VoidTy, OrdTy, Int8PtrTy); +} + +void printArgs(CallInst *); + - bool isAtomicCall(Instruction *I) - { ++bool isAtomicCall(Instruction *I) { + if ( auto *CI = dyn_cast(I) ) { + Function *fun = CI->getCalledFunction(); + if (fun == NULL) + return false; + + StringRef funName = fun->getName(); - - if ( (CI->isTailCall() && funName.contains("atomic_")) || - funName.contains("atomic_compare_exchange_") ) { - // printArgs(CI); - return true; ++ // todo: come up with better rules for function name checking ++ if ( funName.contains("atomic_") ) { ++ return true; ++ } else if (funName.contains("atomic") ) { ++ return true; + } + } + + return false; +} + - void printArgs (CallInst *CI) - { ++void printArgs (CallInst *CI) { + Function *fun = CI->getCalledFunction(); + StringRef funName = fun->getName(); + + User::op_iterator begin = CI->arg_begin(); + User::op_iterator end = CI->arg_end(); + + if ( funName.contains("atomic_") ) { + std::vector parameters; + + for (User::op_iterator it = begin; it != end; ++it) { + Value *param = *it; + parameters.push_back(param); + errs() << *param << " type: " << *param->getType() << "\n"; + } + } + +} + +bool CDSPass::instrumentAtomicCall(CallInst *CI, const DataLayout &DL) { + IRBuilder<> IRB(CI); + Function *fun = CI->getCalledFunction(); + StringRef funName = fun->getName(); + std::vector parameters; + + User::op_iterator begin = CI->arg_begin(); + User::op_iterator end = CI->arg_end(); + for (User::op_iterator it = begin; it != end; ++it) { + Value *param = *it; + parameters.push_back(param); + } + + // obtain source line number of the CallInst + Value *position = getPosition(CI, IRB); + + // the pointer to the address is always the first argument + Value *OrigPtr = parameters[0]; + int Idx = getMemoryAccessFuncIndex(OrigPtr, DL); + if (Idx < 0) + return false; + + const unsigned ByteSize = 1U << Idx; + const unsigned BitSize = ByteSize * 8; + Type *Ty = Type::getIntNTy(IRB.getContext(), BitSize); + Type *PtrTy = Ty->getPointerTo(); + + // atomic_init; args = {obj, order} + if (funName.contains("atomic_init")) { + Value *ptr = IRB.CreatePointerCast(OrigPtr, PtrTy); + Value *val = IRB.CreateBitOrPointerCast(parameters[1], Ty); + Value *args[] = {ptr, val, position}; + + Instruction* funcInst=CallInst::Create(CDSAtomicInit[Idx], args); + ReplaceInstWithInst(CI, funcInst); + + return true; + } + + // atomic_load; args = {obj, order} + if (funName.contains("atomic_load")) { + bool isExplicit = funName.contains("atomic_load_explicit"); + + Value *ptr = IRB.CreatePointerCast(OrigPtr, PtrTy); + Value *order; + if (isExplicit) + order = IRB.CreateBitOrPointerCast(parameters[1], OrdTy); + else + order = ConstantInt::get(OrdTy, + (int) AtomicOrderingCABI::seq_cst); + Value *args[] = {ptr, order, position}; + + Instruction* funcInst=CallInst::Create(CDSAtomicLoad[Idx], args); + ReplaceInstWithInst(CI, funcInst); + + return true; - } ++ } else if (funName.contains("atomic") && ++ funName.contains("load")) { ++ // does this version of call always have an atomic order as an argument? ++ Value *ptr = IRB.CreatePointerCast(OrigPtr, PtrTy); ++ Value *order = IRB.CreateBitOrPointerCast(parameters[1], OrdTy); ++ Value *args[] = {ptr, order, position}; ++ ++ //Instruction* funcInst=CallInst::Create(CDSAtomicLoad[Idx], args); ++ CallInst *funcInst = IRB.CreateCall(CDSAtomicLoad[Idx], args); ++ Value *RetVal = IRB.CreateIntToPtr(funcInst, CI->getType()); ++ ++ CI->replaceAllUsesWith(RetVal); ++ CI->eraseFromParent(); ++ ++ return true; ++ } + + // atomic_store; args = {obj, val, order} + if (funName.contains("atomic_store")) { + bool isExplicit = funName.contains("atomic_store_explicit"); + Value *OrigVal = parameters[1]; + + Value *ptr = IRB.CreatePointerCast(OrigPtr, PtrTy); + Value *val = IRB.CreatePointerCast(OrigVal, Ty); + Value *order; + if (isExplicit) + order = IRB.CreateBitOrPointerCast(parameters[2], OrdTy); + else + order = ConstantInt::get(OrdTy, + (int) AtomicOrderingCABI::seq_cst); + Value *args[] = {ptr, val, order, position}; + + Instruction* funcInst=CallInst::Create(CDSAtomicStore[Idx], args); + ReplaceInstWithInst(CI, funcInst); + + return true; - } ++ } else if (funName.contains("atomic") && ++ funName.contains("EEEE5store")) { ++ // does this version of call always have an atomic order as an argument? ++ Value *OrigVal = parameters[1]; ++ ++ Value *ptr = IRB.CreatePointerCast(OrigPtr, PtrTy); ++ Value *val = IRB.CreatePointerCast(OrigVal, Ty); ++ Value *order = IRB.CreateBitOrPointerCast(parameters[1], OrdTy); ++ Value *args[] = {ptr, val, order, position}; ++ ++ Instruction* funcInst = CallInst::Create(CDSAtomicStore[Idx], args); ++ ReplaceInstWithInst(CI, funcInst); ++ ++ return true; ++ } ++ + + // atomic_fetch_*; args = {obj, val, order} + if (funName.contains("atomic_fetch_") || + funName.contains("atomic_exchange") ) { + bool isExplicit = funName.contains("_explicit"); + Value *OrigVal = parameters[1]; + + int op; + if ( funName.contains("_fetch_add") ) + op = AtomicRMWInst::Add; + else if ( funName.contains("_fetch_sub") ) + op = AtomicRMWInst::Sub; + else if ( funName.contains("_fetch_and") ) + op = AtomicRMWInst::And; + else if ( funName.contains("_fetch_or") ) + op = AtomicRMWInst::Or; + else if ( funName.contains("_fetch_xor") ) + op = AtomicRMWInst::Xor; + else if ( funName.contains("atomic_exchange") ) + op = AtomicRMWInst::Xchg; + else { + errs() << "Unknown atomic read modify write operation\n"; + return false; + } + + Value *ptr = IRB.CreatePointerCast(OrigPtr, PtrTy); + Value *val = IRB.CreatePointerCast(OrigVal, Ty); + Value *order; + if (isExplicit) + order = IRB.CreateBitOrPointerCast(parameters[2], OrdTy); + else + order = ConstantInt::get(OrdTy, + (int) AtomicOrderingCABI::seq_cst); + Value *args[] = {ptr, val, order, position}; + + Instruction* funcInst=CallInst::Create(CDSAtomicRMW[op][Idx], args); + ReplaceInstWithInst(CI, funcInst); + + return true; - } ++ } else if (funName.contains("fetch")) { ++ errs() << "atomic exchange captured. Not implemented yet. "; ++ errs() << "See source file :"; ++ getPositionPrint(CI, IRB); ++ } else if (funName.contains("exchange") && ++ !funName.contains("compare_exchange") ) { ++ errs() << "atomic exchange captured. Not implemented yet. "; ++ errs() << "See source file :"; ++ getPositionPrint(CI, IRB); ++ } + + /* atomic_compare_exchange_*; + args = {obj, expected, new value, order1, order2} + */ + if ( funName.contains("atomic_compare_exchange_") ) { + bool isExplicit = funName.contains("_explicit"); + + Value *Addr = IRB.CreatePointerCast(OrigPtr, PtrTy); + Value *CmpOperand = IRB.CreatePointerCast(parameters[1], PtrTy); + Value *NewOperand = IRB.CreateBitOrPointerCast(parameters[2], Ty); + + Value *order_succ, *order_fail; + if (isExplicit) { + order_succ = IRB.CreateBitOrPointerCast(parameters[3], OrdTy); + order_fail = IRB.CreateBitOrPointerCast(parameters[4], OrdTy); + } else { + order_succ = ConstantInt::get(OrdTy, + (int) AtomicOrderingCABI::seq_cst); + order_fail = ConstantInt::get(OrdTy, + (int) AtomicOrderingCABI::seq_cst); + } + + Value *args[] = {Addr, CmpOperand, NewOperand, + order_succ, order_fail, position}; + + Instruction* funcInst=CallInst::Create(CDSAtomicCAS_V2[Idx], args); + ReplaceInstWithInst(CI, funcInst); + + return true; - } ++ } else if ( funName.contains("compare_exchange_strong") || ++ funName.contains("compare_exchange_weak") ) { ++ Value *Addr = IRB.CreatePointerCast(OrigPtr, PtrTy); ++ Value *CmpOperand = IRB.CreatePointerCast(parameters[1], PtrTy); ++ Value *NewOperand = IRB.CreateBitOrPointerCast(parameters[2], Ty); ++ ++ Value *order_succ, *order_fail; ++ order_succ = IRB.CreateBitOrPointerCast(parameters[3], OrdTy); ++ order_fail = IRB.CreateBitOrPointerCast(parameters[4], OrdTy); ++ ++ Value *args[] = {Addr, CmpOperand, NewOperand, ++ order_succ, order_fail, position}; ++ Instruction* funcInst=CallInst::Create(CDSAtomicCAS_V2[Idx], args); ++ ReplaceInstWithInst(CI, funcInst); ++ ++ return true; ++ } ++ + + return false; +} static bool shouldInstrumentReadWriteFromAddress(const Module *M, Value *Addr) { // Peel off GEPs and BitCasts. @@@ -607,8 -323,8 +626,8 @@@ bool CDSPass::instrumentLoadOrStore(Ins if (Addr->isSwiftError()) return false; -- int size = getTypeSize(Addr->getType()); -- int index = sizetoindex(size); ++ int Idx = getMemoryAccessFuncIndex(Addr, DL); ++ // not supported by CDS yet /* if (IsWrite && isVtableAccess(I)) { @@@ -639,7 -355,7 +658,7 @@@ */ Value *OnAccessFunc = nullptr; -- OnAccessFunc = IsWrite ? CDSStore[index] : CDSLoad[index]; ++ OnAccessFunc = IsWrite ? CDSStore[Idx] : CDSLoad[Idx]; Type *ArgType = IRB.CreatePointerCast(Addr, Addr->getType())->getType(); @@@ -656,7 -372,7 +675,6 @@@ return true; } --// todo: replace getTypeSize with the getMemoryAccessFuncIndex bool CDSPass::instrumentAtomic(Instruction * I, const DataLayout &DL) { IRBuilder<> IRB(I); // LLVMContext &Ctx = IRB.getContext(); @@@ -667,56 -383,56 +685,39 @@@ Value *position = getPosition(I, IRB); -- if (StoreInst *SI = dyn_cast(I)) { ++ if (LoadInst *LI = dyn_cast(I)) { ++ Value *Addr = LI->getPointerOperand(); ++ int Idx=getMemoryAccessFuncIndex(Addr, DL); ++ int atomic_order_index = getAtomicOrderIndex(LI->getOrdering()); ++ Value *order = ConstantInt::get(OrdTy, atomic_order_index); ++ Value *args[] = {Addr, order, position}; ++ Instruction* funcInst=CallInst::Create(CDSAtomicLoad[Idx], args); ++ ReplaceInstWithInst(LI, funcInst); ++ } else if (StoreInst *SI = dyn_cast(I)) { ++ Value *Addr = SI->getPointerOperand(); ++ int Idx=getMemoryAccessFuncIndex(Addr, DL); int atomic_order_index = getAtomicOrderIndex(SI->getOrdering()); -- Value *val = SI->getValueOperand(); -- Value *ptr = SI->getPointerOperand(); Value *order = ConstantInt::get(OrdTy, atomic_order_index); -- Value *args[] = {ptr, val, order, position}; -- -- int size=getTypeSize(ptr->getType()); -- int index=sizetoindex(size); -- -- Instruction* funcInst=CallInst::Create(CDSAtomicStore[index], args); ++ Value *args[] = {Addr, val, order, position}; ++ Instruction* funcInst=CallInst::Create(CDSAtomicStore[Idx], args); ReplaceInstWithInst(SI, funcInst); --// errs() << "Store replaced\n"; -- } else if (LoadInst *LI = dyn_cast(I)) { -- int atomic_order_index = getAtomicOrderIndex(LI->getOrdering()); -- -- Value *ptr = LI->getPointerOperand(); -- Value *order = ConstantInt::get(OrdTy, atomic_order_index); -- Value *args[] = {ptr, order, position}; -- -- int size=getTypeSize(ptr->getType()); -- int index=sizetoindex(size); -- -- Instruction* funcInst=CallInst::Create(CDSAtomicLoad[index], args); -- ReplaceInstWithInst(LI, funcInst); --// errs() << "Load Replaced\n"; } else if (AtomicRMWInst *RMWI = dyn_cast(I)) { ++ Value *Addr = RMWI->getPointerOperand(); ++ int Idx=getMemoryAccessFuncIndex(Addr, DL); int atomic_order_index = getAtomicOrderIndex(RMWI->getOrdering()); -- Value *val = RMWI->getValOperand(); -- Value *ptr = RMWI->getPointerOperand(); Value *order = ConstantInt::get(OrdTy, atomic_order_index); -- Value *args[] = {ptr, val, order, position}; -- -- int size = getTypeSize(ptr->getType()); -- int index = sizetoindex(size); -- -- Instruction* funcInst = CallInst::Create(CDSAtomicRMW[RMWI->getOperation()][index], args); ++ Value *args[] = {Addr, val, order, position}; ++ Instruction* funcInst = CallInst::Create(CDSAtomicRMW[RMWI->getOperation()][Idx], args); ReplaceInstWithInst(RMWI, funcInst); --// errs() << RMWI->getOperationName(RMWI->getOperation()); --// errs() << " replaced\n"; } else if (AtomicCmpXchgInst *CASI = dyn_cast(I)) { IRBuilder<> IRB(CASI); Value *Addr = CASI->getPointerOperand(); ++ int Idx=getMemoryAccessFuncIndex(Addr, DL); -- int size = getTypeSize(Addr->getType()); -- int index = sizetoindex(size); -- const unsigned ByteSize = 1U << index; ++ const unsigned ByteSize = 1U << Idx; const unsigned BitSize = ByteSize * 8; Type *Ty = Type::getIntNTy(IRB.getContext(), BitSize); Type *PtrTy = Ty->getPointerTo(); @@@ -733,7 -449,7 +734,7 @@@ CmpOperand, NewOperand, order_succ, order_fail, position}; -- CallInst *funcInst = IRB.CreateCall(CDSAtomicCAS_V1[index], Args); ++ CallInst *funcInst = IRB.CreateCall(CDSAtomicCAS_V1[Idx], Args); Value *Success = IRB.CreateICmpEQ(funcInst, CmpOperand); Value *OldVal = funcInst; @@@ -769,12 -485,12 +770,12 @@@ int CDSPass::getMemoryAccessFuncIndex(V uint32_t TypeSize = DL.getTypeStoreSizeInBits(OrigTy); if (TypeSize != 8 && TypeSize != 16 && TypeSize != 32 && TypeSize != 64 && TypeSize != 128) { -- // NumAccessesWithBadSize++; ++ NumAccessesWithBadSize++; // Ignore all unusual sizes. return -1; } size_t Idx = countTrailingZeros(TypeSize / 8); -- // assert(Idx < FUNCARRAYSIZE); ++ assert(Idx < kNumberOfAccessSizes); return Idx; }