llvm-project/lldb/unittests/Expression/ExpressionTest.cpp
Michael Buch f89059140b
[lldb][Expression] Encode Module and DIE UIDs into function AsmLabels (#148877)
LLDB currently attaches `AsmLabel`s to `FunctionDecl`s such that that
the `IRExecutionUnit` can determine which mangled name to call (we can't
rely on Clang deriving the correct mangled name to call because the
debug-info AST doesn't contain all the info that would be encoded in the
DWARF linkage names). However, we don't attach `AsmLabel`s for structors
because they have multiple variants and thus it's not clear which
mangled name to use. In the [RFC on fixing expression evaluation of
abi-tagged
structors](https://discourse.llvm.org/t/rfc-lldb-handling-abi-tagged-constructors-destructors-in-expression-evaluator/82816)
we discussed encoding the structor variant into the `AsmLabel`s.
Specifically in [this
thread](https://discourse.llvm.org/t/rfc-lldb-handling-abi-tagged-constructors-destructors-in-expression-evaluator/82816/7)
we discussed that the contents of the `AsmLabel` are completely under
LLDB's control and we could make use of it to uniquely identify a
function by encoding the exact module and DIE that the function is
associated with (mangled names need not be enough since two identical
mangled symbols may live in different modules). So if we already have a
custom `AsmLabel` format, we can encode the structor variant in a
follow-up (the current idea is to append the structor variant as a
suffix to our custom `AsmLabel` when Clang emits the mangled name into
the JITted IR). Then we would just have to teach the `IRExecutionUnit`
to pick the correct structor variant DIE during symbol resolution. The
draft of this is available
[here](https://github.com/llvm/llvm-project/pull/149827)

This patch sets up the infrastructure for the custom `AsmLabel` format
by encoding the module id, DIE id and mangled name in it.

**Implementation**

The flow is as follows:
1. Create the label in `DWARFASTParserClang`. The format is:
`$__lldb_func:module_id:die_id:mangled_name`
2. When resolving external symbols in `IRExecutionUnit`, we parse this
label and then do a lookup by DIE ID (or mangled name into the module if
the encoded DIE is a declaration).

Depends on https://github.com/llvm/llvm-project/pull/151355
2025-08-01 07:21:41 +01:00

123 lines
3.9 KiB
C++

//===-- ExpressionTest.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 "gmock/gmock.h"
#include "gtest/gtest.h"
#include "TestingSupport/TestUtilities.h"
#include "lldb/Expression/Expression.h"
#include "llvm/Testing/Support/Error.h"
using namespace lldb_private;
struct LabelTestCase {
llvm::StringRef encoded;
FunctionCallLabel label;
llvm::SmallVector<llvm::StringRef> error_pattern;
};
static LabelTestCase g_label_test_cases[] = {
// Failure modes
{"bar:0x0:0x0:_Z3foov",
{},
{"expected function call label prefix '$__lldb_func' but found 'bar' "
"instead."}},
{"$__lldb_func :0x0:0x0:_Z3foov",
{},
{"expected function call label prefix '$__lldb_func' but found "
"'$__lldb_func ' instead."}},
{"$__lldb_funcc:0x0:0x0:_Z3foov",
{},
{"expected function call label prefix '$__lldb_func' but found "
"'$__lldb_funcc' instead."}},
{"", {}, {"malformed function call label."}},
{"foo", {}, {"malformed function call label."}},
{"$__lldb_func", {}, {"malformed function call label."}},
{"$__lldb_func:", {}, {"malformed function call label."}},
{"$__lldb_func:0x0:0x0", {}, {"malformed function call label."}},
{"$__lldb_func:abc:0x0:_Z3foov",
{},
{"failed to parse module ID from 'abc'."}},
{"$__lldb_func:-1:0x0:_Z3foov",
{},
{"failed to parse module ID from '-1'."}},
{"$__lldb_func:0x0invalid:0x0:_Z3foov",
{},
{"failed to parse module ID from '0x0invalid'."}},
{"$__lldb_func:0x0 :0x0:_Z3foov",
{},
{"failed to parse module ID from '0x0 '."}},
{"$__lldb_func:0x0:abc:_Z3foov",
{},
{"failed to parse symbol ID from 'abc'."}},
{"$__lldb_func:0x5:-1:_Z3foov",
{},
{"failed to parse symbol ID from '-1'."}},
{"$__lldb_func:0x5:0x0invalid:_Z3foov",
{},
{"failed to parse symbol ID from '0x0invalid'."}},
{"$__lldb_func:0x5:0x0 :_Z3foov",
{},
{"failed to parse symbol ID from '0x0 '."}},
{"$__lldb_func:0x0:0x0:_Z3foov",
{
/*.module_id=*/0x0,
/*.symbol_id=*/0x0,
/*.lookup_name=*/"_Z3foov",
},
{}},
{"$__lldb_func:0x0:0x0:abc:def:::a",
{
/*.module_id=*/0x0,
/*.symbol_id=*/0x0,
/*.lookup_name=*/"abc:def:::a",
},
{}},
{"$__lldb_func:0xd2:0xf0:$__lldb_func",
{
/*.module_id=*/0xd2,
/*.symbol_id=*/0xf0,
/*.lookup_name=*/"$__lldb_func",
},
{}},
};
struct ExpressionTestFixture : public testing::TestWithParam<LabelTestCase> {};
TEST_P(ExpressionTestFixture, FunctionCallLabel) {
const auto &[encoded, label, errors] = GetParam();
auto decoded_or_err = FunctionCallLabel::fromString(encoded);
if (!errors.empty()) {
EXPECT_THAT_EXPECTED(
decoded_or_err,
llvm::FailedWithMessageArray(testing::ElementsAreArray(errors)));
return;
}
EXPECT_THAT_EXPECTED(decoded_or_err, llvm::Succeeded());
auto label_str = label.toString();
EXPECT_EQ(decoded_or_err->toString(), encoded);
EXPECT_EQ(label_str, encoded);
EXPECT_EQ(decoded_or_err->module_id, label.module_id);
EXPECT_EQ(decoded_or_err->symbol_id, label.symbol_id);
EXPECT_EQ(decoded_or_err->lookup_name, label.lookup_name);
auto roundtrip_or_err = FunctionCallLabel::fromString(label_str);
EXPECT_THAT_EXPECTED(roundtrip_or_err, llvm::Succeeded());
EXPECT_EQ(roundtrip_or_err->module_id, label.module_id);
EXPECT_EQ(roundtrip_or_err->symbol_id, label.symbol_id);
EXPECT_EQ(roundtrip_or_err->lookup_name, label.lookup_name);
}
INSTANTIATE_TEST_SUITE_P(FunctionCallLabelTest, ExpressionTestFixture,
testing::ValuesIn(g_label_test_cases));