llvm.org GIT mirror llvm / 002b63f
[SampleFDO] add suffix elision control for fcn names Summary: Add hooks for determining the policy used to decide whether/how to chop off symbol 'suffixes' when locating a given function in a sample profile. Prior to this change, any function symbols of the form "X.Y" were elided/truncated into just "X" when looking up things in a sample profile data file. With this change, the policy on suffixes can be changed by adding a new attribute "sample-profile-suffix-elision-policy" to the function: this attribute can have the value "all" (the default), "selected", or "none". A value of "all" preserves the previous behavior (chop off everything after the first "." character, then treat that as the symbol name). A value of "selected" chops off only the rightmost ".llvm.XXXX" suffix (where "XXX" is any string not containing a "." char). A value of "none" indicates that names should be left as is. Subscribers: jdoerfert, wmi, mtrofin, danielcdh, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D58832 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@356146 91177308-0d34-0410-b5e6-96231b3b80d8 Than McIntosh 1 year, 8 months ago
4 changed file(s) with 183 addition(s) and 11 deletion(s). Raw diff Collapse all Expand all
409409 return getNameInModule(Name, M);
410410 }
411411
412 /// Return the canonical name for a function, taking into account
413 /// suffix elision policy attributes.
414 static StringRef getCanonicalFnName(const Function &F) {
415 static const char *knownSuffixes[] = { ".llvm.", ".part." };
416 auto AttrName = "sample-profile-suffix-elision-policy";
417 auto Attr = F.getFnAttribute(AttrName).getValueAsString();
418 if (Attr == "" || Attr == "all") {
419 return F.getName().split('.').first;
420 } else if (Attr == "selected") {
421 StringRef Cand(F.getName());
422 for (const auto &Suf : knownSuffixes) {
423 StringRef Suffix(Suf);
424 auto It = Cand.rfind(Suffix);
425 if (It == StringRef::npos)
426 return Cand;
427 auto Dit = Cand.rfind('.');
428 if (Dit == It + Suffix.size() - 1)
429 Cand = Cand.substr(0, It);
430 }
431 return Cand;
432 } else if (Attr == "none") {
433 return F.getName();
434 } else {
435 assert(false && "internal error: unknown suffix elision policy");
436 }
437 return F.getName();
438 }
439
412440 /// Translate \p Name into its original name in Module.
413441 /// When the Format is not SPF_Compact_Binary, \p Name needs no translation.
414442 /// When the Format is SPF_Compact_Binary, \p Name in current FunctionSamples
464492 /// built in post-thin-link phase and var promotion has been done,
465493 /// we need to add the substring of function name without the suffix
466494 /// into the GUIDToFuncNameMap.
467 auto pos = OrigName.find('.');
468 if (pos != StringRef::npos) {
469 StringRef NewName = OrigName.substr(0, pos);
470 GUIDToFuncNameMap.insert({Function::getGUID(NewName), NewName});
471 }
495 StringRef CanonName = getCanonicalFnName(F);
496 if (CanonName != OrigName)
497 GUIDToFuncNameMap.insert({Function::getGUID(CanonName), CanonName});
472498 }
473499 CurrentModule = &M;
474500 }
285285
286286 /// Return the samples collected for function \p F.
287287 FunctionSamples *getSamplesFor(const Function &F) {
288 // The function name may have been updated by adding suffix. In sample
289 // profile, the function names are all stripped, so we need to strip
290 // the function name suffix before matching with profile.
291 return getSamplesFor(F.getName().split('.').first);
288 // The function name may have been updated by adding suffix. Call
289 // a helper to (optionally) strip off suffixes so that we can
290 // match against the original function name in the profile.
291 StringRef CanonName = FunctionSamples::getCanonicalFnName(F);
292 return getSamplesFor(CanonName);
292293 }
293294
294295 /// Return the samples collected for function \p F.
592592 void SampleProfileReaderCompactBinary::collectFuncsToUse(const Module &M) {
593593 FuncsToUse.clear();
594594 for (auto &F : M) {
595 StringRef Fname = F.getName().split('.').first;
596 FuncsToUse.insert(Fname);
595 StringRef CanonName = FunctionSamples::getCanonicalFnName(F);
596 FuncsToUse.insert(CanonName);
597597 }
598598 }
599599
198198 VerifySummary(*PS);
199199 delete PS;
200200 }
201
202 void addFunctionSamples(StringMap *Smap, const char *Fname,
203 uint64_t TotalSamples, uint64_t HeadSamples) {
204 StringRef Name(Fname);
205 FunctionSamples FcnSamples;
206 FcnSamples.setName(Name);
207 FcnSamples.addTotalSamples(TotalSamples);
208 FcnSamples.addHeadSamples(HeadSamples);
209 FcnSamples.addBodySamples(1, 0, HeadSamples);
210 (*Smap)[Name] = FcnSamples;
211 }
212
213 StringMap setupFcnSamplesForElisionTest(StringRef Policy) {
214 StringMap Smap;
215 addFunctionSamples(&Smap, "foo", uint64_t(20301), uint64_t(1437));
216 if (Policy == "" || Policy == "all")
217 return Smap;
218 addFunctionSamples(&Smap, "foo.bar", uint64_t(20303), uint64_t(1439));
219 if (Policy == "selected")
220 return Smap;
221 addFunctionSamples(&Smap, "foo.llvm.2465", uint64_t(20305), uint64_t(1441));
222 return Smap;
223 }
224
225 void createFunctionWithSampleProfileElisionPolicy(Module *M,
226 const char *Fname,
227 StringRef Policy) {
228 FunctionType *FnType =
229 FunctionType::get(Type::getVoidTy(Context), {}, false);
230 auto Inserted = M->getOrInsertFunction(Fname, FnType);
231 auto Fcn = cast(Inserted.getCallee());
232 if (Policy != "")
233 Fcn->addFnAttr("sample-profile-suffix-elision-policy", Policy);
234 }
235
236 void setupModuleForElisionTest(Module *M, StringRef Policy) {
237 createFunctionWithSampleProfileElisionPolicy(M, "foo", Policy);
238 createFunctionWithSampleProfileElisionPolicy(M, "foo.bar", Policy);
239 createFunctionWithSampleProfileElisionPolicy(M, "foo.llvm.2465", Policy);
240 }
241
242 void testSuffixElisionPolicy(SampleProfileFormat Format, StringRef Policy,
243 const StringMap &Expected) {
244 SmallVector ProfilePath;
245 std::error_code EC;
246 EC = llvm::sys::fs::createTemporaryFile("profile", "", ProfilePath);
247 ASSERT_TRUE(NoError(EC));
248 StringRef ProfileFile(ProfilePath.data(), ProfilePath.size());
249
250 Module M("my_module", Context);
251 setupModuleForElisionTest(&M, Policy);
252 StringMap ProfMap = setupFcnSamplesForElisionTest(Policy);
253
254 // write profile
255 createWriter(Format, ProfileFile);
256 EC = Writer->write(ProfMap);
257 ASSERT_TRUE(NoError(EC));
258 Writer->getOutputStream().flush();
259
260 // read profile
261 readProfile(M, ProfileFile);
262 EC = Reader->read();
263 ASSERT_TRUE(NoError(EC));
264
265 for (auto I = Expected.begin(); I != Expected.end(); ++I) {
266 uint64_t Esamples = uint64_t(-1);
267 FunctionSamples *Samples = Reader->getSamplesFor(I->getKey());
268 if (Samples != nullptr)
269 Esamples = Samples->getTotalSamples();
270 ASSERT_EQ(I->getValue(), Esamples);
271 }
272 }
201273 };
202274
203275 TEST_F(SampleProfTest, roundtrip_text_profile) {
250322 ASSERT_EQ(BodySamples.get(), Max);
251323 }
252324
325 TEST_F(SampleProfTest, default_suffix_elision_text) {
326 // Default suffix elision policy: strip everything after first dot.
327 // This implies that all suffix variants will map to "foo", so
328 // we don't expect to see any entries for them in the sample
329 // profile.
330 StringMap Expected;
331 Expected["foo"] = uint64_t(20301);
332 Expected["foo.bar"] = uint64_t(-1);
333 Expected["foo.llvm.2465"] = uint64_t(-1);
334 testSuffixElisionPolicy(SampleProfileFormat::SPF_Text, "", Expected);
335 }
336
337 TEST_F(SampleProfTest, default_suffix_elision_compact_binary) {
338 // Default suffix elision policy: strip everything after first dot.
339 // This implies that all suffix variants will map to "foo", so
340 // we don't expect to see any entries for them in the sample
341 // profile.
342 StringMap Expected;
343 Expected["foo"] = uint64_t(20301);
344 Expected["foo.bar"] = uint64_t(-1);
345 Expected["foo.llvm.2465"] = uint64_t(-1);
346 testSuffixElisionPolicy(SampleProfileFormat::SPF_Compact_Binary, "",
347 Expected);
348 }
349
350 TEST_F(SampleProfTest, selected_suffix_elision_text) {
351 // Profile is created and searched using the "selected"
352 // suffix elision policy: we only strip a .XXX suffix if
353 // it matches a pattern known to be generated by the compiler
354 // (e.g. ".llvm.").
355 StringMap Expected;
356 Expected["foo"] = uint64_t(20301);
357 Expected["foo.bar"] = uint64_t(20303);
358 Expected["foo.llvm.2465"] = uint64_t(-1);
359 testSuffixElisionPolicy(SampleProfileFormat::SPF_Text, "selected", Expected);
360 }
361
362 TEST_F(SampleProfTest, selected_suffix_elision_compact_binary) {
363 // Profile is created and searched using the "selected"
364 // suffix elision policy: we only strip a .XXX suffix if
365 // it matches a pattern known to be generated by the compiler
366 // (e.g. ".llvm.").
367 StringMap Expected;
368 Expected["foo"] = uint64_t(20301);
369 Expected["foo.bar"] = uint64_t(20303);
370 Expected["foo.llvm.2465"] = uint64_t(-1);
371 testSuffixElisionPolicy(SampleProfileFormat::SPF_Compact_Binary, "selected",
372 Expected);
373 }
374
375 TEST_F(SampleProfTest, none_suffix_elision_text) {
376 // Profile is created and searched using the "none"
377 // suffix elision policy: no stripping of suffixes at all.
378 // Here we expect to see all variants in the profile.
379 StringMap Expected;
380 Expected["foo"] = uint64_t(20301);
381 Expected["foo.bar"] = uint64_t(20303);
382 Expected["foo.llvm.2465"] = uint64_t(20305);
383 testSuffixElisionPolicy(SampleProfileFormat::SPF_Text, "none", Expected);
384 }
385
386 TEST_F(SampleProfTest, none_suffix_elision_compact_binary) {
387 // Profile is created and searched using the "none"
388 // suffix elision policy: no stripping of suffixes at all.
389 // Here we expect to see all variants in the profile.
390 StringMap Expected;
391 Expected["foo"] = uint64_t(20301);
392 Expected["foo.bar"] = uint64_t(20303);
393 Expected["foo.llvm.2465"] = uint64_t(20305);
394 testSuffixElisionPolicy(SampleProfileFormat::SPF_Compact_Binary, "none",
395 Expected);
396 }
397
253398 } // end anonymous namespace