[Lldb-commits] [lldb] [llvm] [lldb][RISCV] function calls support in lldb expressions (PR #99336)
via lldb-commits
lldb-commits at lists.llvm.org
Wed Jul 17 08:07:44 PDT 2024
https://github.com/dlav-sc created https://github.com/llvm/llvm-project/pull/99336
To make function calls inside lldb expressions ABI support, JIT engine support are required. This patch augments corresponding functionality to RISCV ABI and implements RISCV relocation resolver in JIT, which allows to make function calls with integer and void function arguments and return value. Also it adds RISCV specific DirectToIndirectFunctionCallsReplacement IR pass, that allows to make assembly jumps at any 64bit address without RISCV large code model, which has not been implemented yet. This pass is needed, because jitted code contains more that +-2GB jumps, which are not available in RISCV without large code model now.
Fixed tests:
```
TestCallThatRestarts.ExprCommandThatRestartsTestCase
TestMainThreadExit.ThreadExitTestCase
TestSBValuePersist.SBValuePersistTestCase
TestThisClassTypeMixing.TestCase
Test11588.Issue11581TestCase
TestExprsChar.ExprCharTestCase
TestExpressions.TestExpressions
TestCallWithTimeout.ExprCommandWithTimeoutsTestCase
TestInlineNamespace.TestInlineNamespace
TestInlineNamespaceAlias.TestInlineNamespace
TestSetValues.SetValuesTestCase
TestValueAPIAddressOfVoidStar.ValueAPIVoidStarTestCase
TestXValuePrinting.ExprXValuePrintingTestCase
lldbsuite.test.lldbtest.TestRedefinitionsInInlines
lldbsuite.test.lldbtest.TestVirtualOverload
TestSetValues.SetValuesTestCase
TestExprs.BasicExprCommandsTestCase
TestExprHelpExamples.Radar9673644TestCase
TestExprEntryBP.ExprEntryBPTestCase
TestExprInsideLambdas.ExprInsideLambdaTestCase
TestConstStaticIntegralMemberInt128.TestCase
TestDWIMPrint.TestCase
TestCPPStaticMembers.TestCase
TestNestedPersistentTypes.NestedPersistentTypesTestCase
TestSaveJITObjects.SaveJITObjectsTestCase
TestNamespaceLookup.NamespaceLookupTestCase
TestCCallingConventions.TestCase
TestCppTypedef.TestCppTypedef
TestUnwindExpression.UnwindFromExpressionTest
TestCxxChar8_t.CxxChar8_tTestCase
TestCallCPPFunction.CallCPPFunctionTestCase
TestExprDoesntBlock.ExprDoesntDeadlockTestCase
TestEnumTypes.EnumTypesTestCase
TestAnonymous.AnonymousTestCase
```
>From c35ab331df2bc76a32d36a51ecb020ea2c13ff39 Mon Sep 17 00:00:00 2001
From: Daniil Avdeev <daniil.avdeev at syntacore.com>
Date: Thu, 11 Jul 2024 11:21:36 +0000
Subject: [PATCH 1/5] [lldb][RISCV] add jitted function calls to ABI
Function calls support in LLDB expressions for RISCV: 1 of 5
Augments corresponding functionality to RISCV ABI, which allows to jit
lldb expressions and thus make function calls inside them. Only function
calls with integer and void function arguments and return value are
supported.
---
.../Plugins/ABI/RISCV/ABISysV_riscv.cpp | 89 +++++++++++++++++--
lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.h | 3 +
2 files changed, 85 insertions(+), 7 deletions(-)
diff --git a/lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.cpp b/lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.cpp
index 6395f5bb5bd9b..f3edee1dd6dc1 100644
--- a/lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.cpp
+++ b/lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.cpp
@@ -10,7 +10,9 @@
#include <array>
#include <limits>
+#include <sstream>
+#include "llvm/ADT/STLExtras.h"
#include "llvm/IR/DerivedTypes.h"
#include "lldb/Core/PluginManager.h"
@@ -19,6 +21,7 @@
#include "lldb/Target/RegisterContext.h"
#include "lldb/Target/StackFrame.h"
#include "lldb/Target/Thread.h"
+#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/RegisterValue.h"
#define DEFINE_REG_NAME(reg_num) ConstString(#reg_num).GetCString()
@@ -163,11 +166,83 @@ TotalArgsSizeInWords(bool is_rv64,
return total_size;
}
+static bool UpdateRegister(RegisterContext *reg_ctx,
+ const lldb::RegisterKind reg_kind,
+ const uint32_t reg_num, const addr_t value) {
+ Log *log = GetLog(LLDBLog::Expressions);
+
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfo(reg_kind, reg_num);
+
+ LLDB_LOG(log, "Writing %s: 0x%" PRIx64, reg_info->name,
+ static_cast<uint64_t>(value));
+ if (!reg_ctx->WriteRegisterFromUnsigned(reg_info, value)) {
+ LLDB_LOG(log, "Writing %s: failed", reg_info->name);
+ return false;
+ }
+ return true;
+}
+
+static void LogInitInfo(Log *log, const Thread &thread, addr_t sp,
+ addr_t func_addr, addr_t return_addr,
+ const llvm::ArrayRef<addr_t> args) {
+ assert(log);
+ std::stringstream ss;
+ ss << "ABISysV_riscv::PrepareTrivialCall"
+ << " (tid = 0x%" << std::hex << thread.GetID() << ", sp = 0x%" << sp
+ << ", func_addr = 0x%" << func_addr << ", return_addr = 0x%"
+ << return_addr;
+
+ for (auto &&[idx, arg] : enumerate(args))
+ ss << ", arg" << std::dec << idx << " = 0x%" << std::hex << arg;
+ ss << ")";
+ log->PutString(ss.str());
+}
+
bool ABISysV_riscv::PrepareTrivialCall(Thread &thread, addr_t sp,
addr_t func_addr, addr_t return_addr,
llvm::ArrayRef<addr_t> args) const {
- // TODO: Implement
- return false;
+ Log *log = GetLog(LLDBLog::Expressions);
+ if (log)
+ LogInitInfo(log, thread, sp, func_addr, return_addr, args);
+
+ const auto reg_ctx_sp = thread.GetRegisterContext();
+ if (!reg_ctx_sp) {
+ LLDB_LOG(log, "Failed to get RegisterContext");
+ return false;
+ }
+
+ if (args.size() > s_regs_for_args_count) {
+ LLDB_LOG(log, "Function has %lu arguments, but only %lu are allowed!",
+ args.size(), s_regs_for_args_count);
+ return false;
+ }
+
+ // Write arguments to registers
+ for (auto &&[idx, arg] : enumerate(args)) {
+ const RegisterInfo *reg_info = reg_ctx_sp->GetRegisterInfo(
+ eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + idx);
+ LLDB_LOG(log, "About to write arg%lu (0x%" PRIx64 ") into %s", idx, arg,
+ reg_info->name);
+
+ if (!reg_ctx_sp->WriteRegisterFromUnsigned(reg_info, arg)) {
+ LLDB_LOG(log, "Failed to write arg%lu (0x%" PRIx64 ") into %s", idx, arg,
+ reg_info->name);
+ return false;
+ }
+ }
+
+ if (!UpdateRegister(reg_ctx_sp.get(), eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_PC, func_addr))
+ return false;
+ if (!UpdateRegister(reg_ctx_sp.get(), eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_SP, sp))
+ return false;
+ if (!UpdateRegister(reg_ctx_sp.get(), eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_RA, return_addr))
+ return false;
+
+ LLDB_LOG(log, "ABISysV_riscv::%s: success", __func__);
+ return true;
}
bool ABISysV_riscv::PrepareTrivialCall(
@@ -221,14 +296,14 @@ bool ABISysV_riscv::PrepareTrivialCall(
assert(prototype.getFunctionNumParams() == args.size());
const size_t num_args = args.size();
- const size_t regs_for_args_count = 8U;
const size_t num_args_in_regs =
- num_args > regs_for_args_count ? regs_for_args_count : num_args;
+ num_args > s_regs_for_args_count ? s_regs_for_args_count : num_args;
// Number of arguments passed on stack.
size_t args_size = TotalArgsSizeInWords(m_is_rv64, args);
- auto on_stack =
- args_size <= regs_for_args_count ? 0 : args_size - regs_for_args_count;
+ auto on_stack = args_size <= s_regs_for_args_count
+ ? 0
+ : args_size - s_regs_for_args_count;
auto offset = on_stack * word_size;
uint8_t reg_value[8];
@@ -259,7 +334,7 @@ bool ABISysV_riscv::PrepareTrivialCall(
++reg_index;
}
- if (reg_index < regs_for_args_count || size == 0)
+ if (reg_index < s_regs_for_args_count || size == 0)
continue;
// Remaining arguments are passed on the stack.
diff --git a/lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.h b/lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.h
index d8cf008dbb0bf..04ec018c8a718 100644
--- a/lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.h
+++ b/lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.h
@@ -124,6 +124,9 @@ class ABISysV_riscv : public lldb_private::RegInfoBasedABI {
using lldb_private::RegInfoBasedABI::RegInfoBasedABI; // Call CreateInstance
// instead.
bool m_is_rv64; // true if target is riscv64; false if target is riscv32
+ static constexpr size_t s_regs_for_args_count =
+ 8U; // number of argument registers (the base integer calling convention
+ // provides 8 argument registers, a0-a7)
};
#endif // liblldb_ABISysV_riscv_h_
>From 7459a88009bd013015bcfd9b84a5e3dc29ca9988 Mon Sep 17 00:00:00 2001
From: Daniil Avdeev <daniil.avdeev at syntacore.com>
Date: Thu, 11 Jul 2024 11:23:39 +0000
Subject: [PATCH 2/5] [lldb][RISCV] add JIT relocations resolver
Function calls support in LLDB expressions for RISCV: 2 of 5
Adds required RISCV relocations resolving functionality in lldb
ExecutionEngine.
---
.../RuntimeDyld/RuntimeDyldELF.cpp | 155 +++++++++++++++++-
.../RuntimeDyld/RuntimeDyldELF.h | 7 +
2 files changed, 159 insertions(+), 3 deletions(-)
diff --git a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp
index 736d9a3e056f1..a43edd41f5e88 100644
--- a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp
+++ b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp
@@ -1011,6 +1011,135 @@ void RuntimeDyldELF::resolveBPFRelocation(const SectionEntry &Section,
}
}
+static void applyUTypeImmRISCV(uint8_t *InstrAddr, uint32_t Imm) {
+ uint32_t UpperImm = (Imm + 0x800) & 0xfffff000;
+ auto Instr = support::ulittle32_t::ref(InstrAddr);
+ Instr = (Instr & 0xfff) | UpperImm;
+}
+
+static void applyITypeImmRISCV(uint8_t *InstrAddr, uint32_t Imm) {
+ uint32_t LowerImm = Imm & 0xfff;
+ auto Instr = support::ulittle32_t::ref(InstrAddr);
+ Instr = (Instr & 0xfffff) | (LowerImm << 20);
+}
+
+void RuntimeDyldELF::resolveRISCVRelocation(const SectionEntry &Section,
+ uint64_t Offset, uint64_t Value,
+ uint32_t Type, int64_t Addend,
+ SID SectionID) {
+ switch (Type) {
+ default: {
+ std::string Err = "Unimplemented reloc type: " + std::to_string(Type);
+ llvm::report_fatal_error(Err.c_str());
+ }
+ // 32-bit PC-relative function call, macros call, tail (PIC)
+ // Write first 20 bits of 32 bit value to the auipc instruction
+ // Last 12 bits to the jalr instruction
+ case ELF::R_RISCV_CALL:
+ case ELF::R_RISCV_CALL_PLT: {
+ uint64_t P = Section.getLoadAddressWithOffset(Offset);
+ uint64_t PCOffset = Value + Addend - P;
+ applyUTypeImmRISCV(Section.getAddressWithOffset(Offset), PCOffset);
+ applyITypeImmRISCV(Section.getAddressWithOffset(Offset + 4), PCOffset);
+ break;
+ }
+ // High 20 bits of 32-bit absolute address, %hi(symbol)
+ case ELF::R_RISCV_HI20: {
+ uint64_t PCOffset = Value + Addend;
+ applyUTypeImmRISCV(Section.getAddressWithOffset(Offset), PCOffset);
+ break;
+ }
+ // Low 12 bits of 32-bit absolute address, %lo(symbol)
+ case ELF::R_RISCV_LO12_I: {
+ uint64_t PCOffset = Value + Addend;
+ applyITypeImmRISCV(Section.getAddressWithOffset(Offset), PCOffset);
+ break;
+ }
+ // High 20 bits of 32-bit PC-relative reference, %pcrel_hi(symbol)
+ case ELF::R_RISCV_GOT_HI20:
+ case ELF::R_RISCV_PCREL_HI20: {
+ uint64_t P = Section.getLoadAddressWithOffset(Offset);
+ uint64_t PCOffset = Value + Addend - P;
+ applyUTypeImmRISCV(Section.getAddressWithOffset(Offset), PCOffset);
+ break;
+ }
+
+ // label:
+ // auipc a0, %pcrel_hi(symbol) // R_RISCV_PCREL_HI20
+ // addi a0, a0, %pcrel_lo(label) // R_RISCV_PCREL_LO12_I
+ //
+ // The low 12 bits of relative address between pc and symbol.
+ // The symbol is related to the high part instruction which is marked by
+ // label.
+ case ELF::R_RISCV_PCREL_LO12_I: {
+ for (auto &&PendingReloc : PendingRelocs) {
+ const RelocationValueRef &MatchingValue = PendingReloc.first;
+ RelocationEntry &Reloc = PendingReloc.second;
+ uint64_t HIRelocPC =
+ getSectionLoadAddress(Reloc.SectionID) + Reloc.Offset;
+ if (Value + Addend == HIRelocPC) {
+ uint64_t Symbol = getSectionLoadAddress(MatchingValue.SectionID) +
+ MatchingValue.Addend;
+ auto PCOffset = Symbol - HIRelocPC;
+ applyITypeImmRISCV(Section.getAddressWithOffset(Offset), PCOffset);
+ return;
+ }
+ }
+
+ llvm::report_fatal_error(
+ "R_RISCV_PCREL_LO12_I without matching R_RISCV_PCREL_HI20");
+ }
+ case ELF::R_RISCV_32_PCREL: {
+ uint64_t FinalAddress = Section.getLoadAddressWithOffset(Offset);
+ int64_t RealOffset = Value + Addend - FinalAddress;
+ int32_t TruncOffset = Lo_32(RealOffset);
+ support::ulittle32_t::ref(Section.getAddressWithOffset(Offset)) =
+ TruncOffset;
+ break;
+ }
+ case ELF::R_RISCV_32: {
+ auto Ref = support::ulittle32_t::ref(Section.getAddressWithOffset(Offset));
+ Ref = Value + Addend;
+ break;
+ }
+ case ELF::R_RISCV_64: {
+ auto Ref = support::ulittle64_t::ref(Section.getAddressWithOffset(Offset));
+ Ref = Value + Addend;
+ break;
+ }
+ case ELF::R_RISCV_ADD16: {
+ auto Ref = support::ulittle16_t::ref(Section.getAddressWithOffset(Offset));
+ Ref = Ref + Value + Addend;
+ break;
+ }
+ case ELF::R_RISCV_ADD32: {
+ auto Ref = support::ulittle32_t::ref(Section.getAddressWithOffset(Offset));
+ Ref = Ref + Value + Addend;
+ break;
+ }
+ case ELF::R_RISCV_ADD64: {
+ auto Ref = support::ulittle64_t::ref(Section.getAddressWithOffset(Offset));
+ Ref = Ref + Value + Addend;
+ break;
+ }
+ case ELF::R_RISCV_SUB16: {
+ auto Ref = support::ulittle16_t::ref(Section.getAddressWithOffset(Offset));
+ Ref = Ref - Value - Addend;
+ break;
+ }
+ case ELF::R_RISCV_SUB32: {
+ auto Ref = support::ulittle32_t::ref(Section.getAddressWithOffset(Offset));
+ Ref = Ref - Value - Addend;
+ break;
+ }
+ case ELF::R_RISCV_SUB64: {
+ auto Ref = support::ulittle64_t::ref(Section.getAddressWithOffset(Offset));
+ Ref = Ref - Value - Addend;
+ break;
+ }
+ }
+}
+
// The target location for the relocation is described by RE.SectionID and
// RE.Offset. RE.SectionID can be used to find the SectionEntry. Each
// SectionEntry has three members describing its location.
@@ -1076,12 +1205,17 @@ void RuntimeDyldELF::resolveRelocation(const SectionEntry &Section,
case Triple::bpfeb:
resolveBPFRelocation(Section, Offset, Value, Type, Addend);
break;
+ case Triple::riscv32: // Fall through.
+ case Triple::riscv64:
+ resolveRISCVRelocation(Section, Offset, Value, Type, Addend, SectionID);
+ break;
default:
llvm_unreachable("Unsupported CPU type!");
}
}
-void *RuntimeDyldELF::computePlaceholderAddress(unsigned SectionID, uint64_t Offset) const {
+void *RuntimeDyldELF::computePlaceholderAddress(unsigned SectionID,
+ uint64_t Offset) const {
return (void *)(Sections[SectionID].getObjAddress() + Offset);
}
@@ -1870,7 +2004,8 @@ RuntimeDyldELF::processRelocationRef(
Value.Addend += support::ulittle32_t::ref(computePlaceholderAddress(SectionID, Offset));
processSimpleRelocation(SectionID, Offset, RelType, Value);
} else if (RelType == ELF::R_X86_64_PC64) {
- Value.Addend += support::ulittle64_t::ref(computePlaceholderAddress(SectionID, Offset));
+ Value.Addend += support::ulittle64_t::ref(
+ computePlaceholderAddress(SectionID, Offset));
processSimpleRelocation(SectionID, Offset, RelType, Value);
} else if (RelType == ELF::R_X86_64_GOTTPOFF) {
processX86_64GOTTPOFFRelocation(SectionID, Offset, Value, Addend);
@@ -1884,9 +2019,23 @@ RuntimeDyldELF::processRelocationRef(
} else {
processSimpleRelocation(SectionID, Offset, RelType, Value);
}
+ } else if (Arch == Triple::riscv32 || Arch == Triple::riscv64) {
+ // *_LO12 relocation receive information about a symbol from the
+ // corresponding *_HI20 relocation, so we have to collect this information
+ // before resolving
+ if (RelType == ELF::R_RISCV_GOT_HI20 ||
+ RelType == ELF::R_RISCV_PCREL_HI20 ||
+ RelType == ELF::R_RISCV_TPREL_HI20 ||
+ RelType == ELF::R_RISCV_TLS_GD_HI20 ||
+ RelType == ELF::R_RISCV_TLS_GOT_HI20) {
+ RelocationEntry RE(SectionID, Offset, RelType, Addend);
+ PendingRelocs.push_back({Value, RE});
+ }
+ processSimpleRelocation(SectionID, Offset, RelType, Value);
} else {
if (Arch == Triple::x86) {
- Value.Addend += support::ulittle32_t::ref(computePlaceholderAddress(SectionID, Offset));
+ Value.Addend += support::ulittle32_t::ref(
+ computePlaceholderAddress(SectionID, Offset));
}
processSimpleRelocation(SectionID, Offset, RelType, Value);
}
diff --git a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h
index b73d2af8c0c49..087338ead639b 100644
--- a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h
+++ b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h
@@ -60,6 +60,10 @@ class RuntimeDyldELF : public RuntimeDyldImpl {
void resolveBPFRelocation(const SectionEntry &Section, uint64_t Offset,
uint64_t Value, uint32_t Type, int64_t Addend);
+ void resolveRISCVRelocation(const SectionEntry &Section, uint64_t Offset,
+ uint64_t Value, uint32_t Type, int64_t Addend,
+ SID SectionID);
+
unsigned getMaxStubSize() const override {
if (Arch == Triple::aarch64 || Arch == Triple::aarch64_be)
return 20; // movz; movk; movk; movk; br
@@ -148,6 +152,9 @@ class RuntimeDyldELF : public RuntimeDyldImpl {
// *HI16 relocations will be added for resolving when we find matching
// *LO16 part. (Mips specific)
+ //
+ // *HI20 relocations will be added for resolving when we find matching
+ // *LO12 part. (RISC-V specific)
SmallVector<std::pair<RelocationValueRef, RelocationEntry>, 8> PendingRelocs;
// When a module is loaded we save the SectionID of the EH frame section
>From 7074ef5cfcb6b5fafad555940871ecebd272257d Mon Sep 17 00:00:00 2001
From: Daniil Avdeev <daniil.avdeev at syntacore.com>
Date: Thu, 11 Jul 2024 11:25:22 +0000
Subject: [PATCH 3/5] [lldb][RISCV] add DirectToIndirectFCR pass
Function calls support in LLDB expressions for RISCV: 3 of 5
Adds RISCV specific DirectToIndirectFunctionCallsReplacement IR pass,
that allows to make assembly jumps at any 64bit address without RISCV
large code model, which has not been implemented yet. This pass is
needed, because jitted code contains more that +-2GB jumps, which are
not available in RISCV without large code model now.
---
.../RISCV/DirectToIndirectFCR.cpp | 194 ++++++++++++++++++
.../Architecture/RISCV/DirectToIndirectFCR.h | 58 ++++++
2 files changed, 252 insertions(+)
create mode 100644 lldb/source/Plugins/Architecture/RISCV/DirectToIndirectFCR.cpp
create mode 100644 lldb/source/Plugins/Architecture/RISCV/DirectToIndirectFCR.h
diff --git a/lldb/source/Plugins/Architecture/RISCV/DirectToIndirectFCR.cpp b/lldb/source/Plugins/Architecture/RISCV/DirectToIndirectFCR.cpp
new file mode 100644
index 0000000000000..71d3001fc06e8
--- /dev/null
+++ b/lldb/source/Plugins/Architecture/RISCV/DirectToIndirectFCR.cpp
@@ -0,0 +1,194 @@
+//===--- DirectToIndirectFCR.cpp - RISC-V specific pass -------------------===//
+//
+// 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 "llvm/IR/Constants.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/InstIterator.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Value.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Transforms/Utils/BasicBlockUtils.h"
+
+#include "Plugins/Architecture/RISCV/DirectToIndirectFCR.h"
+
+#include "lldb/Core/Architecture.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/Symtab.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+
+#include <optional>
+
+using namespace llvm;
+using namespace lldb_private;
+
+namespace {
+std::string GetValueTypeStr(const llvm::Type *value_ty) {
+ assert(value_ty);
+ std::string str_type;
+ llvm::raw_string_ostream rso(str_type);
+ value_ty->print(rso);
+ return rso.str();
+}
+
+template <typename... Args> void LogMessage(const char *msg, Args &&...args) {
+ Log *log = GetLog(LLDBLog::Expressions);
+ LLDB_LOG(log, msg, std::forward<Args>(args)...);
+}
+} // namespace
+
+bool DirectToIndirectFCR::canBeReplaced(const llvm::CallInst *ci) {
+ assert(ci);
+ auto *return_value_ty = ci->getType();
+ if (!(return_value_ty->isIntegerTy() || return_value_ty->isVoidTy())) {
+ LogMessage("DirectToIndirectFCR: function {0} has unsupported "
+ "return type ({1})\n",
+ ci->getCalledFunction()->getName(),
+ GetValueTypeStr(return_value_ty));
+ return false;
+ }
+
+ const auto *arg = llvm::find_if_not(ci->args(), [](const auto &arg) {
+ const auto *type = arg->getType();
+ return type->isIntegerTy();
+ });
+
+ if (arg != ci->arg_end()) {
+ LogMessage("DirectToIndirectFCR: argument {0} of {1} function "
+ "has unsupported type ({2})\n",
+ (*arg)->getName(), ci->getCalledFunction()->getName(),
+ GetValueTypeStr((*arg)->getType()));
+ return false;
+ }
+ return true;
+}
+
+std::vector<llvm::Value *>
+DirectToIndirectFCR::getFunctionArgsAsValues(const llvm::CallInst *ci) {
+ assert(ci);
+ std::vector<llvm::Value *> args{};
+ llvm::transform(ci->args(), std::back_inserter(args),
+ [](const auto &arg) { return arg.get(); });
+ return args;
+}
+
+std::optional<lldb::addr_t>
+DirectToIndirectFCR::getFunctionAddress(const llvm::CallInst *ci) const {
+ auto *target = m_exe_ctx.GetTargetPtr();
+ const auto &lldb_module_sp = target->GetExecutableModule();
+ const auto &symtab = lldb_module_sp->GetSymtab();
+ const llvm::StringRef name = ci->getCalledFunction()->getName();
+
+ // eSymbolTypeCode: we try to find function
+ // eDebugNo: not a debug symbol
+ // eVisibilityExtern: function from extern module
+ const auto *symbol = symtab->FindFirstSymbolWithNameAndType(
+ ConstString(name), lldb::SymbolType::eSymbolTypeCode,
+ Symtab::Debug::eDebugNo, Symtab::Visibility::eVisibilityExtern);
+ if (!symbol) {
+ LogMessage("DirectToIndirectFCR: can't find {0} in symtab\n", name);
+ return std::nullopt;
+ }
+
+ lldb::addr_t addr = symbol->GetLoadAddress(target);
+ LogMessage("DirectToIndirectFCR: found address ({0}) of symbol {1}\n", addr,
+ name);
+ return addr;
+}
+
+llvm::CallInst *DirectToIndirectFCR::getInstReplace(llvm::CallInst *ci) const {
+ assert(ci);
+
+ std::optional<lldb::addr_t> addr_or_null = getFunctionAddress(ci);
+ if (!addr_or_null.has_value())
+ return nullptr;
+
+ lldb::addr_t addr = addr_or_null.value();
+
+ llvm::IRBuilder<> builder(ci);
+
+ std::vector<llvm::Value *> args = getFunctionArgsAsValues(ci);
+ llvm::Constant *func_addr = builder.getInt64(addr);
+ llvm::PointerType *ptr_func_ty = builder.getPtrTy();
+ auto *cast = builder.CreateIntToPtr(func_addr, ptr_func_ty);
+ auto *new_inst =
+ builder.CreateCall(ci->getFunctionType(), cast, ArrayRef(args));
+ return new_inst;
+}
+
+DirectToIndirectFCR::DirectToIndirectFCR(const ExecutionContext &exe_ctx)
+ : FunctionPass(ID), m_exe_ctx{exe_ctx} {}
+
+DirectToIndirectFCR::~DirectToIndirectFCR() = default;
+
+bool DirectToIndirectFCR::runOnFunction(llvm::Function &func) {
+ bool has_irreplaceable =
+ llvm::any_of(instructions(func), [this](llvm::Instruction &inst) {
+ llvm::CallInst *ci = dyn_cast<llvm::CallInst>(&inst);
+ if (!ci || ci->getCalledFunction()->isIntrinsic() ||
+ (DirectToIndirectFCR::canBeReplaced(ci) &&
+ getFunctionAddress(ci).has_value()))
+ return false;
+ return true;
+ });
+
+ if (has_irreplaceable) {
+ func.getParent()->getOrInsertNamedMetadata(
+ Architecture::s_target_incompatibility_marker);
+ return false;
+ }
+
+ std::vector<std::reference_wrapper<llvm::Instruction>>
+ replaceable_function_calls{};
+ llvm::copy_if(instructions(func),
+ std::back_inserter(replaceable_function_calls),
+ [](llvm::Instruction &inst) {
+ llvm::CallInst *ci = dyn_cast<llvm::CallInst>(&inst);
+ if (ci && !ci->getCalledFunction()->isIntrinsic())
+ return true;
+ return false;
+ });
+
+ if (replaceable_function_calls.empty())
+ return false;
+
+ std::vector<std::pair<llvm::CallInst *, llvm::CallInst *>> replaces;
+ llvm::transform(replaceable_function_calls, std::back_inserter(replaces),
+ [this](std::reference_wrapper<llvm::Instruction> inst)
+ -> std::pair<llvm::CallInst *, llvm::CallInst *> {
+ llvm::CallInst *ci = cast<llvm::CallInst>(&(inst.get()));
+ llvm::CallInst *new_inst = getInstReplace(ci);
+ return {ci, new_inst};
+ });
+
+ for (auto &&[from, to] : replaces) {
+ from->replaceAllUsesWith(to);
+ from->eraseFromParent();
+ }
+
+ return true;
+}
+
+llvm::StringRef DirectToIndirectFCR::getPassName() const {
+ return "Transform function calls to calls by address";
+}
+
+char DirectToIndirectFCR::ID = 0;
+
+llvm::FunctionPass *
+lldb_private::createDirectToIndirectFCR(const ExecutionContext &exe_ctx) {
+ return new DirectToIndirectFCR(exe_ctx);
+}
diff --git a/lldb/source/Plugins/Architecture/RISCV/DirectToIndirectFCR.h b/lldb/source/Plugins/Architecture/RISCV/DirectToIndirectFCR.h
new file mode 100644
index 0000000000000..5ac5820bd95a0
--- /dev/null
+++ b/lldb/source/Plugins/Architecture/RISCV/DirectToIndirectFCR.h
@@ -0,0 +1,58 @@
+//===--- DirectToIndirectFCR.h - RISC-V specific pass ---------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#pragma once
+
+#include "lldb/lldb-types.h"
+
+#include "llvm/IR/Instructions.h"
+#include "llvm/Pass.h"
+
+namespace lldb_private {
+
+class ExecutionContext;
+
+// During the lldb expression execution lldb wraps a user expression, jittes
+// fabricated code and then puts it into the stack memory. Thus, if user tried
+// to make a function call there will be a jump from a stack address to a code
+// sections's address. RISC-V Architecture doesn't have a large code model yet
+// and can make only a +-2GiB jumps, but in 64-bit architecture a distance
+// between stack addresses and code sections's addresses is longer. Therefore,
+// relocations resolver obtains an invalid address. To avoid such problem, this
+// pass should be used. It replaces function calls with appropriate function's
+// addresses explicitly. By doing so it removes relocations related to function
+// calls. This pass should be cosidered as temprorary solution until a large
+// code model will be approved.
+class DirectToIndirectFCR : public llvm::FunctionPass {
+
+ static bool canBeReplaced(const llvm::CallInst *ci);
+
+ static std::vector<llvm::Value *>
+ getFunctionArgsAsValues(const llvm::CallInst *ci);
+
+ std::optional<lldb::addr_t>
+ getFunctionAddress(const llvm::CallInst *ci) const;
+
+ llvm::CallInst *getInstReplace(llvm::CallInst *ci) const;
+
+public:
+ static char ID;
+
+ DirectToIndirectFCR(const ExecutionContext &exe_ctx);
+ ~DirectToIndirectFCR() override;
+
+ bool runOnFunction(llvm::Function &func) override;
+
+ llvm::StringRef getPassName() const override;
+
+private:
+ const ExecutionContext &m_exe_ctx;
+};
+
+llvm::FunctionPass *createDirectToIndirectFCR(const ExecutionContext &exe_ctx);
+} // namespace lldb_private
>From c4db74e6926bc54cf56a4f9c0710e2948f680ada Mon Sep 17 00:00:00 2001
From: Daniil Avdeev <daniil.avdeev at syntacore.com>
Date: Thu, 11 Jul 2024 11:27:53 +0000
Subject: [PATCH 4/5] [lldb] add architecture depended IR passes
Function calls support in LLDB expressions for RISCV: 4 of 5
Adds architecture depended IR passes handler, that can apply architecture
specific IR passes before IRForTarget.
---
lldb/include/lldb/Core/Architecture.h | 12 ++++
.../Plugins/Architecture/CMakeLists.txt | 1 +
.../Architecture/RISCV/ArchitectureRISCV.cpp | 55 +++++++++++++++++++
.../Architecture/RISCV/ArchitectureRISCV.h | 34 ++++++++++++
.../Plugins/Architecture/RISCV/CMakeLists.txt | 12 ++++
.../Clang/ClangExpressionParser.cpp | 23 ++++++++
6 files changed, 137 insertions(+)
create mode 100644 lldb/source/Plugins/Architecture/RISCV/ArchitectureRISCV.cpp
create mode 100644 lldb/source/Plugins/Architecture/RISCV/ArchitectureRISCV.h
create mode 100644 lldb/source/Plugins/Architecture/RISCV/CMakeLists.txt
diff --git a/lldb/include/lldb/Core/Architecture.h b/lldb/include/lldb/Core/Architecture.h
index b6fc1a20e1e69..ca6a9207d8012 100644
--- a/lldb/include/lldb/Core/Architecture.h
+++ b/lldb/include/lldb/Core/Architecture.h
@@ -12,6 +12,7 @@
#include "lldb/Core/PluginInterface.h"
#include "lldb/Target/DynamicRegisterInfo.h"
#include "lldb/Target/MemoryTagManager.h"
+#include "llvm/IR/LegacyPassManager.h"
namespace lldb_private {
@@ -129,6 +130,17 @@ class Architecture : public PluginInterface {
RegisterContext ®_context) const {
return false;
}
+
+ // Takes a Pass Manager and adds passes for this Architecture that should be
+ // run before IRForTarget
+ virtual std::unique_ptr<llvm::legacy::PassManager>
+ GetArchitectureCustomPasses(const ExecutionContext &exe_ctx,
+ const llvm::StringRef expr) const {
+ return nullptr;
+ }
+
+ static constexpr llvm::StringLiteral s_target_incompatibility_marker =
+ "target_incompatibility_detected";
};
} // namespace lldb_private
diff --git a/lldb/source/Plugins/Architecture/CMakeLists.txt b/lldb/source/Plugins/Architecture/CMakeLists.txt
index 9ed8edf70af3f..78cdaa0bdf2d4 100644
--- a/lldb/source/Plugins/Architecture/CMakeLists.txt
+++ b/lldb/source/Plugins/Architecture/CMakeLists.txt
@@ -2,3 +2,4 @@ add_subdirectory(Arm)
add_subdirectory(Mips)
add_subdirectory(PPC64)
add_subdirectory(AArch64)
+add_subdirectory(RISCV)
diff --git a/lldb/source/Plugins/Architecture/RISCV/ArchitectureRISCV.cpp b/lldb/source/Plugins/Architecture/RISCV/ArchitectureRISCV.cpp
new file mode 100644
index 0000000000000..e4608b41bd787
--- /dev/null
+++ b/lldb/source/Plugins/Architecture/RISCV/ArchitectureRISCV.cpp
@@ -0,0 +1,55 @@
+//===-- ArchitectureRISCV.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 "Plugins/Architecture/RISCV/ArchitectureRISCV.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/ArchSpec.h"
+
+#include "llvm/IR/LegacyPassManager.h"
+
+#include "DirectToIndirectFCR.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+LLDB_PLUGIN_DEFINE(ArchitectureRISCV)
+
+void ArchitectureRISCV::Initialize() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ "RISCV-specific algorithms",
+ &ArchitectureRISCV::Create);
+}
+
+void ArchitectureRISCV::Terminate() {
+ PluginManager::UnregisterPlugin(&ArchitectureRISCV::Create);
+}
+
+std::unique_ptr<Architecture> ArchitectureRISCV::Create(const ArchSpec &arch) {
+ if (!arch.GetTriple().isRISCV())
+ return nullptr;
+ return std::unique_ptr<Architecture>(new ArchitectureRISCV());
+}
+
+void ArchitectureRISCV::OverrideStopInfo(Thread &thread) const {}
+
+std::unique_ptr<llvm::legacy::PassManager>
+ArchitectureRISCV::GetArchitectureCustomPasses(
+ const ExecutionContext &exe_ctx, const llvm::StringRef expr) const {
+ // LLDB generates additional support functions like
+ // '_$__lldb_valid_pointer_check', that do not require custom passes
+ if (expr != "$__lldb_expr")
+ return nullptr;
+
+ std::unique_ptr<llvm::legacy::PassManager> custom_passes =
+ std::make_unique<llvm::legacy::PassManager>();
+ auto *P = createDirectToIndirectFCR(exe_ctx);
+ custom_passes->add(P);
+ return custom_passes;
+}
diff --git a/lldb/source/Plugins/Architecture/RISCV/ArchitectureRISCV.h b/lldb/source/Plugins/Architecture/RISCV/ArchitectureRISCV.h
new file mode 100644
index 0000000000000..6ef6c62de5f27
--- /dev/null
+++ b/lldb/source/Plugins/Architecture/RISCV/ArchitectureRISCV.h
@@ -0,0 +1,34 @@
+//===-- ArchitectureRISCV.h -------------------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#pragma once
+
+#include "lldb/Core/Architecture.h"
+
+namespace lldb_private {
+
+class ArchitectureRISCV : public Architecture {
+public:
+ static llvm::StringRef GetPluginNameStatic() { return "riscv"; }
+ static void Initialize();
+ static void Terminate();
+
+ llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
+
+ void OverrideStopInfo(Thread &thread) const override;
+
+ std::unique_ptr<llvm::legacy::PassManager>
+ GetArchitectureCustomPasses(const ExecutionContext &exe_ctx,
+ const llvm::StringRef expr) const override;
+
+private:
+ static std::unique_ptr<Architecture> Create(const ArchSpec &arch);
+ ArchitectureRISCV() = default;
+};
+
+} // namespace lldb_private
diff --git a/lldb/source/Plugins/Architecture/RISCV/CMakeLists.txt b/lldb/source/Plugins/Architecture/RISCV/CMakeLists.txt
new file mode 100644
index 0000000000000..f2545eb35b000
--- /dev/null
+++ b/lldb/source/Plugins/Architecture/RISCV/CMakeLists.txt
@@ -0,0 +1,12 @@
+add_lldb_library(lldbPluginArchitectureRISCV PLUGIN
+ ArchitectureRISCV.cpp
+ DirectToIndirectFCR.cpp
+
+ LINK_LIBS
+ lldbPluginProcessUtility
+ lldbCore
+ lldbTarget
+ lldbUtility
+ LINK_COMPONENTS
+ Support
+ )
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
index 303e88feea20b..2825beef257d3 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
@@ -41,8 +41,10 @@
#include "llvm/Support/Debug.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/TargetSelect.h"
+#include "llvm/TargetParser/Triple.h"
#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/DynamicLibrary.h"
#include "llvm/Support/ErrorHandling.h"
@@ -1362,6 +1364,27 @@ lldb_private::Status ClangExpressionParser::DoPrepareForExecution(
custom_passes.EarlyPasses->run(*llvm_module_up);
}
+ std::unique_ptr<llvm::legacy::PassManager> arch_passes(nullptr);
+ if (lldb::TargetSP target_sp = exe_ctx.GetTargetSP()) {
+ Architecture *arch = target_sp->GetArchitecturePlugin();
+ if (arch) {
+ arch_passes =
+ arch->GetArchitectureCustomPasses(exe_ctx, m_expr.FunctionName());
+ }
+ }
+
+ if (arch_passes)
+ arch_passes->run(*llvm_module_up);
+
+ if (llvm_module_up->getNamedMetadata(
+ Architecture::s_target_incompatibility_marker)) {
+ err.SetErrorToGenericError();
+ err.SetErrorStringWithFormat(
+ "%s - Architecture passes failure on function %s\n. Function "
+ "contains unsupported function calls",
+ __FUNCTION__, m_expr.FunctionName());
+ }
+
execution_unit_sp = std::make_shared<IRExecutionUnit>(
m_llvm_context, // handed off here
llvm_module_up, // handed off here
>From 3ad75f024eec22f2dfd0ae7cc911f6d70202932d Mon Sep 17 00:00:00 2001
From: Daniil Avdeev <daniil.avdeev at syntacore.com>
Date: Thu, 11 Jul 2024 13:12:34 +0000
Subject: [PATCH 5/5] [lldb][RISCV] add function calls tests
Function calls support in LLDB expressions for RISCV: 5 of 5
Adds tests on function calls inside lldb expressions
---
lldb/test/API/riscv/expressions/Makefile | 3 +
.../API/riscv/expressions/TestExpressions.py | 88 +++++++++++++++++++
lldb/test/API/riscv/expressions/main.cpp | 51 +++++++++++
3 files changed, 142 insertions(+)
create mode 100644 lldb/test/API/riscv/expressions/Makefile
create mode 100644 lldb/test/API/riscv/expressions/TestExpressions.py
create mode 100644 lldb/test/API/riscv/expressions/main.cpp
diff --git a/lldb/test/API/riscv/expressions/Makefile b/lldb/test/API/riscv/expressions/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/riscv/expressions/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/riscv/expressions/TestExpressions.py b/lldb/test/API/riscv/expressions/TestExpressions.py
new file mode 100644
index 0000000000000..15faa7c741c7c
--- /dev/null
+++ b/lldb/test/API/riscv/expressions/TestExpressions.py
@@ -0,0 +1,88 @@
+"""
+Test RISC-V expressions evaluation.
+"""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class TestExpressions(TestBase):
+
+ def common_setup(self):
+ self.build()
+ lldbutil.run_to_source_breakpoint(
+ self, "// break here", lldb.SBFileSpec("main.cpp")
+ )
+
+ @skipIf(archs=no_match(["rv64gc"]))
+ def test_int_arg(self):
+ self.common_setup()
+ self.expect_expr("foo(foo(5), foo())", result_type="int", result_value="8")
+
+ @skipIf(archs=no_match(["rv64gc"]))
+ def test_double_arg(self):
+ self.common_setup()
+ self.expect(
+ "expr func_with_double_arg(1, 6.5)",
+ error=True,
+ substrs=["Architecture passes failure on function $__lldb_expr"],
+ )
+
+ @skipIf(archs=no_match(["rv64gc"]))
+ def test_ptr_arg(self):
+ self.common_setup()
+ self.expect(
+ "expr func_with_ptr_arg(\"bla\")",
+ error=True,
+ substrs=["Architecture passes failure on function $__lldb_expr"],
+ )
+
+ @skipIf(archs=no_match(["rv64gc"]))
+ def test_struct_arg(self):
+ self.common_setup()
+ self.expect_expr("func_with_struct_arg(s)", result_type="int", result_value="3")
+
+ @skipIf(archs=no_match(["rv64gc"]))
+ def test_unsupported_struct_arg(self):
+ self.common_setup()
+ self.expect(
+ "expr func_with_unsupported_struct_arg(u)",
+ error=True,
+ substrs=["Architecture passes failure on function $__lldb_expr"],
+ )
+
+ @skipIf(archs=no_match(["rv64gc"]))
+ def test_double_ret_val(self):
+ self.common_setup()
+
+ self.expect(
+ "expr func_with_double_return()",
+ error=True,
+ substrs=["Architecture passes failure on function $__lldb_expr"],
+ )
+
+ @skipIf(archs=no_match(["rv64gc"]))
+ def test_ptr_ret_val(self):
+ self.common_setup()
+ self.expect(
+ "expr func_with_ptr_return()",
+ error=True,
+ substrs=["Architecture passes failure on function $__lldb_expr"],
+ )
+
+ @skipIf(archs=no_match(["rv64gc"]))
+ def test_struct_return(self):
+ self.common_setup()
+ self.expect_expr("func_with_struct_return()", result_type="S")
+
+ @skipIf(archs=no_match(["rv64gc"]))
+ def test_ptr_ret_val(self):
+ self.common_setup()
+ self.expect(
+ "expr func_with_unsupported_struct_return()",
+ error=True,
+ substrs=["Architecture passes failure on function $__lldb_expr"],
+ )
+
diff --git a/lldb/test/API/riscv/expressions/main.cpp b/lldb/test/API/riscv/expressions/main.cpp
new file mode 100644
index 0000000000000..ffbe95a66f10a
--- /dev/null
+++ b/lldb/test/API/riscv/expressions/main.cpp
@@ -0,0 +1,51 @@
+struct S {
+ int a;
+ int b;
+};
+
+struct U {
+ int a;
+ double d;
+};
+
+int g;
+
+int func_with_double_arg(int a, double b) { return 1; }
+
+int func_with_ptr_arg(char *msg) { return 2; }
+
+int func_with_struct_arg(struct S s) { return 3; }
+
+int func_with_unsupported_struct_arg(struct U u) { return 4; }
+
+double func_with_double_return() { return 42.0; }
+
+int *func_with_ptr_return() { return &g; }
+
+struct S func_with_struct_return() {
+ struct S s = {3, 4};
+ return s;
+}
+
+struct U func_with_unsupported_struct_return() {
+ struct U u = {3, 42.0};
+ return u;
+}
+
+int foo() { return 3; }
+
+int foo(int a) { return a; }
+
+int foo(int a, int b) { return a + b; }
+
+int main() {
+ struct S s = {1, 2};
+ struct U u = {1, 1.0};
+ double d = func_with_double_arg(1, 1.0) + func_with_ptr_arg("msg") +
+ func_with_struct_arg(s) + func_with_double_return() +
+ func_with_unsupported_struct_arg(u) + foo() + foo(1) + foo(1, 2);
+ int *ptr = func_with_ptr_return();
+ s = func_with_struct_return();
+ u = func_with_unsupported_struct_return();
+ return 0; // break here
+}
More information about the lldb-commits
mailing list