Skip to content

Commit 2788430

Browse files
committed
[llvm][Dwarf] Add LanguageDescription API that accounts for version (llvm#162048)
Currently `llvm::dwarf::LanguageDescription` returns a stringified `DW_LNAME`. It would be useful to have an API that returns the language name for a particular `DW_LNAME_`/version pair. LLDB's use case is that it wants to emit diagnostics with human readable descriptions of the language we got from debug-info (see llvm#161688). We could maintain a side-table in LLDB but thought this might be generally useful and should live next to the existing `LanguageDescription` API. (cherry picked from commit 030d8e6)
1 parent 61020d3 commit 2788430

File tree

3 files changed

+191
-0
lines changed

3 files changed

+191
-0
lines changed

llvm/include/llvm/BinaryFormat/Dwarf.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,8 +499,15 @@ toDW_LNAME(SourceLanguage language) {
499499
return {};
500500
}
501501

502+
/// Returns a version-independent language name.
502503
LLVM_ABI llvm::StringRef LanguageDescription(SourceLanguageName name);
503504

505+
/// Returns a language name corresponding to the specified version.
506+
/// If the version is not recognized for the specified language, returns
507+
/// the version-independent name.
508+
LLVM_ABI llvm::StringRef LanguageDescription(SourceLanguageName Name,
509+
uint32_t Version);
510+
504511
inline bool isCPlusPlus(SourceLanguage S) {
505512
bool result = false;
506513
// Deliberately enumerate all the language options so we get a warning when

llvm/lib/BinaryFormat/Dwarf.cpp

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,117 @@ StringRef llvm::dwarf::LanguageDescription(dwarf::SourceLanguageName lname) {
472472
return "Unknown";
473473
}
474474

475+
StringRef llvm::dwarf::LanguageDescription(dwarf::SourceLanguageName Name,
476+
uint32_t Version) {
477+
switch (Name) {
478+
// YYYY
479+
case DW_LNAME_Ada: {
480+
if (Version <= 1983)
481+
return "Ada 83";
482+
if (Version <= 1995)
483+
return "Ada 95";
484+
if (Version <= 2005)
485+
return "Ada 2005";
486+
if (Version <= 2012)
487+
return "Ada 2012";
488+
} break;
489+
490+
case DW_LNAME_Cobol: {
491+
if (Version <= 1974)
492+
return "COBOL-74";
493+
if (Version <= 1985)
494+
return "COBOL-85";
495+
} break;
496+
497+
case DW_LNAME_Fortran: {
498+
if (Version <= 1977)
499+
return "FORTRAN 77";
500+
if (Version <= 1990)
501+
return "FORTRAN 90";
502+
if (Version <= 1995)
503+
return "Fortran 95";
504+
if (Version <= 2003)
505+
return "Fortran 2003";
506+
if (Version <= 2008)
507+
return "Fortran 2008";
508+
if (Version <= 2018)
509+
return "Fortran 2018";
510+
} break;
511+
512+
// YYYYMM
513+
case DW_LNAME_C: {
514+
if (Version == 0)
515+
break;
516+
if (Version <= 198912)
517+
return "C89";
518+
if (Version <= 199901)
519+
return "C99";
520+
if (Version <= 201112)
521+
return "C11";
522+
if (Version <= 201710)
523+
return "C17";
524+
} break;
525+
526+
case DW_LNAME_C_plus_plus: {
527+
if (Version == 0)
528+
break;
529+
if (Version <= 199711)
530+
return "C++98";
531+
if (Version <= 200310)
532+
return "C++03";
533+
if (Version <= 201103)
534+
return "C++11";
535+
if (Version <= 201402)
536+
return "C++14";
537+
if (Version <= 201703)
538+
return "C++17";
539+
if (Version <= 202002)
540+
return "C++20";
541+
} break;
542+
543+
case DW_LNAME_ObjC_plus_plus:
544+
case DW_LNAME_ObjC:
545+
case DW_LNAME_Move:
546+
case DW_LNAME_SYCL:
547+
case DW_LNAME_BLISS:
548+
case DW_LNAME_Crystal:
549+
case DW_LNAME_D:
550+
case DW_LNAME_Dylan:
551+
case DW_LNAME_Go:
552+
case DW_LNAME_Haskell:
553+
case DW_LNAME_HLSL:
554+
case DW_LNAME_Java:
555+
case DW_LNAME_Julia:
556+
case DW_LNAME_Kotlin:
557+
case DW_LNAME_Modula2:
558+
case DW_LNAME_Modula3:
559+
case DW_LNAME_OCaml:
560+
case DW_LNAME_OpenCL_C:
561+
case DW_LNAME_Pascal:
562+
case DW_LNAME_PLI:
563+
case DW_LNAME_Python:
564+
case DW_LNAME_RenderScript:
565+
case DW_LNAME_Rust:
566+
case DW_LNAME_Swift:
567+
case DW_LNAME_UPC:
568+
case DW_LNAME_Zig:
569+
case DW_LNAME_Assembly:
570+
case DW_LNAME_C_sharp:
571+
case DW_LNAME_Mojo:
572+
case DW_LNAME_GLSL:
573+
case DW_LNAME_GLSL_ES:
574+
case DW_LNAME_OpenCL_CPP:
575+
case DW_LNAME_CPP_for_OpenCL:
576+
case DW_LNAME_Ruby:
577+
case DW_LNAME_Hylo:
578+
case DW_LNAME_Metal:
579+
break;
580+
}
581+
582+
// Fallback to un-versioned name.
583+
return LanguageDescription(Name);
584+
}
585+
475586
StringRef llvm::dwarf::CaseString(unsigned Case) {
476587
switch (Case) {
477588
case DW_ID_case_sensitive:

llvm/unittests/BinaryFormat/DwarfTest.cpp

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,4 +219,77 @@ TEST(DwarfTest, lname) {
219219
EXPECT_EQ(roundtrip(DW_LANG_##NAME), DW_LANG_##NAME);
220220
#include "llvm/BinaryFormat/Dwarf.def"
221221
}
222+
223+
TEST(DwarfTest, lname_getSourceLanguageName) {
224+
// Some basics.
225+
EXPECT_EQ(getSourceLanguageName("DW_LNAME_Ada"), DW_LNAME_Ada);
226+
EXPECT_EQ(getSourceLanguageName("DW_LNAME_Metal"), DW_LNAME_Metal);
227+
228+
// Test invalid input.
229+
EXPECT_EQ(getSourceLanguageName(""), 0U);
230+
EXPECT_EQ(getSourceLanguageName("blah"), 0U);
231+
EXPECT_EQ(getSourceLanguageName("DW_LNAME__something_unlikely"), 0U);
232+
EXPECT_EQ(getSourceLanguageName("DW_LANG_C"), 0U);
233+
234+
// Test that we cover all DW_LNAME_ names.
235+
#define xstr(X) #X
236+
#define HANDLE_DW_LNAME(ID, NAME, DESC, LOWER_BOUND) \
237+
EXPECT_EQ(getSourceLanguageName(xstr(DW_LNAME_##NAME)), DW_LNAME_##NAME);
238+
#include "llvm/BinaryFormat/Dwarf.def"
239+
}
240+
241+
TEST(DwarfTest, lname_SourceLanguageNameString) {
242+
// Some basics.
243+
EXPECT_EQ(SourceLanguageNameString(DW_LNAME_C_plus_plus),
244+
"DW_LNAME_C_plus_plus");
245+
EXPECT_EQ(SourceLanguageNameString(DW_LNAME_CPP_for_OpenCL),
246+
"DW_LNAME_CPP_for_OpenCL");
247+
248+
// Test invalid input.
249+
EXPECT_EQ(SourceLanguageNameString(static_cast<SourceLanguageName>(0)), "");
250+
251+
// Test that we cover all DW_LNAME_ names.
252+
#define xstr(X) #X
253+
#define HANDLE_DW_LNAME(ID, NAME, DESC, LOWER_BOUND) \
254+
EXPECT_EQ(SourceLanguageNameString(DW_LNAME_##NAME), xstr(DW_LNAME_##NAME));
255+
#include "llvm/BinaryFormat/Dwarf.def"
256+
}
257+
258+
TEST(DWARFDebugInfo, TestLanguageDescription_Versioned) {
259+
// Tests for the llvm::dwarf::LanguageDescription API that
260+
// takes a name *and* a version.
261+
262+
// Unknown language.
263+
EXPECT_EQ(
264+
llvm::dwarf::LanguageDescription(static_cast<SourceLanguageName>(0)),
265+
"Unknown");
266+
267+
EXPECT_EQ(
268+
llvm::dwarf::LanguageDescription(static_cast<SourceLanguageName>(0), 0),
269+
"Unknown");
270+
271+
// Test that specifying an invalid version falls back to a valid language name
272+
// regardless.
273+
EXPECT_EQ(llvm::dwarf::LanguageDescription(DW_LNAME_ObjC, 0), "Objective C");
274+
EXPECT_EQ(llvm::dwarf::LanguageDescription(DW_LNAME_Julia, 0), "Julia");
275+
276+
// Check some versions.
277+
EXPECT_EQ(llvm::dwarf::LanguageDescription(DW_LNAME_C_plus_plus, 199711),
278+
"C++98");
279+
EXPECT_EQ(llvm::dwarf::LanguageDescription(DW_LNAME_C_plus_plus, 201402),
280+
"C++14");
281+
282+
// Versions round up.
283+
EXPECT_EQ(llvm::dwarf::LanguageDescription(DW_LNAME_C_plus_plus, 201400),
284+
"C++14");
285+
286+
// Version 0 for C and C++ is an unversioned name.
287+
EXPECT_EQ(llvm::dwarf::LanguageDescription(DW_LNAME_C, 0), "C (K&R and ISO)");
288+
EXPECT_EQ(llvm::dwarf::LanguageDescription(DW_LNAME_C_plus_plus, 0),
289+
"ISO C++");
290+
291+
// Version 0 for other versioned languages may not be the unversioned name.
292+
EXPECT_EQ(llvm::dwarf::LanguageDescription(DW_LNAME_Fortran, 0),
293+
"FORTRAN 77");
294+
}
222295
} // end namespace

0 commit comments

Comments
 (0)