Skip to content

Commit 3e5d8a1

Browse files
committed
Reapply "RuntimeLibcalls: Generate table of libcall name lengths (#153… (#153864)
This reverts commit 334e9bf. Check if llvm-nm exists before building the benchmark.
1 parent cf5f311 commit 3e5d8a1

File tree

14 files changed

+545
-100
lines changed

14 files changed

+545
-100
lines changed

llvm/benchmarks/CMakeLists.txt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,24 @@ add_benchmark(FormatVariadicBM FormatVariadicBM.cpp PARTIAL_SOURCES_INTENDED)
1111
add_benchmark(GetIntrinsicInfoTableEntriesBM GetIntrinsicInfoTableEntriesBM.cpp PARTIAL_SOURCES_INTENDED)
1212
add_benchmark(SandboxIRBench SandboxIRBench.cpp PARTIAL_SOURCES_INTENDED)
1313

14+
add_benchmark(RuntimeLibcallsBench RuntimeLibcalls.cpp PARTIAL_SOURCES_INTENDED)
15+
16+
17+
if(TARGET llvm-nm)
18+
# Extract the list of symbols in a random utility as sample data.
19+
set(SYMBOL_TEST_DATA_FILE "sample_symbol_list.txt")
20+
set(SYMBOL_TEST_DATA_SOURCE_BINARY $<TARGET_FILE:llc>)
21+
22+
add_custom_command(OUTPUT ${SYMBOL_TEST_DATA_FILE}
23+
COMMAND $<TARGET_FILE:llvm-nm> --no-demangle --no-sort
24+
--format=just-symbols
25+
${SYMBOL_TEST_DATA_SOURCE_BINARY} > ${SYMBOL_TEST_DATA_FILE}
26+
DEPENDS "$<TARGET_FILE:llvm-nm>" "$<TARGET_FILE:llc>")
27+
28+
add_custom_target(generate-runtime-libcalls-sample-symbol-list
29+
DEPENDS ${SYMBOL_TEST_DATA_FILE})
30+
31+
add_dependencies(RuntimeLibcallsBench generate-runtime-libcalls-sample-symbol-list)
32+
target_compile_definitions(RuntimeLibcallsBench PRIVATE
33+
-DSYMBOL_TEST_DATA_FILE="${CMAKE_CURRENT_BINARY_DIR}/${SYMBOL_TEST_DATA_FILE}")
34+
endif()

llvm/benchmarks/RuntimeLibcalls.cpp

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "llvm/IR/RuntimeLibcalls.h"
10+
#include "benchmark/benchmark.h"
11+
#include "llvm/IR/DataLayout.h"
12+
#include "llvm/Support/Error.h"
13+
#include "llvm/Support/LineIterator.h"
14+
#include "llvm/Support/MemoryBuffer.h"
15+
#include "llvm/TargetParser/Triple.h"
16+
#include <random>
17+
#include <string>
18+
using namespace llvm;
19+
20+
static constexpr unsigned MaxFuncNameSize = 53;
21+
22+
static std::vector<StringRef> getLibcallNameStringRefs() {
23+
std::vector<StringRef> Names(RTLIB::NumLibcallImpls);
24+
// Keep the strlens on the StringRef construction out of the benchmark loop.
25+
for (RTLIB::LibcallImpl LC : RTLIB::libcall_impls())
26+
Names[LC] = RTLIB::RuntimeLibcallsInfo::getLibcallImplName(LC);
27+
28+
return Names;
29+
}
30+
31+
static std::vector<std::string> getRandomFuncNames() {
32+
std::mt19937_64 Rng;
33+
std::uniform_int_distribution<> StringLengthDistribution(1, MaxFuncNameSize);
34+
std::uniform_int_distribution<> CharDistribution(1, 255);
35+
int NumTestFuncs = 1 << 10;
36+
std::vector<std::string> TestFuncNames(NumTestFuncs);
37+
38+
for (std::string &TestFuncName : TestFuncNames) {
39+
for (int I = 0, E = StringLengthDistribution(Rng); I != E; ++I)
40+
TestFuncName += static_cast<char>(CharDistribution(Rng));
41+
}
42+
43+
return TestFuncNames;
44+
}
45+
46+
#ifdef SYMBOL_TEST_DATA_FILE
47+
static std::vector<std::string> readSymbolsFromFile(StringRef InputFile) {
48+
auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile, /*IsText=*/true);
49+
if (!BufOrError) {
50+
reportFatalUsageError("failed to open \'" + Twine(InputFile) +
51+
"\': " + BufOrError.getError().message());
52+
}
53+
54+
// Hackily figure out if there's a prefix on the symbol names - llvm-nm
55+
// appears to not have a flag to skip this.
56+
llvm::Triple HostTriple(LLVM_HOST_TRIPLE);
57+
std::string DummyDatalayout = "e";
58+
DummyDatalayout += DataLayout::getManglingComponent(HostTriple);
59+
60+
DataLayout DL(DummyDatalayout);
61+
char GlobalPrefix = DL.getGlobalPrefix();
62+
63+
std::vector<std::string> Lines;
64+
for (line_iterator LineIt(**BufOrError, /*SkipBlanks=*/true);
65+
!LineIt.is_at_eof(); ++LineIt) {
66+
StringRef SymbolName = *LineIt;
67+
SymbolName.consume_front(StringRef(&GlobalPrefix, 1));
68+
69+
Lines.push_back(SymbolName.str());
70+
}
71+
return Lines;
72+
}
73+
#endif
74+
75+
static void BM_LookupRuntimeLibcallByNameKnownCalls(benchmark::State &State) {
76+
std::vector<StringRef> Names = getLibcallNameStringRefs();
77+
78+
for (auto _ : State) {
79+
for (StringRef Name : Names) {
80+
benchmark::DoNotOptimize(
81+
RTLIB::RuntimeLibcallsInfo::lookupLibcallImplName(Name).empty());
82+
}
83+
}
84+
}
85+
86+
static void BM_LookupRuntimeLibcallByNameRandomCalls(benchmark::State &State) {
87+
std::vector<std::string> TestFuncNames = getRandomFuncNames();
88+
89+
for (auto _ : State) {
90+
for (const std::string &Name : TestFuncNames) {
91+
benchmark::DoNotOptimize(
92+
RTLIB::RuntimeLibcallsInfo::lookupLibcallImplName(StringRef(Name))
93+
.empty());
94+
}
95+
}
96+
}
97+
98+
#ifdef SYMBOL_TEST_DATA_FILE
99+
// This isn't fully representative, it doesn't include any anonymous functions.
100+
// nm -n --no-demangle --format=just-symbols sample-binary > sample.txt
101+
static void BM_LookupRuntimeLibcallByNameSampleData(benchmark::State &State) {
102+
std::vector<std::string> TestFuncNames =
103+
readSymbolsFromFile(SYMBOL_TEST_DATA_FILE);
104+
for (auto _ : State) {
105+
for (const std::string &Name : TestFuncNames) {
106+
benchmark::DoNotOptimize(
107+
RTLIB::RuntimeLibcallsInfo::lookupLibcallImplName(StringRef(Name))
108+
.empty());
109+
}
110+
}
111+
}
112+
#endif
113+
114+
BENCHMARK(BM_LookupRuntimeLibcallByNameKnownCalls);
115+
BENCHMARK(BM_LookupRuntimeLibcallByNameRandomCalls);
116+
117+
#ifdef SYMBOL_TEST_DATA_FILE
118+
BENCHMARK(BM_LookupRuntimeLibcallByNameSampleData);
119+
#endif
120+
121+
BENCHMARK_MAIN();

llvm/include/llvm/CodeGen/TargetLowering.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3557,15 +3557,19 @@ class LLVM_ABI TargetLoweringBase {
35573557

35583558
/// Get the libcall routine name for the specified libcall.
35593559
const char *getLibcallName(RTLIB::Libcall Call) const {
3560-
return Libcalls.getLibcallName(Call);
3560+
// FIXME: Return StringRef
3561+
return Libcalls.getLibcallName(Call).data();
35613562
}
35623563

35633564
/// Get the libcall routine name for the specified libcall implementation
3564-
const char *getLibcallImplName(RTLIB::LibcallImpl Call) const {
3565-
return Libcalls.getLibcallImplName(Call);
3565+
static StringRef getLibcallImplName(RTLIB::LibcallImpl Call) {
3566+
return RTLIB::RuntimeLibcallsInfo::getLibcallImplName(Call);
35663567
}
35673568

3568-
const char *getMemcpyName() const { return Libcalls.getMemcpyName(); }
3569+
const char *getMemcpyName() const {
3570+
// FIXME: Return StringRef
3571+
return Libcalls.getMemcpyName().data();
3572+
}
35693573

35703574
/// Get the comparison predicate that's to be used to test the result of the
35713575
/// comparison libcall against zero. This should only be used with

llvm/include/llvm/IR/RuntimeLibcalls.h

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -77,17 +77,17 @@ struct RuntimeLibcallsInfo {
7777

7878
/// Get the libcall routine name for the specified libcall.
7979
// FIXME: This should be removed. Only LibcallImpl should have a name.
80-
const char *getLibcallName(RTLIB::Libcall Call) const {
80+
StringRef getLibcallName(RTLIB::Libcall Call) const {
8181
return getLibcallImplName(LibcallImpls[Call]);
8282
}
8383

8484
/// Get the libcall routine name for the specified libcall implementation.
85-
// FIXME: Change to return StringRef
86-
static const char *getLibcallImplName(RTLIB::LibcallImpl CallImpl) {
85+
static StringRef getLibcallImplName(RTLIB::LibcallImpl CallImpl) {
8786
if (CallImpl == RTLIB::Unsupported)
88-
return nullptr;
89-
return RuntimeLibcallImplNameTable[RuntimeLibcallNameOffsetTable[CallImpl]]
90-
.data();
87+
return StringRef();
88+
return StringRef(RuntimeLibcallImplNameTable.getCString(
89+
RuntimeLibcallNameOffsetTable[CallImpl]),
90+
RuntimeLibcallNameSizeTable[CallImpl]);
9191
}
9292

9393
/// Return the lowering's selection of implementation call for \p Call
@@ -119,9 +119,10 @@ struct RuntimeLibcallsInfo {
119119

120120
/// Return a function name compatible with RTLIB::MEMCPY, or nullptr if fully
121121
/// unsupported.
122-
const char *getMemcpyName() const {
123-
if (const char *Memcpy = getLibcallName(RTLIB::MEMCPY))
124-
return Memcpy;
122+
StringRef getMemcpyName() const {
123+
RTLIB::LibcallImpl Memcpy = getLibcallImpl(RTLIB::MEMCPY);
124+
if (Memcpy != RTLIB::Unsupported)
125+
return getLibcallImplName(Memcpy);
125126

126127
// Fallback to memmove if memcpy isn't available.
127128
return getLibcallName(RTLIB::MEMMOVE);
@@ -132,11 +133,41 @@ struct RuntimeLibcallsInfo {
132133
return ImplToLibcall[Impl];
133134
}
134135

136+
/// Check if a function name is a recognized runtime call of any kind. This
137+
/// does not consider if this call is available for any current compilation,
138+
/// just that it is a known call somewhere. This returns the set of all
139+
/// LibcallImpls which match the name; multiple implementations with the same
140+
/// name may exist but differ in interpretation based on the target context.
141+
///
142+
/// Generated by tablegen.
143+
LLVM_ABI static inline iota_range<RTLIB::LibcallImpl>
144+
lookupLibcallImplName(StringRef Name){
145+
// Inlining the early exit on the string name appears to be worthwhile when
146+
// querying a real set of symbols
147+
#define GET_LOOKUP_LIBCALL_IMPL_NAME_BODY
148+
#include "llvm/IR/RuntimeLibcalls.inc"
149+
#undef GET_LOOKUP_LIBCALL_IMPL_NAME_BODY
150+
}
151+
135152
/// Check if this is valid libcall for the current module, otherwise
136153
/// RTLIB::Unsupported.
137-
LLVM_ABI RTLIB::LibcallImpl getSupportedLibcallImpl(StringRef FuncName) const;
154+
LLVM_ABI RTLIB::LibcallImpl
155+
getSupportedLibcallImpl(StringRef FuncName) const {
156+
for (RTLIB::LibcallImpl Impl : lookupLibcallImplName(FuncName)) {
157+
// FIXME: This should not depend on looking up ImplToLibcall, only the
158+
// list of libcalls for the module.
159+
RTLIB::LibcallImpl Recognized = LibcallImpls[ImplToLibcall[Impl]];
160+
if (Recognized != RTLIB::Unsupported)
161+
return Recognized;
162+
}
163+
164+
return RTLIB::Unsupported;
165+
}
138166

139167
private:
168+
LLVM_ABI static iota_range<RTLIB::LibcallImpl>
169+
lookupLibcallImplNameImpl(StringRef Name);
170+
140171
/// Stores the implementation choice for each each libcall.
141172
RTLIB::LibcallImpl LibcallImpls[RTLIB::UNKNOWN_LIBCALL + 1] = {
142173
RTLIB::Unsupported};
@@ -153,17 +184,16 @@ struct RuntimeLibcallsInfo {
153184
LLVM_ABI static const char RuntimeLibcallImplNameTableStorage[];
154185
LLVM_ABI static const StringTable RuntimeLibcallImplNameTable;
155186
LLVM_ABI static const uint16_t RuntimeLibcallNameOffsetTable[];
187+
LLVM_ABI static const uint8_t RuntimeLibcallNameSizeTable[];
156188

157189
/// Map from a concrete LibcallImpl implementation to its RTLIB::Libcall kind.
158190
LLVM_ABI static const RTLIB::Libcall ImplToLibcall[RTLIB::NumLibcallImpls];
159191

160-
/// Check if a function name is a recognized runtime call of any kind. This
161-
/// does not consider if this call is available for any current compilation,
162-
/// just that it is a known call somewhere. This returns the set of all
163-
/// LibcallImpls which match the name; multiple implementations with the same
164-
/// name may exist but differ in interpretation based on the target context.
165-
LLVM_ABI static iterator_range<ArrayRef<uint16_t>::const_iterator>
166-
getRecognizedLibcallImpls(StringRef FuncName);
192+
/// Utility function for tablegenerated lookup function. Return a range of
193+
/// enum values that apply for the function name at \p NameOffsetEntry with
194+
/// the value \p StrOffset.
195+
static inline iota_range<RTLIB::LibcallImpl>
196+
libcallImplNameHit(uint16_t NameOffsetEntry, uint16_t StrOffset);
167197

168198
static bool darwinHasSinCosStret(const Triple &TT) {
169199
if (!TT.isOSDarwin())

llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ static bool lowerObjCCall(Function &F, RTLIB::LibcallImpl NewFn,
145145

146146
// FIXME: When RuntimeLibcalls is an analysis, check if the function is really
147147
// supported, and go through RTLIB::Libcall.
148-
const char *NewFnName = RTLIB::RuntimeLibcallsInfo::getLibcallImplName(NewFn);
148+
StringRef NewFnName = RTLIB::RuntimeLibcallsInfo::getLibcallImplName(NewFn);
149149

150150
// If we haven't already looked up this function, check to see if the
151151
// program already contains a function with this name.

llvm/lib/IR/RuntimeLibcalls.cpp

Lines changed: 18 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "llvm/IR/RuntimeLibcalls.h"
1010
#include "llvm/ADT/StringTable.h"
1111
#include "llvm/Support/Debug.h"
12+
#include "llvm/Support/xxhash.h"
1213
#include "llvm/TargetParser/ARMTargetParser.h"
1314

1415
#define DEBUG_TYPE "runtime-libcalls-info"
@@ -18,9 +19,11 @@ using namespace RTLIB;
1819

1920
#define GET_INIT_RUNTIME_LIBCALL_NAMES
2021
#define GET_SET_TARGET_RUNTIME_LIBCALL_SETS
22+
#define DEFINE_GET_LOOKUP_LIBCALL_IMPL_NAME
2123
#include "llvm/IR/RuntimeLibcalls.inc"
2224
#undef GET_INIT_RUNTIME_LIBCALL_NAMES
2325
#undef GET_SET_TARGET_RUNTIME_LIBCALL_SETS
26+
#undef DEFINE_GET_LOOKUP_LIBCALL_IMPL_NAME
2427

2528
/// Set default libcall names. If a target wants to opt-out of a libcall it
2629
/// should be placed here.
@@ -58,49 +61,23 @@ void RuntimeLibcallsInfo::initLibcalls(const Triple &TT,
5861
}
5962
}
6063

61-
RTLIB::LibcallImpl
62-
RuntimeLibcallsInfo::getSupportedLibcallImpl(StringRef FuncName) const {
63-
const ArrayRef<uint16_t> RuntimeLibcallNameOffsets(
64-
RuntimeLibcallNameOffsetTable);
65-
66-
iterator_range<ArrayRef<uint16_t>::const_iterator> Range =
67-
getRecognizedLibcallImpls(FuncName);
68-
69-
for (auto I = Range.begin(); I != Range.end(); ++I) {
70-
RTLIB::LibcallImpl Impl =
71-
static_cast<RTLIB::LibcallImpl>(I - RuntimeLibcallNameOffsets.begin());
72-
73-
// FIXME: This should not depend on looking up ImplToLibcall, only the list
74-
// of libcalls for the module.
75-
RTLIB::LibcallImpl Recognized = LibcallImpls[ImplToLibcall[Impl]];
76-
if (Recognized != RTLIB::Unsupported)
77-
return Recognized;
64+
LLVM_ATTRIBUTE_ALWAYS_INLINE
65+
iota_range<RTLIB::LibcallImpl>
66+
RuntimeLibcallsInfo::libcallImplNameHit(uint16_t NameOffsetEntry,
67+
uint16_t StrOffset) {
68+
int NumAliases = 1;
69+
for (uint16_t Entry : ArrayRef(RuntimeLibcallNameOffsetTable)
70+
.drop_front(NameOffsetEntry + 1)) {
71+
if (Entry != StrOffset)
72+
break;
73+
++NumAliases;
7874
}
7975

80-
return RTLIB::Unsupported;
81-
}
82-
83-
iterator_range<ArrayRef<uint16_t>::const_iterator>
84-
RuntimeLibcallsInfo::getRecognizedLibcallImpls(StringRef FuncName) {
85-
StringTable::Iterator It = lower_bound(RuntimeLibcallImplNameTable, FuncName);
86-
if (It == RuntimeLibcallImplNameTable.end() || *It != FuncName)
87-
return iterator_range(ArrayRef<uint16_t>());
88-
89-
uint16_t IndexVal = It.offset().value();
90-
const ArrayRef<uint16_t> TableRef(RuntimeLibcallNameOffsetTable);
91-
92-
ArrayRef<uint16_t>::const_iterator E = TableRef.end();
93-
ArrayRef<uint16_t>::const_iterator EntriesBegin =
94-
std::lower_bound(TableRef.begin(), E, IndexVal);
95-
ArrayRef<uint16_t>::const_iterator EntriesEnd = EntriesBegin;
96-
97-
while (EntriesEnd != E && *EntriesEnd == IndexVal)
98-
++EntriesEnd;
99-
100-
assert(EntriesBegin != E &&
101-
"libcall found in name table but not offset table");
102-
103-
return make_range(EntriesBegin, EntriesEnd);
76+
RTLIB::LibcallImpl ImplStart = static_cast<RTLIB::LibcallImpl>(
77+
&RuntimeLibcallNameOffsetTable[NameOffsetEntry] -
78+
&RuntimeLibcallNameOffsetTable[0]);
79+
return enum_seq(ImplStart,
80+
static_cast<RTLIB::LibcallImpl>(ImplStart + NumAliases));
10481
}
10582

10683
bool RuntimeLibcallsInfo::isAAPCS_ABI(const Triple &TT, StringRef ABIName) {

llvm/lib/LTO/LTO.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1422,7 +1422,7 @@ SmallVector<const char *> LTO::getRuntimeLibcallSymbols(const Triple &TT) {
14221422

14231423
for (RTLIB::LibcallImpl Impl : LibcallImpls) {
14241424
if (Impl != RTLIB::Unsupported)
1425-
LibcallSymbols.push_back(Libcalls.getLibcallImplName(Impl));
1425+
LibcallSymbols.push_back(Libcalls.getLibcallImplName(Impl).data());
14261426
}
14271427

14281428
return LibcallSymbols;

0 commit comments

Comments
 (0)