mirror of
https://github.com/intel/llvm.git
synced 2026-01-14 03:50:17 +08:00
Depends on * https://github.com/llvm/llvm-project/pull/148877 * https://github.com/llvm/llvm-project/pull/155483 * https://github.com/llvm/llvm-project/pull/155485 * https://github.com/llvm/llvm-project/pull/154137 * https://github.com/llvm/llvm-project/pull/154142 This patch is an implementation of [this discussion](https://discourse.llvm.org/t/rfc-lldb-handling-abi-tagged-constructors-destructors-in-expression-evaluator/82816/7) about handling ABI-tagged structors during expression evaluation. **Motivation** LLDB encodes the mangled name of a `DW_TAG_subprogram` into `AsmLabel`s on function and method Clang AST nodes. This means that when calls to these functions get lowered into IR (when running JITted expressions), the address resolver can locate the appropriate symbol by mangled name (and it is guaranteed to find the symbol because we got the mangled name from debug-info, instead of letting Clang mangle it based on AST structure). However, we don't do this for `CXXConstructorDecl`s/`CXXDestructorDecl`s because these structor declarations in DWARF don't have a linkage name. This is because there can be multiple variants of a structor, each with a distinct mangling in the Itanium ABI. Each structor variant has its own definition `DW_TAG_subprogram`. So LLDB doesn't know which mangled name to put into the `AsmLabel`. Currently this means using ABI-tagged structors in LLDB expressions won't work (see [this RFC](https://discourse.llvm.org/t/rfc-lldb-handling-abi-tagged-constructors-destructors-in-expression-evaluator/82816) for concrete examples). **Proposed Solution** The `FunctionCallLabel` encoding that we put into `AsmLabel`s already supports stuffing more info about a DIE into it. So this patch extends the `FunctionCallLabel` to contain an optional discriminator (a sequence of bytes) which the `SymbolFileDWARF` plugin interprets as the constructor/destructor variant of that DIE. So when searching for the definition DIE, LLDB will include the structor variant in its heuristic for determining a match. There's a few subtleties here: 1. At the point at which LLDB first constructs the label, it has no way of knowing (just by looking at the debug-info declaration), which structor variant the expression evaluator is supposed to call. That's something that gets decided when compiling the expression. So we let the Clang mangler inject the correct structor variant into the `AsmLabel` during JITing. I adjusted the `AsmLabelAttr` mangling for this in https://github.com/llvm/llvm-project/pull/155485. An option would've been to create a new Clang attribute which behaved like an `AsmLabel` but with these special semantics for LLDB. My main concern there is that we'd have to adjust all the `AsmLabelAttr` checks around Clang to also now account for this new attribute. 2. The compiler is free to omit the `C1` variant of a constructor if the `C2` variant is sufficient. In that case it may alias `C1` to `C2`, leaving us with only the `C2` `DW_TAG_subprogram` in the object file. Linux is one of the platforms where this occurs. For those cases I added a heuristic in `SymbolFileDWARF` where we pick `C2` if we asked for `C1` but it doesn't exist. This may not always be correct (e.g., if the compiler decided to drop `C1` for other reasons). 3. In https://github.com/llvm/llvm-project/pull/154142 Clang will emit `C4`/`D4` variants of ctors/dtors on declarations. When resolving the `FunctionCallLabel` we will now substitute the actual variant that Clang told us we need to call into the mangled name. We do this using LLDB's `ManglingSubstitutor`. That way we find the definition DIE exactly the same way we do for regular function calls. 4. In cases where declarations and definitions live in separate modules, the DIE ID encoded in the function call label may not be enough to find the definition DIE in the encoded module ID. For those cases we fall back to how LLDB used to work: look up in all images of the target. To make sure we don't use the unified mangled name for the fallback lookup, we change the lookup name to whatever mangled name the FunctionCallLabel resolved to. rdar://104968288
83 lines
3.1 KiB
C++
83 lines
3.1 KiB
C++
//===-- Expression.cpp ----------------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "lldb/Expression/Expression.h"
|
|
#include "lldb/Target/ExecutionContextScope.h"
|
|
#include "lldb/Target/Target.h"
|
|
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/Support/Error.h"
|
|
|
|
using namespace lldb_private;
|
|
|
|
Expression::Expression(Target &target)
|
|
: m_target_wp(target.shared_from_this()),
|
|
m_jit_start_addr(LLDB_INVALID_ADDRESS),
|
|
m_jit_end_addr(LLDB_INVALID_ADDRESS) {
|
|
// Can't make any kind of expression without a target.
|
|
assert(m_target_wp.lock());
|
|
}
|
|
|
|
Expression::Expression(ExecutionContextScope &exe_scope)
|
|
: m_target_wp(exe_scope.CalculateTarget()),
|
|
m_jit_start_addr(LLDB_INVALID_ADDRESS),
|
|
m_jit_end_addr(LLDB_INVALID_ADDRESS) {
|
|
assert(m_target_wp.lock());
|
|
}
|
|
|
|
llvm::Expected<FunctionCallLabel>
|
|
lldb_private::FunctionCallLabel::fromString(llvm::StringRef label) {
|
|
llvm::SmallVector<llvm::StringRef, 5> components;
|
|
label.split(components, ":", /*MaxSplit=*/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 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))
|
|
return llvm::createStringError(
|
|
llvm::formatv("failed to parse module ID from '{0}'.", module_label));
|
|
|
|
lldb::user_id_t die_id;
|
|
if (!llvm::to_integer(die_label, die_id))
|
|
return llvm::createStringError(
|
|
llvm::formatv("failed to parse symbol ID from '{0}'.", die_label));
|
|
|
|
return FunctionCallLabel{/*.discriminator=*/discriminator,
|
|
/*.module_id=*/module_id,
|
|
/*.symbol_id=*/die_id,
|
|
/*.lookup_name=*/lookup_name};
|
|
}
|
|
|
|
std::string lldb_private::FunctionCallLabel::toString() const {
|
|
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{ discriminator: {0}, module_id: "
|
|
"{1:x}, symbol_id: {2:x}, "
|
|
"lookup_name: {3} }",
|
|
label.discriminator, label.module_id, label.symbol_id,
|
|
label.lookup_name);
|
|
}
|