Skip to content

[lldb][Expression] Add structor variant to LLDB's function call labels #149827

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions clang/include/clang/Basic/ABI.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,16 @@ enum CXXCtorType {
Ctor_Comdat, ///< The COMDAT used for ctors
Ctor_CopyingClosure, ///< Copying closure variant of a ctor
Ctor_DefaultClosure, ///< Default closure variant of a ctor
Ctor_Unified, ///< GCC-style unified ctor.
};

/// C++ destructor types.
enum CXXDtorType {
Dtor_Deleting, ///< Deleting dtor
Dtor_Complete, ///< Complete object dtor
Dtor_Base, ///< Base object dtor
Dtor_Comdat ///< The COMDAT used for dtors
Dtor_Deleting, ///< Deleting dtor
Dtor_Complete, ///< Complete object dtor
Dtor_Base, ///< Base object dtor
Dtor_Comdat, ///< The COMDAT used for dtors
Dtor_Unified, ///< GCC-style unified dtor.
};

} // end namespace clang
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/AST/ItaniumMangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6056,6 +6056,9 @@ void CXXNameMangler::mangleCXXCtorType(CXXCtorType T,
case Ctor_Base:
Out << '2';
break;
case Ctor_Unified:
Out << '4';
break;
case Ctor_Comdat:
Out << '5';
break;
Expand Down Expand Up @@ -6083,6 +6086,9 @@ void CXXNameMangler::mangleCXXDtorType(CXXDtorType T) {
case Dtor_Base:
Out << "D2";
break;
case Dtor_Unified:
Out << "D4";
break;
case Dtor_Comdat:
Out << "D5";
break;
Expand Down
33 changes: 32 additions & 1 deletion clang/lib/AST/Mangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,33 @@ bool MangleContext::shouldMangleDeclName(const NamedDecl *D) {
return shouldMangleCXXName(D);
}

static llvm::StringRef g_lldb_func_call_label_prefix = "$__lldb_func:";

/// Given an LLDB function call label, this function prints the label
/// into \c Out, together with the structor type of \c GD (if the
/// decl is a constructor/destructor). LLDB knows how to handle mangled
/// names with this encoding.
///
/// Example input label:
/// $__lldb_func::123:456:~Foo
///
/// Example output:
/// $__lldb_func:D1:123:456:~Foo
///
static void emitLLDBAsmLabel(llvm::StringRef label, GlobalDecl GD,
llvm::raw_ostream &Out) {
assert(label.starts_with(g_lldb_func_call_label_prefix));

Out << g_lldb_func_call_label_prefix;

if (llvm::isa<clang::CXXConstructorDecl>(GD.getDecl()))
Out << "C" << GD.getCtorType();
else if (llvm::isa<clang::CXXDestructorDecl>(GD.getDecl()))
Out << "D" << GD.getDtorType();

Out << label.substr(g_lldb_func_call_label_prefix.size());
}

void MangleContext::mangleName(GlobalDecl GD, raw_ostream &Out) {
const ASTContext &ASTContext = getASTContext();
const NamedDecl *D = cast<NamedDecl>(GD.getDecl());
Expand Down Expand Up @@ -185,7 +212,11 @@ void MangleContext::mangleName(GlobalDecl GD, raw_ostream &Out) {
if (!UserLabelPrefix.empty())
Out << '\01'; // LLVM IR Marker for __asm("foo")

Out << ALA->getLabel();
if (ALA->getLabel().starts_with(g_lldb_func_call_label_prefix))
emitLLDBAsmLabel(ALA->getLabel(), GD, Out);
else
Out << ALA->getLabel();

return;
}

Expand Down
2 changes: 2 additions & 0 deletions clang/lib/AST/MicrosoftMangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1494,6 +1494,8 @@ void MicrosoftCXXNameMangler::mangleCXXDtorType(CXXDtorType T) {
// <operator-name> ::= ?_E # vector deleting destructor
// FIXME: Add a vector deleting dtor type. It goes in the vtable, so we need
// it.
case Dtor_Unified:
llvm_unreachable("not expecting a unified dtor");
case Dtor_Comdat:
llvm_unreachable("not expecting a COMDAT");
}
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/CodeGen/CGClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1493,6 +1493,8 @@ void CodeGenFunction::EmitDestructorBody(FunctionArgList &Args) {
// we'd introduce *two* handler blocks. In the Microsoft ABI, we
// always delegate because we might not have a definition in this TU.
switch (DtorType) {
case Dtor_Unified:
llvm_unreachable("not expecting a unified dtor");
case Dtor_Comdat: llvm_unreachable("not expecting a COMDAT");
case Dtor_Deleting: llvm_unreachable("already handled deleting case");

Expand Down
22 changes: 15 additions & 7 deletions clang/lib/CodeGen/CGDebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include "clang/AST/RecordLayout.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/VTableBuilder.h"
#include "clang/Basic/ABI.h"
#include "clang/Basic/CodeGenOptions.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/Version.h"
Expand Down Expand Up @@ -2177,22 +2178,29 @@ static bool isFunctionLocalClass(const CXXRecordDecl *RD) {

llvm::DISubprogram *CGDebugInfo::CreateCXXMemberFunction(
const CXXMethodDecl *Method, llvm::DIFile *Unit, llvm::DIType *RecordTy) {
bool IsCtorOrDtor =
isa<CXXConstructorDecl>(Method) || isa<CXXDestructorDecl>(Method);

StringRef MethodName = getFunctionName(Method);
llvm::DISubroutineType *MethodTy = getOrCreateMethodType(Method, Unit);

// Since a single ctor/dtor corresponds to multiple functions, it doesn't
// make sense to give a single ctor/dtor a linkage name.
const bool ShouldAddLinkageName =
(!isa<CXXConstructorDecl>(Method) && !isa<CXXDestructorDecl>(Method)) ||
CGM.getTarget().getCXXABI().isItaniumFamily();

StringRef MethodLinkageName;
// FIXME: 'isFunctionLocalClass' seems like an arbitrary/unintentional
// property to use here. It may've been intended to model "is non-external
// type" but misses cases of non-function-local but non-external classes such
// as those in anonymous namespaces as well as the reverse - external types
// that are function local, such as those in (non-local) inline functions.
if (!IsCtorOrDtor && !isFunctionLocalClass(Method->getParent()))
MethodLinkageName = CGM.getMangledName(Method);
if (ShouldAddLinkageName && !isFunctionLocalClass(Method->getParent())) {
if (auto *Ctor = dyn_cast<CXXConstructorDecl>(Method))
MethodLinkageName =
CGM.getMangledName(GlobalDecl(Ctor, CXXCtorType::Ctor_Unified));
else if (auto *Dtor = dyn_cast<CXXDestructorDecl>(Method))
MethodLinkageName =
CGM.getMangledName(GlobalDecl(Dtor, CXXDtorType::Dtor_Unified));
else
MethodLinkageName = CGM.getMangledName(Method);
}

// Get the location for the method.
llvm::DIFile *MethodDefUnit = nullptr;
Expand Down
4 changes: 3 additions & 1 deletion clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "clang/AST/Mangle.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/Basic/ABI.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/CodeGenOptions.h"
#include "clang/Basic/Diagnostic.h"
Expand Down Expand Up @@ -2150,7 +2151,8 @@ StringRef CodeGenModule::getMangledName(GlobalDecl GD) {
if (const auto *CD = dyn_cast<CXXConstructorDecl>(CanonicalGD.getDecl())) {
if (!getTarget().getCXXABI().hasConstructorVariants()) {
CXXCtorType OrigCtorType = GD.getCtorType();
assert(OrigCtorType == Ctor_Base || OrigCtorType == Ctor_Complete);
assert(OrigCtorType == Ctor_Base || OrigCtorType == Ctor_Complete ||
OrigCtorType == Ctor_Unified);
if (OrigCtorType == Ctor_Base)
CanonicalGD = GlobalDecl(CD, Ctor_Complete);
}
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/CodeGen/ItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI {
case Dtor_Base:
return false;

case Dtor_Unified:
llvm_unreachable("unexpected unified dtor");

case Dtor_Comdat:
llvm_unreachable("emitting dtor comdat as function?");
}
Expand All @@ -102,6 +105,9 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI {
case Ctor_Base:
return false;

case Ctor_Unified:
llvm_unreachable("unexpected unified ctor");

case Ctor_CopyingClosure:
case Ctor_DefaultClosure:
llvm_unreachable("closure ctors in Itanium ABI?");
Expand Down
75 changes: 75 additions & 0 deletions clang/unittests/AST/DeclTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "clang/AST/Mangle.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Basic/ABI.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/TargetInfo.h"
Expand Down Expand Up @@ -102,6 +103,80 @@ TEST(Decl, AsmLabelAttr) {
"foo");
}

TEST(Decl, AsmLabelAttr_LLDB) {
StringRef Code = R"(
struct S {
void f() {}
S() = default;
~S() = default;
};
)";
auto AST =
tooling::buildASTFromCodeWithArgs(Code, {"-target", "i386-apple-darwin"});
ASTContext &Ctx = AST->getASTContext();
assert(Ctx.getTargetInfo().getUserLabelPrefix() == StringRef("_") &&
"Expected target to have a global prefix");
DiagnosticsEngine &Diags = AST->getDiagnostics();

const auto *DeclS =
selectFirst<CXXRecordDecl>("d", match(cxxRecordDecl().bind("d"), Ctx));

auto *DeclF = *DeclS->method_begin();
auto *Ctor = *DeclS->ctor_begin();
auto *Dtor = DeclS->getDestructor();

ASSERT_TRUE(DeclF);
ASSERT_TRUE(Ctor);
ASSERT_TRUE(Dtor);

DeclF->addAttr(AsmLabelAttr::Create(Ctx, "$__lldb_func::123:123:_Z1fv"));
Ctor->addAttr(AsmLabelAttr::Create(Ctx, "$__lldb_func::123:123:S"));
Dtor->addAttr(AsmLabelAttr::Create(Ctx, "$__lldb_func::123:123:~S"));

std::unique_ptr<ItaniumMangleContext> MC(
ItaniumMangleContext::create(Ctx, Diags));

{
std::string Mangled;
llvm::raw_string_ostream OS_Mangled(Mangled);
MC->mangleName(DeclF, OS_Mangled);

ASSERT_EQ(Mangled, "\x01$__lldb_func::123:123:_Z1fv");
};

{
std::string Mangled;
llvm::raw_string_ostream OS_Mangled(Mangled);
MC->mangleName(GlobalDecl(Ctor, CXXCtorType::Ctor_Complete), OS_Mangled);

ASSERT_EQ(Mangled, "\x01$__lldb_func:C0:123:123:S");
};

{
std::string Mangled;
llvm::raw_string_ostream OS_Mangled(Mangled);
MC->mangleName(GlobalDecl(Ctor, CXXCtorType::Ctor_Base), OS_Mangled);

ASSERT_EQ(Mangled, "\x01$__lldb_func:C1:123:123:S");
};

{
std::string Mangled;
llvm::raw_string_ostream OS_Mangled(Mangled);
MC->mangleName(GlobalDecl(Dtor, CXXDtorType::Dtor_Deleting), OS_Mangled);

ASSERT_EQ(Mangled, "\x01$__lldb_func:D0:123:123:~S");
};

{
std::string Mangled;
llvm::raw_string_ostream OS_Mangled(Mangled);
MC->mangleName(GlobalDecl(Dtor, CXXDtorType::Dtor_Base), OS_Mangled);

ASSERT_EQ(Mangled, "\x01$__lldb_func:D2:123:123:~S");
};
}

TEST(Decl, MangleDependentSizedArray) {
StringRef Code = R"(
template <int ...N>
Expand Down
2 changes: 2 additions & 0 deletions libcxxabi/src/demangle/ItaniumDemangle.h
Original file line number Diff line number Diff line change
Expand Up @@ -1766,6 +1766,8 @@ class CtorDtorName final : public Node {

template<typename Fn> void match(Fn F) const { F(Basename, IsDtor, Variant); }

int getVariant() const { return Variant; }

void printLeft(OutputBuffer &OB) const override {
if (IsDtor)
OB += "~";
Expand Down
8 changes: 6 additions & 2 deletions lldb/include/lldb/Expression/Expression.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,15 @@ class Expression {
///
/// The format being:
///
/// <prefix>:<module uid>:<symbol uid>:<name>
/// <prefix>:<discriminator>:<module uid>:<symbol uid>:<name>
///
/// The label string needs to stay valid for the entire lifetime
/// of this object.
struct FunctionCallLabel {
/// Arbitrary string which language plugins can interpret for their
/// own needs.
llvm::StringRef discriminator;

/// Unique identifier of the lldb_private::Module
/// which contains the symbol identified by \c symbol_id.
lldb::user_id_t module_id;
Expand All @@ -133,7 +137,7 @@ struct FunctionCallLabel {
///
/// The representation roundtrips through \c fromString:
/// \code{.cpp}
/// llvm::StringRef encoded = "$__lldb_func:0x0:0x0:_Z3foov";
/// llvm::StringRef encoded = "$__lldb_func:blah:0x0:0x0:_Z3foov";
/// FunctionCallLabel label = *fromString(label);
///
/// assert (label.toString() == encoded);
Expand Down
4 changes: 2 additions & 2 deletions lldb/include/lldb/Symbol/SymbolFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -332,12 +332,12 @@ class SymbolFile : public PluginInterface {
/// Resolves the function corresponding to the specified LLDB function
/// call \c label.
///
/// \param[in] label The FunctionCallLabel to be resolved.
/// \param[in,out] label The FunctionCallLabel to be resolved.
///
/// \returns An llvm::Error if the specified \c label couldn't be resolved.
/// Returns the resolved function (as a SymbolContext) otherwise.
virtual llvm::Expected<SymbolContext>
ResolveFunctionCallLabel(const FunctionCallLabel &label) {
ResolveFunctionCallLabel(FunctionCallLabel &label) {
return llvm::createStringError("Not implemented");
}

Expand Down
29 changes: 17 additions & 12 deletions lldb/source/Expression/Expression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,21 @@ Expression::Expression(ExecutionContextScope &exe_scope)

llvm::Expected<FunctionCallLabel>
lldb_private::FunctionCallLabel::fromString(llvm::StringRef label) {
llvm::SmallVector<llvm::StringRef, 4> components;
label.split(components, ":", /*MaxSplit=*/3);
llvm::SmallVector<llvm::StringRef, 5> components;
label.split(components, ":", /*MaxSplit=*/4);

if (components.size() != 4)
if (components.size() != 5)
return llvm::createStringError("malformed function call label.");

if (components[0] != FunctionCallLabelPrefix)
return llvm::createStringError(llvm::formatv(
"expected function call label prefix '{0}' but found '{1}' instead.",
FunctionCallLabelPrefix, components[0]));

llvm::StringRef module_label = components[1];
llvm::StringRef die_label = components[2];
llvm::StringRef discriminator = components[1];
llvm::StringRef module_label = components[2];
llvm::StringRef die_label = components[3];
llvm::StringRef lookup_name = components[4];

lldb::user_id_t module_id = 0;
if (!llvm::to_integer(module_label, module_id))
Expand All @@ -58,20 +60,23 @@ lldb_private::FunctionCallLabel::fromString(llvm::StringRef label) {
return llvm::createStringError(
llvm::formatv("failed to parse symbol ID from '{0}'.", die_label));

return FunctionCallLabel{/*.module_id=*/module_id,
return FunctionCallLabel{/*.discriminator=*/discriminator,
/*.module_id=*/module_id,
/*.symbol_id=*/die_id,
/*.lookup_name=*/components[3]};
/*.lookup_name=*/lookup_name};
}

std::string lldb_private::FunctionCallLabel::toString() const {
return llvm::formatv("{0}:{1:x}:{2:x}:{3}", FunctionCallLabelPrefix,
module_id, symbol_id, lookup_name)
return llvm::formatv("{0}:{1}:{2:x}:{3:x}:{4}", FunctionCallLabelPrefix,
discriminator, module_id, symbol_id, lookup_name)
.str();
}

void llvm::format_provider<FunctionCallLabel>::format(
const FunctionCallLabel &label, raw_ostream &OS, StringRef Style) {
OS << llvm::formatv("FunctionCallLabel{ module_id: {0:x}, symbol_id: {1:x}, "
"lookup_name: {2} }",
label.module_id, label.symbol_id, label.lookup_name);
OS << llvm::formatv("FunctionCallLabel{ discriminator: {0}, module_id: "
"{1:x}, symbol_id: {2:x}, "
"lookup_name: {3} }",
label.discriminator, label.module_id, label.symbol_id,
label.lookup_name);
}
2 changes: 1 addition & 1 deletion lldb/source/Expression/IRExecutionUnit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -778,7 +778,7 @@ class LoadAddressResolver {
/// Returns address of the function referred to by the special function call
/// label \c label.
static llvm::Expected<lldb::addr_t>
ResolveFunctionCallLabel(const FunctionCallLabel &label,
ResolveFunctionCallLabel(FunctionCallLabel &label,
const lldb_private::SymbolContext &sc,
bool &symbol_was_missing_weak) {
symbol_was_missing_weak = false;
Expand Down
Loading
Loading