From 809cb18308fd93ced773d0b64f877a234efe0009 Mon Sep 17 00:00:00 2001 From: Yashwanth Nannapaneni Date: Tue, 29 Dec 2020 22:33:09 +0000 Subject: [PATCH 1/7] Changing FiberManagerInternal to use FOLLY_LIBRARY_SANITIZE_ADDRESS --- folly/fibers/Fiber.cpp | 8 ++++---- folly/fibers/Fiber.h | 2 +- folly/fibers/FiberManager.cpp | 6 +++--- folly/fibers/FiberManagerInternal-inl.h | 4 ++-- folly/fibers/FiberManagerInternal.h | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/folly/fibers/Fiber.cpp b/folly/fibers/Fiber.cpp index 495dd5f85bb..81a0ca32db6 100644 --- a/folly/fibers/Fiber.cpp +++ b/folly/fibers/Fiber.cpp @@ -79,7 +79,7 @@ Fiber::Fiber(FiberManager& fiberManager) void Fiber::init(bool recordStackUsed) { // It is necessary to disable the logic for ASAN because we change // the fiber's stack. -#ifndef FOLLY_SANITIZE_ADDRESS +#ifndef FOLLY_LIBRARY_SANITIZE_ADDRESS recordStackUsed_ = recordStackUsed; if (UNLIKELY(recordStackUsed_ && !stackFilledWithMagic_)) { CHECK_EQ( @@ -103,7 +103,7 @@ void Fiber::init(bool recordStackUsed) { } Fiber::~Fiber() { -#ifdef FOLLY_SANITIZE_ADDRESS +#ifdef FOLLY_LIBRARY_SANITIZE_ADDRESS if (asanFakeStack_ != nullptr) { fiberManager_.freeFakeStack(asanFakeStack_); } @@ -115,7 +115,7 @@ Fiber::~Fiber() { void Fiber::recordStackPosition() { // For ASAN builds, functions may run on fake stack. // So we cannot get meaningful stack position. -#ifndef FOLLY_SANITIZE_ADDRESS +#ifndef FOLLY_LIBRARY_SANITIZE_ADDRESS int stackDummy; auto currentPosition = static_cast( fiberStackLimit_ + fiberStackSize_ - @@ -126,7 +126,7 @@ void Fiber::recordStackPosition() { } [[noreturn]] void Fiber::fiberFunc() { -#ifdef FOLLY_SANITIZE_ADDRESS +#ifdef FOLLY_LIBRARY_SANITIZE_ADDRESS fiberManager_.registerFinishSwitchStackWithAsan( nullptr, &asanMainStackBase_, &asanMainStackSize_); #endif diff --git a/folly/fibers/Fiber.h b/folly/fibers/Fiber.h index c83127a8efd..9344096aefb 100644 --- a/folly/fibers/Fiber.h +++ b/folly/fibers/Fiber.h @@ -193,7 +193,7 @@ class Fiber { folly::IntrusiveListHook globalListHook_; /**< list hook for global list */ std::thread::id threadId_{}; -#ifdef FOLLY_SANITIZE_ADDRESS +#ifdef FOLLY_LIBRARY_SANITIZE_ADDRESS void* asanFakeStack_{nullptr}; const void* asanMainStackBase_{nullptr}; size_t asanMainStackSize_{0}; diff --git a/folly/fibers/FiberManager.cpp b/folly/fibers/FiberManager.cpp index f64f3aa52e4..40c7f8c70a7 100644 --- a/folly/fibers/FiberManager.cpp +++ b/folly/fibers/FiberManager.cpp @@ -33,7 +33,7 @@ #include #include -#ifdef FOLLY_SANITIZE_ADDRESS +#ifdef FOLLY_LIBRARY_SANITIZE_ADDRESS #ifndef _WIN32 #include @@ -217,7 +217,7 @@ void FiberManager::FibersPoolResizer::run() { } } -#ifdef FOLLY_SANITIZE_ADDRESS +#ifdef FOLLY_LIBRARY_SANITIZE_ADDRESS void FiberManager::registerStartSwitchStackWithAsan( void** saveFakeStack, @@ -336,7 +336,7 @@ static AsanUnpoisonMemoryRegionFuncPtr getUnpoisonMemoryRegionFunc() { return nullptr; } -#endif // FOLLY_SANITIZE_ADDRESS +#endif // FOLLY_LIBRARY_SANITIZE_ADDRESS // TVOS and WatchOS platforms have SIGSTKSZ but not sigaltstack #if defined(SIGSTKSZ) && !FOLLY_APPLE_TVOS && !FOLLY_APPLE_WATCHOS diff --git a/folly/fibers/FiberManagerInternal-inl.h b/folly/fibers/FiberManagerInternal-inl.h index 2da28158277..ad53ef7948f 100644 --- a/folly/fibers/FiberManagerInternal-inl.h +++ b/folly/fibers/FiberManagerInternal-inl.h @@ -66,7 +66,7 @@ inline void FiberManager::ensureLoopScheduled() { inline void FiberManager::activateFiber(Fiber* fiber) { DCHECK_EQ(activeFiber_, (Fiber*)nullptr); -#ifdef FOLLY_SANITIZE_ADDRESS +#ifdef FOLLY_LIBRARY_SANITIZE_ADDRESS DCHECK(!fiber->asanMainStackBase_); DCHECK(!fiber->asanMainStackSize_); auto stack = fiber->getStack(); @@ -86,7 +86,7 @@ inline void FiberManager::activateFiber(Fiber* fiber) { inline void FiberManager::deactivateFiber(Fiber* fiber) { DCHECK_EQ(activeFiber_, fiber); -#ifdef FOLLY_SANITIZE_ADDRESS +#ifdef FOLLY_LIBRARY_SANITIZE_ADDRESS DCHECK(fiber->asanMainStackBase_); DCHECK(fiber->asanMainStackSize_); diff --git a/folly/fibers/FiberManagerInternal.h b/folly/fibers/FiberManagerInternal.h index 4f47f475853..c3cd52e9288 100644 --- a/folly/fibers/FiberManagerInternal.h +++ b/folly/fibers/FiberManagerInternal.h @@ -601,7 +601,7 @@ class FiberManager : public ::folly::Executor { void runReadyFiber(Fiber* fiber); void remoteReadyInsert(Fiber* fiber); -#ifdef FOLLY_SANITIZE_ADDRESS +#ifdef FOLLY_LIBRARY_SANITIZE_ADDRESS // These methods notify ASAN when a fiber is entered/exited so that ASAN can // find the right stack extents when it needs to poison/unpoison the stack. @@ -617,7 +617,7 @@ class FiberManager : public ::folly::Executor { void freeFakeStack(void* fakeStack); void unpoisonFiberStack(const Fiber* fiber); -#endif // FOLLY_SANITIZE_ADDRESS +#endif // FOLLY_LIBRARY_SANITIZE_ADDRESS bool alternateSignalStackRegistered_{false}; From 79d52e9a36ffbf4833419bb8b69e999a3b90e7b0 Mon Sep 17 00:00:00 2001 From: Yashwanth Nannapaneni Date: Thu, 31 Dec 2020 22:09:26 +0000 Subject: [PATCH 2/7] include experimental/exception_tracer in library --- CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c88caa07972..971928523dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -149,7 +149,6 @@ auto_sources(hfiles "*.h" "RECURSE" "${FOLLY_DIR}") REMOVE_MATCHES_FROM_LISTS(files hfiles MATCHES "^${FOLLY_DIR}/build/" - "^${FOLLY_DIR}/experimental/exception_tracer/" "^${FOLLY_DIR}/futures/exercises/" "^${FOLLY_DIR}/logging/example/" "^${FOLLY_DIR}/(.*/)?test/" From 2f1be0defb69fd7f55f5352cd720f84a4bc07d49 Mon Sep 17 00:00:00 2001 From: Nathan G Bronson Date: Fri, 19 Mar 2021 14:14:11 +0000 Subject: [PATCH 3/7] [folly/container/F14] avoid operator!= ambiguity in c++20 Summary: F14 map `iterator` doesn't define `operator==` or `operator!=` to itself. Those member functions take `const_iterator` as the right-hand side, relying on the ability to convert implicitly from `iterator` to `const_iterator`. This interacts poorly with c++20's default equality operators, triggering clang's `-Wambiguous-reversed-operator`. This diff replaces the equality member functions with friends that use the same left-hand and right-hand types. iterator != const_iterator will now compile by implicitly converting the left-hand-side and then calling `operator!=(const_iterator const&, const_iterator const&)`, rather than calling `iterator::operator!=(const_iterator const&)`. This resolves the ambiguity in c++20, and should work fine on earlier versions, although I have not tested it on anything except c++17. Test Plan: does it build? --- folly/container/detail/F14Policy.h | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/folly/container/detail/F14Policy.h b/folly/container/detail/F14Policy.h index 4b44140f647..7c1211bda2d 100644 --- a/folly/container/detail/F14Policy.h +++ b/folly/container/detail/F14Policy.h @@ -469,11 +469,13 @@ class ValueContainerIterator : public ValueContainerIteratorBase { return cur; } - bool operator==(ValueContainerIterator const& rhs) const { - return underlying_ == rhs.underlying_; + friend bool operator==(ValueContainerIterator const& lhs, + ValueContainerIterator const& rhs) { + return lhs.underlying_ == rhs.underlying_; } - bool operator!=(ValueContainerIterator const& rhs) const { - return !(*this == rhs); + friend bool operator!=(ValueContainerIterator const& lhs, + ValueContainerIterator const& rhs) { + return !(lhs == rhs); } private: @@ -698,11 +700,13 @@ class NodeContainerIterator : public BaseIter> { return cur; } - bool operator==(NodeContainerIterator const& rhs) const { - return underlying_ == rhs.underlying_; + friend bool operator==(NodeContainerIterator const& lhs, + NodeContainerIterator const& rhs) { + return lhs.underlying_ == rhs.underlying_; } - bool operator!=(NodeContainerIterator const& rhs) const { - return !(*this == rhs); + friend bool operator!=(NodeContainerIterator const& lhs, + NodeContainerIterator const& rhs) { + return !(lhs == rhs); } private: @@ -936,11 +940,13 @@ class VectorContainerIterator : public BaseIter { return cur; } - bool operator==(VectorContainerIterator const& rhs) const { - return current_ == rhs.current_; + friend bool operator==(VectorContainerIterator const& lhs, + VectorContainerIterator const& rhs) { + return lhs.current_ == rhs.current_; } - bool operator!=(VectorContainerIterator const& rhs) const { - return !(*this == rhs); + friend bool operator!=(VectorContainerIterator const& lhs, + VectorContainerIterator const& rhs) { + return !(lhs == rhs); } private: From 8fbae2cc21beeb9ebd5b45e4bfb1fa35355e0c85 Mon Sep 17 00:00:00 2001 From: Nathan G Bronson Date: Thu, 1 Apr 2021 00:21:29 +0000 Subject: [PATCH 4/7] [folly] increase the alt stack size threshold for alternate symbolization Summary: If sigaltstack is in use, the alternate stack may be too small to run the normal SafeStackTracePrinter. In that case we must fall back to UnsafeSelfAllocateStackTracePrinter. Logic to enable this fallback already exists, but the threshold of 8931 bytes is not sufficient to run the current symbolizer code--the actual stack size needed by SafeStackTracePrinter is greater than 48K. GCC sanitizers use a sigaltstack of 4*SIGSTKSZ = 32K. `Dwarf::findLocation` makes space for locals with `sub $0xa198,%rsp`, which is 41368 bytes. Adjusting the alternate stack size on x86_64, I observed a failure at 48K and a success at 56K. Test Plan: 1. observe SIGSEGV during symbolization of SIGABRT in a GCC ASAN binary 2. adjust sigaltstack size to find a value that is (at least sometimes) big enough 3. increase threshold in folly, observe no SIGSEGV in the GCC ASAN binary --- folly/experimental/symbolizer/SignalHandler.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/folly/experimental/symbolizer/SignalHandler.cpp b/folly/experimental/symbolizer/SignalHandler.cpp index 2df79a24ebb..5dd0d94ea72 100644 --- a/folly/experimental/symbolizer/SignalHandler.cpp +++ b/folly/experimental/symbolizer/SignalHandler.cpp @@ -478,10 +478,11 @@ void signalHandler(int signum, siginfo_t* info, void* uctx) { #endif // FOLLY_USE_SYMBOLIZER -// Small sigaltstack size threshold. -// 8931 is known to cause the signal handler to stack overflow during -// symbolization even for a simple one-liner "kill(getpid(), SIGTERM)". -constexpr size_t kSmallSigAltStackSize = 8931; +// Small sigaltstack size threshold. If the alternate stack is too small we +// must use UnsafeSelfAllocateStackTracePrinter() to avoid stack overflow +// during symbolization of a signal. 48K has been observed to have stack +// overflow, and 56K has been observed to work. +constexpr size_t kSmallSigAltStackSize = 65536; FOLLY_MAYBE_UNUSED bool isSmallSigAltStackEnabled() { stack_t ss; From 6afdcee89d5fefadd502ba4677332c303f1c42b9 Mon Sep 17 00:00:00 2001 From: Nathan G Bronson Date: Tue, 6 Apr 2021 21:03:03 +0000 Subject: [PATCH 5/7] [folly] using-declaration to pull operator== into iterators for c++20 Summary: Clang's -Wambiguous-reversed-operator warning claims that dynamic's and IOBuf's iterators have an ambiguous operator== under C++20 rules. Language lawyers will have to decide whether this is actually an issue or an overly-aggressive warning that doesn't understand CRTP. Either way, a using declaration seems to be sufficient to resolve things. Test Plan: Build with clang-12 and -std=c++20 --- folly/dynamic-inl.h | 15 +++++++++++++++ folly/io/IOBuf.h | 8 ++++++++ 2 files changed, 23 insertions(+) diff --git a/folly/dynamic-inl.h b/folly/dynamic-inl.h index 1262e117d2d..21e1567aeff 100644 --- a/folly/dynamic-inl.h +++ b/folly/dynamic-inl.h @@ -239,6 +239,9 @@ struct dynamic::item_iterator : detail::IteratorAdaptor< /* implicit */ item_iterator(dynamic::ObjectImpl::iterator b) : Super(b) {} using object_type = dynamic::ObjectImpl; + + using Super::operator==; + using Super::operator!=; }; struct dynamic::value_iterator : detail::IteratorAdaptor< @@ -256,6 +259,9 @@ struct dynamic::value_iterator : detail::IteratorAdaptor< using object_type = dynamic::ObjectImpl; dynamic& dereference() const { return base()->second; } + + using Super::operator==; + using Super::operator!=; }; struct dynamic::const_item_iterator @@ -276,6 +282,9 @@ struct dynamic::const_item_iterator /* implicit */ const_item_iterator(item_iterator i) : Super(i.base()) {} using object_type = dynamic::ObjectImpl const; + + using Super::operator==; + using Super::operator!=; }; struct dynamic::const_key_iterator : detail::IteratorAdaptor< @@ -294,6 +303,9 @@ struct dynamic::const_key_iterator : detail::IteratorAdaptor< using object_type = dynamic::ObjectImpl const; dynamic const& dereference() const { return base()->first; } + + using Super::operator==; + using Super::operator!=; }; struct dynamic::const_value_iterator : detail::IteratorAdaptor< @@ -315,6 +327,9 @@ struct dynamic::const_value_iterator : detail::IteratorAdaptor< using object_type = dynamic::ObjectImpl const; dynamic const& dereference() const { return base()->second; } + + using Super::operator==; + using Super::operator!=; }; ////////////////////////////////////////////////////////////////////// diff --git a/folly/io/IOBuf.h b/folly/io/IOBuf.h index d971d9e6352..faff72ac5e3 100644 --- a/folly/io/IOBuf.h +++ b/folly/io/IOBuf.h @@ -1692,6 +1692,11 @@ class IOBuf::Iterator : public detail::IteratorFacade< IOBuf::Iterator, ByteRange const, std::forward_iterator_tag> { + using Super = detail::IteratorFacade< + IOBuf::Iterator, + ByteRange const, + std::forward_iterator_tag>; + public: // Note that IOBufs are stored as a circular list without a guard node, // so pos == end is ambiguous (it may mean "begin" or "end"). To solve @@ -1718,6 +1723,9 @@ class IOBuf::Iterator : public detail::IteratorFacade< return *this; } + using Super::operator==; + using Super::operator!=; + const ByteRange& dereference() const { return val_; } bool equal(const Iterator& other) const { From d8bd6d2e5713aac40d49345dd4a793acd4d1ea77 Mon Sep 17 00:00:00 2001 From: Tudor Bosman Date: Wed, 16 Jun 2021 00:54:38 +0000 Subject: [PATCH 6/7] Speed up findLocation in the absence of .debug_aranges --- folly/experimental/symbolizer/Dwarf.cpp | 87 +++++++++++++++++++++---- folly/experimental/symbolizer/Dwarf.h | 6 +- 2 files changed, 80 insertions(+), 13 deletions(-) diff --git a/folly/experimental/symbolizer/Dwarf.cpp b/folly/experimental/symbolizer/Dwarf.cpp index f15574bd7ec..b55aa5145e4 100644 --- a/folly/experimental/symbolizer/Dwarf.cpp +++ b/folly/experimental/symbolizer/Dwarf.cpp @@ -458,6 +458,9 @@ bool Dwarf::findDebugInfoOffset( * Best effort: * - fills @inlineFrames if mode == FULL_WITH_INLINE, * - calls @eachParameterName on the function parameters. + * + * if @checkAddress is true, we verify that the address is mapped to + * a range in this CU before running the line number VM */ bool Dwarf::findLocation( uintptr_t address, @@ -465,7 +468,8 @@ bool Dwarf::findLocation( detail::CompilationUnit& cu, LocationInfo& locationInfo, folly::Range inlineFrames, - folly::FunctionRef eachParameterName) const { + folly::FunctionRef eachParameterName, + bool checkAddress) const { detail::Die die = getDieAtOffset(cu, cu.firstDie); // Partial compilation unit (DW_TAG_partial_unit) is not supported. FOLLY_SAFE_CHECK( @@ -477,41 +481,98 @@ bool Dwarf::findLocation( folly::StringPiece compilationDirectory; folly::Optional mainFileName; folly::Optional baseAddrCU; + folly::Optional rangesOffset; + bool seenLowPC = false; + bool seenHighPC = false; + enum : unsigned { + kStmtList = 1U << 0, + kCompDir = 1U << 1, + kName = 1U << 2, + kLowPC = 1U << 3, + kHighPCOrRanges = 1U << 4, + }; + unsigned expectedAttributes = kStmtList | kCompDir | kName | kLowPC; + bool foundAddress = !checkAddress; + if (!foundAddress) { + expectedAttributes |= kHighPCOrRanges; + } forEachAttribute(cu, die, [&](const detail::Attribute& attr) { switch (attr.spec.name) { case DW_AT_stmt_list: + expectedAttributes &= ~kStmtList; // Offset in .debug_line for the line number VM program for this // compilation unit lineOffset = boost::get(attr.attrValue); break; case DW_AT_comp_dir: + expectedAttributes &= ~kCompDir; // Compilation directory compilationDirectory = boost::get(attr.attrValue); break; case DW_AT_name: + expectedAttributes &= ~kName; // File name of main file being compiled mainFileName = boost::get(attr.attrValue); break; case DW_AT_low_pc: - case DW_AT_entry_pc: - // 2.17.1: historically DW_AT_low_pc was used. DW_AT_entry_pc was - // introduced in DWARF3. Support either to determine the base address of - // the CU. + expectedAttributes &= ~kLowPC; baseAddrCU = boost::get(attr.attrValue); + if (!foundAddress) { + if (address < *baseAddrCU) { + return false; + } + seenLowPC = true; + if (seenHighPC) { + foundAddress = true; + } else if (rangesOffset) { + if (!isAddrInRangeList(address, baseAddrCU, *rangesOffset, + cu.addrSize)) { + return false; + } + foundAddress = true; + } + } + break; + case DW_AT_high_pc: + expectedAttributes &= ~kHighPCOrRanges; + if (!foundAddress) { + if (address >= boost::get(attr.attrValue)) { + return false; + } + seenHighPC = true; + foundAddress = seenLowPC; + } + break; + case DW_AT_ranges: + // 3.1.1: CU entries have: + // - either DW_AT_low_pc and DW_AT_high_pc + // OR + // - DW_AT_ranges and optional DW_AT_low_pc + expectedAttributes &= ~kHighPCOrRanges; + if (!foundAddress) { + rangesOffset = boost::get(attr.attrValue); + if (seenLowPC) { + if (!isAddrInRangeList(address, baseAddrCU, *rangesOffset, + cu.addrSize)) { + return false; + } + foundAddress = true; + } + } break; } - return true; // continue forEachAttribute + return (expectedAttributes != 0); // continue forEachAttribute }); + if (!foundAddress || !lineOffset) { + return false; + } + if (mainFileName) { locationInfo.hasMainFile = true; locationInfo.mainFile = Path(compilationDirectory, "", *mainFileName); } - if (!lineOffset) { - return false; - } - folly::StringPiece lineSection(debugLine_); lineSection.advance(*lineOffset); LineNumberVM lineVM(lineSection, compilationDirectory); @@ -660,7 +721,8 @@ bool Dwarf::findAddress( // Read compilation unit header from .debug_info auto unit = getCompilationUnit(debugInfo_, offset); return findLocation( - address, mode, unit, locationInfo, inlineFrames, eachParameterName); + address, mode, unit, locationInfo, inlineFrames, eachParameterName, + false /*checkAddress*/); } else if (mode == LocationInfoMode::FAST) { // NOTE: Clang (when using -gdwarf-aranges) doesn't generate entries // in .debug_aranges for some functions, but always generates @@ -688,7 +750,8 @@ bool Dwarf::findAddress( unit, locationInfo, inlineFrames, - eachParameterName)) { + eachParameterName, + true /*checkAddress*/)) { return true; } } diff --git a/folly/experimental/symbolizer/Dwarf.h b/folly/experimental/symbolizer/Dwarf.h index 564e419df1d..479b1f1e660 100644 --- a/folly/experimental/symbolizer/Dwarf.h +++ b/folly/experimental/symbolizer/Dwarf.h @@ -122,6 +122,9 @@ class Dwarf { * Best effort: * - fills @inlineFrames if mode == FULL_WITH_INLINE, * - calls @eachParameterName on the function parameters. + * + * if @checkAddress is true, we verify that the address is mapped to + * a range in this CU before running the line number VM */ bool findLocation( uintptr_t address, @@ -129,7 +132,8 @@ class Dwarf { detail::CompilationUnit& cu, LocationInfo& info, folly::Range inlineFrames, - folly::FunctionRef eachParameterName) const; + folly::FunctionRef eachParameterName, + bool checkAddress = true) const; /** * Finds a subprogram debugging info entry that contains a given address among From 5d0323f18b1823bd47dec808a6f8854e4fed11cc Mon Sep 17 00:00:00 2001 From: Wei Li Date: Thu, 26 May 2022 00:34:55 +0000 Subject: [PATCH 7/7] prefetch first matched value --- folly/container/F14Map.h | 5 +++++ folly/container/detail/F14Table.h | 13 +++++++++++++ 2 files changed, 18 insertions(+) diff --git a/folly/container/F14Map.h b/folly/container/F14Map.h index 430cbe7a113..f2ead4433a4 100644 --- a/folly/container/F14Map.h +++ b/folly/container/F14Map.h @@ -628,6 +628,11 @@ class F14BasicMap { return table_.prehash(key); } + template + void prefetchFirstMatchedValue(K const& key) const { + return table_.prefetchFirstMatchedValue(key); + } + FOLLY_ALWAYS_INLINE iterator find(key_type const& key) { return table_.makeIter(table_.find(key)); } diff --git a/folly/container/detail/F14Table.h b/folly/container/detail/F14Table.h index 9aa2b8af723..591a40e63bf 100644 --- a/folly/container/detail/F14Table.h +++ b/folly/container/detail/F14Table.h @@ -1323,6 +1323,19 @@ class F14Table : public Policy { prefetchAddr(firstChunk); return F14HashToken(std::move(hp)); } + + // Prefetch the first matched value if there is at least one match in tags + template + void prefetchFirstMatchedValue(K const& key) const { + auto hp = splitHash(this->computeKeyHash(key)); + std::size_t index = hp.first; + ChunkPtr chunk = chunks_ + (index & chunkMask_); + auto hits = chunk->tagMatchIter(hp.second); + if (hits.hasNext()) { + auto i = hits.next(); + this->prefetchValue(chunk->item(i)); + } + } template FOLLY_ALWAYS_INLINE ItemIter find(K const& key) const {