[Lldb-commits] [lldb] [llvm] [lldb][RISCV] function calls support in lldb expressions (PR #99336)
via lldb-commits
lldb-commits at lists.llvm.org
Mon Sep 2 01:49:42 PDT 2024
https://github.com/dlav-sc updated https://github.com/llvm/llvm-project/pull/99336
>From fb4e062d33c105591d8fb46d72dd64181880bdcd 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/6] [lldb][RISCV] add jitted function calls to ABI
Function calls support in LLDB expressions for RISCV: 1 of 6
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 de304183f67175..6e7ab20c83bfe1 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 "Utility/RISCV_DWARF_Registers.h"
@@ -20,6 +22,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()
@@ -164,11 +167,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(
@@ -222,14 +297,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];
@@ -260,7 +335,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 d8cf008dbb0bf8..04ec018c8a7180 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 fb8a9d09acb15277ea78698615cdd774e8eb3d24 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/6] [lldb][RISCV] add JIT relocations resolver
Function calls support in LLDB expressions for RISCV: 2 of 6
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 736d9a3e056f1e..a43edd41f5e889 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 1b90013dfe2df9..97517884654bc5 100644
--- a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h
+++ b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h
@@ -58,6 +58,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
@@ -146,6 +150,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 589d0713f186f1a83d7ecd5088aaba5c25c40c90 Mon Sep 17 00:00:00 2001
From: Daniil Avdeev <daniil.avdeev at syntacore.com>
Date: Fri, 2 Aug 2024 09:41:27 +0000
Subject: [PATCH 3/6] [lldb][RISCV] PIC for RISCV64 MCJIT
Function calls support in LLDB expressions for RISCV: 3 of 6
This patch allows to access .rodata section from lldb expressions (like
string literals).
---
lldb/source/Expression/IRExecutionUnit.cpp | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/lldb/source/Expression/IRExecutionUnit.cpp b/lldb/source/Expression/IRExecutionUnit.cpp
index d2f2ee26fd4307..f32186279cf89c 100644
--- a/lldb/source/Expression/IRExecutionUnit.cpp
+++ b/lldb/source/Expression/IRExecutionUnit.cpp
@@ -274,8 +274,9 @@ void IRExecutionUnit::GetRunnableInfo(Status &error, lldb::addr_t &func_addr,
builder.setEngineKind(llvm::EngineKind::JIT)
.setErrorStr(&error_string)
- .setRelocationModel(triple.isOSBinFormatMachO() ? llvm::Reloc::PIC_
- : llvm::Reloc::Static)
+ .setRelocationModel((triple.isOSBinFormatMachO() || triple.isRISCV64())
+ ? llvm::Reloc::PIC_
+ : llvm::Reloc::Static)
.setMCJITMemoryManager(std::make_unique<MemoryManager>(*this))
.setOptLevel(llvm::CodeGenOptLevel::Less);
>From dabd3e3cf7b2cf96028df20aed01df200f248301 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 4/6] [lldb][RISCV] add DirectToIndirectFCR pass
Function calls support in LLDB expressions for RISCV: 4 of 6
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 | 210 ++++++++++++++++++
.../Architecture/RISCV/DirectToIndirectFCR.h | 58 +++++
2 files changed, 268 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 00000000000000..d5e9b781c9a038
--- /dev/null
+++ b/lldb/source/Plugins/Architecture/RISCV/DirectToIndirectFCR.cpp
@@ -0,0 +1,210 @@
+//===--- 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)...);
+}
+
+std::string getFunctionName(const llvm::CallInst *ci) {
+ return ci->isIndirectCall() ? "indirect"
+ : ci->getCalledFunction()->getName().str();
+}
+} // 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() ||
+ return_value_ty->isPointerTy())) {
+ LogMessage("DirectToIndirectFCR: function {0} has unsupported "
+ "return type ({1})\n",
+ getFunctionName(ci), 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() || type->isPointerTy();
+ });
+
+ if (arg != ci->arg_end()) {
+ LogMessage("DirectToIndirectFCR: argument {0} of {1} function "
+ "has unsupported type ({2})\n",
+ (*arg)->getName(), getFunctionName(ci),
+ 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)
+ return false;
+
+ // The function signature does not match the call signature.
+ if (!ci->isIndirectCall() && !ci->getCalledFunction())
+ return true;
+
+ if (!ci->isIndirectCall() && ci->getCalledFunction()->isIntrinsic())
+ return false;
+
+ if (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->isIndirectCall() &&
+ !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 00000000000000..5ac5820bd95a07
--- /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 00bce99986b70034a414ea8e6e51ab950c08860a 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 5/6] [lldb] add architecture depended IR passes
Function calls support in LLDB expressions for RISCV: 5 of 6
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 b6fc1a20e1e696..ca6a9207d80125 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 9ed8edf70af3fd..78cdaa0bdf2d48 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 00000000000000..e4608b41bd787f
--- /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 00000000000000..6ef6c62de5f274
--- /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 00000000000000..f2545eb35b0003
--- /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 90f26de939a478..61e85dd73b4a93 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
@@ -43,8 +43,10 @@
#include "llvm/Support/Error.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"
@@ -1441,6 +1443,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 da97b8d1647eb6515e75e31003905d47d407b3e6 Mon Sep 17 00:00:00 2001
From: Daniil Avdeev <daniil.avdeev at syntacore.com>
Date: Fri, 30 Aug 2024 13:27:25 +0300
Subject: [PATCH 6/6] [lldb][RISCV] add function calls tests
Function calls support in LLDB expressions for RISCV: 6 of 6
Adds tests on function calls inside lldb expressions
---
lldb/test/API/riscv/expressions/Makefile | 3 +
.../API/riscv/expressions/TestExpressions.py | 94 +++++++++++++++++++
lldb/test/API/riscv/expressions/main.cpp | 54 +++++++++++
3 files changed, 151 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 00000000000000..99998b20bcb050
--- /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 00000000000000..32d007a926d2ac
--- /dev/null
+++ b/lldb/test/API/riscv/expressions/TestExpressions.py
@@ -0,0 +1,94 @@
+"""
+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(\"message\")", result_type="int", result_value="2")
+
+ @skipIf(archs=no_match(["rv64gc"]))
+ def test_ptr_ret_val(self):
+ self.common_setup()
+ self.expect("expr func_with_ptr_return()", substrs=["global"])
+
+ @skipIf(archs=no_match(["rv64gc"]))
+ def test_ptr(self):
+ self.common_setup()
+ self.expect(
+ "expr func_with_ptr(\"message\")",
+ substrs=["message"],
+ )
+
+ @skipIf(archs=no_match(["rv64gc"]))
+ def test_global_ptr(self):
+ self.common_setup()
+ self.expect(
+ "expr func_with_ptr(g_str)",
+ substrs=["global"],
+ )
+
+ @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_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 00000000000000..bdf9e28ef3d566
--- /dev/null
+++ b/lldb/test/API/riscv/expressions/main.cpp
@@ -0,0 +1,54 @@
+struct S {
+ int a;
+ int b;
+};
+
+struct U {
+ int a;
+ double d;
+};
+
+int g;
+
+char *g_str = "global";
+
+int func_with_double_arg(int a, double b) { return 1; }
+
+int func_with_ptr_arg(char *msg) { return 2; }
+char *func_with_ptr_return() { return g_str; }
+char *func_with_ptr(char *msg) { return msg; }
+
+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; }
+
+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_struct_arg(s) +
+ func_with_ptr_arg("msg") + func_with_double_return() +
+ func_with_unsupported_struct_arg(u) + foo() + foo(1) + foo(1, 2);
+ char *msg = func_with_ptr("msg");
+ char *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