Index: source/API/SystemInitializerFull.cpp =================================================================== --- source/API/SystemInitializerFull.cpp +++ source/API/SystemInitializerFull.cpp @@ -29,6 +29,7 @@ #include "Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.h" #include "Plugins/ABI/MacOSX-arm64/ABIMacOSX_arm64.h" #include "Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.h" +#include "Plugins/ABI/SysV-arc/ABISysV_arc.h" #include "Plugins/ABI/SysV-arm/ABISysV_arm.h" #include "Plugins/ABI/SysV-arm64/ABISysV_arm64.h" #include "Plugins/ABI/SysV-hexagon/ABISysV_hexagon.h" @@ -315,6 +317,7 @@ ABIMacOSX_i386::Initialize(); ABIMacOSX_arm::Initialize(); ABIMacOSX_arm64::Initialize(); + ABISysV_arc::Initialize(); ABISysV_arm::Initialize(); ABISysV_arm64::Initialize(); ABISysV_hexagon::Initialize(); @@ -449,6 +454,7 @@ ABIMacOSX_i386::Terminate(); ABIMacOSX_arm::Terminate(); ABIMacOSX_arm64::Terminate(); + ABISysV_arc::Terminate(); ABISysV_arm::Terminate(); ABISysV_arm64::Terminate(); ABISysV_hexagon::Terminate(); Index: source/Plugins/ABI/CMakeLists.txt =================================================================== --- source/Plugins/ABI/CMakeLists.txt +++ source/Plugins/ABI/CMakeLists.txt @@ -1,3 +1,4 @@ +add_subdirectory(SysV-arc) add_subdirectory(SysV-arm) add_subdirectory(SysV-arm64) add_subdirectory(SysV-hexagon) Index: source/Plugins/ABI/SysV-arc/ABISysV_arc.h =================================================================== --- source/Plugins/ABI/SysV-arc/ABISysV_arc.h +++ source/Plugins/ABI/SysV-arc/ABISysV_arc.h @@ -0,0 +1,103 @@ +//===-- ABISysV_arc.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ABISysV_arc_h_ +#define liblldb_ABISysV_arc_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Target/ABI.h" +#include "lldb/lldb-private.h" + +class ABISysV_arc : public lldb_private::ABI { +public: + ~ABISysV_arc() override = default; + + size_t GetRedZoneSize() const override; + + bool PrepareTrivialCall(lldb_private::Thread &thread, lldb::addr_t sp, + lldb::addr_t functionAddress, + lldb::addr_t returnAddress, + llvm::ArrayRef args) const override; + + // Special thread plan for GDB style non-jit function calls. + bool + PrepareTrivialCall(lldb_private::Thread &thread, lldb::addr_t sp, + lldb::addr_t functionAddress, lldb::addr_t returnAddress, + llvm::Type &prototype, + llvm::ArrayRef args) const override; + + bool GetArgumentValues(lldb_private::Thread &thread, + lldb_private::ValueList &values) const override; + + lldb_private::Status + SetReturnValueObject(lldb::StackFrameSP &frame_sp, + lldb::ValueObjectSP &new_value) override; + + lldb::ValueObjectSP + GetReturnValueObjectImpl(lldb_private::Thread &thread, + lldb_private::CompilerType &type) const override; + + // Specialized to work with llvm IR types. + lldb::ValueObjectSP GetReturnValueObjectImpl(lldb_private::Thread &thread, + llvm::Type &type) const override; + + bool + CreateFunctionEntryUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override; + + bool CreateDefaultUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override; + + bool RegisterIsVolatile(const lldb_private::RegisterInfo *reg_info) override; + + bool CallFrameAddressIsValid(lldb::addr_t cfa) override { + // Stack call frame address must be 4 byte aligned. + return (cfa & 0x3ull) == 0; + } + + bool CodeAddressIsValid(lldb::addr_t pc) override { + // Code addresse must be 2 byte aligned. + return (pc & 1ull) == 0; + } + + const lldb_private::RegisterInfo * + GetRegisterInfoArray(uint32_t &count) override; + + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + + static void Initialize(); + + static void Terminate(); + + static lldb::ABISP CreateInstance(lldb::ProcessSP process_sp, + const lldb_private::ArchSpec &arch); + + static lldb_private::ConstString GetPluginNameStatic(); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + + lldb_private::ConstString GetPluginName() override; + + uint32_t GetPluginVersion() override; + +protected: + lldb::ValueObjectSP + GetReturnValueObjectSimple(lldb_private::Thread &thread, + lldb_private::CompilerType &ast_type) const; + +private: + using lldb_private::ABI::ABI; // Call CreateInstance instead. +}; + +#endif // liblldb_ABISysV_arc_h_ Index: source/Plugins/ABI/SysV-arc/ABISysV_arc.cpp =================================================================== --- source/Plugins/ABI/SysV-arc/ABISysV_arc.cpp +++ source/Plugins/ABI/SysV-arc/ABISysV_arc.cpp @@ -0,0 +1,512 @@ +//===-- ABISysV_arc.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ABISysV_arc.h" + +// C Includes +// C++ Includes +#include +#include +#include + +// Other libraries and framework includes +#include "llvm/ADT/Triple.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/Support/MathExtras.h" + +// Project includes +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Core/ValueObjectMemory.h" +#include "lldb/Core/ValueObjectRegister.h" +#include "lldb/Symbol/UnwindPlan.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/Status.h" + +using namespace lldb; +using namespace lldb_private; + +size_t ABISysV_arc::GetRedZoneSize() const { return 0; } + +//------------------------------------------------------------------ +// Static Functions +//------------------------------------------------------------------ + +ABISP ABISysV_arc::CreateInstance(ProcessSP process_sp, const ArchSpec &arch) { + return llvm::Triple::arc == arch.GetTriple().getArch() ? + ABISP(new ABISysV_arc(process_sp)) : + ABISP(); +} + +namespace { +const size_t word_size = 4u; +const size_t reg_size = word_size; + +inline size_t AugmentArgSize(size_t size_in_bytes) { + return llvm::alignTo(size_in_bytes, word_size); +} + +size_t TotalArgsSizeInWords(const llvm::ArrayRef &args) { + size_t total_size = 0; + for (const auto &arg : args) + total_size += + (ABI::CallArgument::TargetValue == arg.type ? AugmentArgSize(arg.size) + : reg_size) / + word_size; + + return total_size; +} +} // namespace + +bool ABISysV_arc::PrepareTrivialCall(Thread &thread, addr_t sp, + addr_t func_addr, addr_t return_addr, + llvm::ArrayRef args) const { + // We don't use the traditional trivial call specialized for jit. + return false; +} + +bool ABISysV_arc::PrepareTrivialCall( + Thread &thread, lldb::addr_t sp, lldb::addr_t pc, lldb::addr_t ra, + llvm::Type &prototype, llvm::ArrayRef args) const { + auto reg_ctx = thread.GetRegisterContext(); + if (!reg_ctx) + return false; + + uint32_t pc_reg = reg_ctx->ConvertRegisterKindToRegisterNumber( + eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + if (pc_reg == LLDB_INVALID_REGNUM) + return false; + + uint32_t ra_reg = reg_ctx->ConvertRegisterKindToRegisterNumber( + eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA); + if (ra_reg == LLDB_INVALID_REGNUM) + return false; + + uint32_t sp_reg = reg_ctx->ConvertRegisterKindToRegisterNumber( + eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); + if (sp_reg == LLDB_INVALID_REGNUM) + return false; + + Status error; + lldb::ProcessSP process = thread.GetProcess(); + + // Push host data onto target. + for (const auto &arg : args) { + // Skip over target values. + if (arg.type == ABI::CallArgument::TargetValue) + continue; + + // Create space on the stack for this data 4-byte aligned. + sp -= AugmentArgSize(arg.size); + + process->WriteMemory(sp, arg.data_ap.get(), arg.size, error); + if (error.Fail()) + return false; + + // Update the argument with the target pointer. + *const_cast(&arg.value) = sp; + } + + // Make sure number of parameters matches prototype. + assert(!prototype.isFunctionVarArg()); + assert(prototype.getFunctionNumParams() == args.size()); + + const bool is_reg_set_reduced = + process->GetTarget().GetArchitecture().GetFlags() & ArchSpec::eARC_rf16; + const size_t regs_for_args_count = is_reg_set_reduced ? 4u : 8u; + + // Number of arguments passed on stack. + auto args_size = TotalArgsSizeInWords(args); + auto on_stack = + args_size <= regs_for_args_count ? 0 : args_size - regs_for_args_count; + auto offset = on_stack * word_size; + + uint8_t reg_value[reg_size]; + size_t reg_index = LLDB_REGNUM_GENERIC_ARG1; + + for (const auto &arg : args) { + auto value = reinterpret_cast(&arg.value); + auto size = + ABI::CallArgument::TargetValue == arg.type ? arg.size : reg_size; + + // Pass arguments via registers. + while (size > 0 && reg_index < regs_for_args_count) { + size_t byte_index = 0; + auto end = size < reg_size ? size : reg_size; + + while (byte_index < end) { + reg_value[byte_index++] = *(value++); + --size; + } + + while (byte_index < reg_size) { + reg_value[byte_index++] = 0; + } + + RegisterValue reg_val_obj(reg_value, reg_size, lldb::eByteOrderLittle); + if (!reg_ctx->WriteRegister( + reg_ctx->GetRegisterInfo(eRegisterKindGeneric, reg_index), + reg_val_obj)) + return false; + + // NOTE: It's unsafe to iterate through LLDB_REGNUM_GENERICs. + ++reg_index; + } + + if (reg_index < regs_for_args_count || size == 0) + continue; + + // Remaining arguments are passed on the stack. + process->WriteMemory(sp - offset, value, size, error); + if (!error.Success()) + return false; + + offset -= AugmentArgSize(size); + } + + // Set stack pointer immediately below arguments. + sp -= on_stack * word_size; + + // Update registers with current function call state. + reg_ctx->WriteRegisterFromUnsigned(pc_reg, pc); + reg_ctx->WriteRegisterFromUnsigned(ra_reg, ra); + reg_ctx->WriteRegisterFromUnsigned(sp_reg, sp); + + return true; +} + +bool ABISysV_arc::GetArgumentValues(Thread &thread, ValueList &values) const { + return false; +} + +Status ABISysV_arc::SetReturnValueObject(lldb::StackFrameSP &frame_sp, + lldb::ValueObjectSP &new_value_sp) { + Status result; + if (!new_value_sp) { + result.SetErrorString("Empty value object for return value."); + return result; + } + + CompilerType compiler_type = new_value_sp->GetCompilerType(); + if (!compiler_type) { + result.SetErrorString("Null clang type for return value."); + return result; + } + + auto ®_ctx = *frame_sp->GetThread()->GetRegisterContext(); + + bool is_signed = false; + if (!compiler_type.IsIntegerOrEnumerationType(is_signed) && + !compiler_type.IsPointerType()) { + result.SetErrorString("We don't support returning other types at present"); + return result; + } + + DataExtractor data; + size_t num_bytes = new_value_sp->GetData(data, result); + + if (result.Fail()) { + result.SetErrorStringWithFormat( + "Couldn't convert return value to raw data: %s", result.AsCString()); + return result; + } + + if (num_bytes <= 2 * reg_size) { + lldb::offset_t offset = 0; + uint64_t raw_value = data.GetMaxU64(&offset, num_bytes); + + auto reg_info = + reg_ctx.GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1); + if (!reg_ctx.WriteRegisterFromUnsigned(reg_info, raw_value)) { + result.SetErrorStringWithFormat("Couldn't write value to register %s", + reg_info->name); + return result; + } + + if (num_bytes <= reg_size) + return result; // Successfully written. + + raw_value >>= 32; + reg_info = + reg_ctx.GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG2); + if (!reg_ctx.WriteRegisterFromUnsigned(reg_info, raw_value)) { + result.SetErrorStringWithFormat("Couldn't write value to register %s", + reg_info->name); + } + + return result; + } + + result.SetErrorString( + "We don't support returning large integer values at present."); + return result; +} + +namespace { +template +void SetInteger(Scalar &scalar, uint64_t raw_value, bool is_signed) { + raw_value &= std::numeric_limits::max(); + if (is_signed) + scalar = static_cast::type>(raw_value); + else + scalar = static_cast(raw_value); +} + +bool SetSizedInteger(Scalar &scalar, uint64_t raw_value, uint8_t size_in_bytes, + bool is_signed) { + switch (size_in_bytes) { + default: + return false; + + case sizeof(uint64_t): + SetInteger(scalar, raw_value, is_signed); + break; + + case sizeof(uint32_t): + SetInteger(scalar, raw_value, is_signed); + break; + + case sizeof(uint16_t): + SetInteger(scalar, raw_value, is_signed); + break; + + case sizeof(uint8_t): + SetInteger(scalar, raw_value, is_signed); + break; + } + + return true; +} + +bool SetSizedFloat(Scalar &scalar, uint64_t raw_value, uint8_t size_in_bytes) { + switch (size_in_bytes) { + default: + return false; + + case sizeof(uint64_t): + scalar = *reinterpret_cast(&raw_value); + break; + + case sizeof(uint32_t): + scalar = *reinterpret_cast(&raw_value); + break; + } + + return true; +} + +uint64_t ReadRawValue(const RegisterContextSP ®_ctx, uint8_t size_in_bytes) { + auto reg_info_r0 = + reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1); + + // Extract the register context so we can read arguments from registers. + uint64_t raw_value = + reg_ctx->ReadRegisterAsUnsigned(reg_info_r0, 0) & UINT32_MAX; + + if (sizeof(uint64_t) == size_in_bytes) + raw_value |= (reg_ctx->ReadRegisterAsUnsigned( + reg_ctx->GetRegisterInfo(eRegisterKindGeneric, + LLDB_REGNUM_GENERIC_ARG2), 0) & + UINT64_MAX) << 32u; + + return raw_value; +} +} // namespace + +ValueObjectSP +ABISysV_arc::GetReturnValueObjectSimple(Thread &thread, + CompilerType &compiler_type) const { + if (!compiler_type) + return ValueObjectSP(); + + auto reg_ctx = thread.GetRegisterContext(); + if (!reg_ctx) + return ValueObjectSP(); + + Value value; + value.SetCompilerType(compiler_type); + + const uint32_t type_flags = compiler_type.GetTypeInfo(); + // Integer return type. + if (type_flags & eTypeIsInteger) { + const size_t byte_size = compiler_type.GetByteSize(nullptr); + auto raw_value = ReadRawValue(reg_ctx, byte_size); + + const bool is_signed = (type_flags & eTypeIsSigned) != 0; + if (!SetSizedInteger(value.GetScalar(), raw_value, byte_size, is_signed)) + return ValueObjectSP(); + + value.SetValueType(Value::eValueTypeScalar); + } + // Pointer return type. + else if (type_flags & eTypeIsPointer) { + auto reg_info_r0 = reg_ctx->GetRegisterInfo(eRegisterKindGeneric, + LLDB_REGNUM_GENERIC_ARG1); + value.GetScalar() = reg_ctx->ReadRegisterAsUnsigned(reg_info_r0, 0); + + value.SetValueType(Value::eValueTypeScalar); + } + // Floating point return type. + else if (type_flags & eTypeIsFloat) { + uint32_t float_count = 0; + bool is_complex = false; + + if (compiler_type.IsFloatingPointType(float_count, is_complex) && + 1 == float_count && !is_complex) { + const size_t byte_size = compiler_type.GetByteSize(nullptr); + auto raw_value = ReadRawValue(reg_ctx, byte_size); + + if (!SetSizedFloat(value.GetScalar(), raw_value, byte_size)) + return ValueObjectSP(); + } + } + // Unsupported return type. + else + return ValueObjectSP(); + + return ValueObjectConstResult::Create(thread.GetStackFrameAtIndex(0).get(), + value, ConstString("")); +} + +ValueObjectSP ABISysV_arc::GetReturnValueObjectImpl( + Thread &thread, CompilerType &return_compiler_type) const { + ValueObjectSP return_valobj_sp; + + if (!return_compiler_type) + return return_valobj_sp; + + ExecutionContext exe_ctx(thread.shared_from_this()); + return GetReturnValueObjectSimple(thread, return_compiler_type); +} + +lldb::ValueObjectSP +ABISysV_arc::GetReturnValueObjectImpl(lldb_private::Thread &thread, + llvm::Type &retType) const { + auto reg_ctx = thread.GetRegisterContext(); + if (!reg_ctx) + return ValueObjectSP(); + + Value value; + // Void return type. + if (retType.isVoidTy()) { + value.GetScalar() = 0; + } + // Integer return type. + else if (retType.isIntegerTy()) { + size_t byte_size = retType.getPrimitiveSizeInBits(); + if (1 != byte_size) // For boolian type. + byte_size /= CHAR_BIT; + + auto raw_value = ReadRawValue(reg_ctx, byte_size); + + const bool is_signed = false; // IR Type doesn't provide this info. + if (!SetSizedInteger(value.GetScalar(), raw_value, byte_size, is_signed)) + return ValueObjectSP(); + } + // Pointer return type. + else if (retType.isPointerTy()) { + auto reg_info_r0 = reg_ctx->GetRegisterInfo(eRegisterKindGeneric, + LLDB_REGNUM_GENERIC_ARG1); + value.GetScalar() = reg_ctx->ReadRegisterAsUnsigned(reg_info_r0, 0); + value.SetValueType(Value::eValueTypeScalar); + } + // Floating point return type. + else if (retType.isFloatingPointTy()) { + const size_t byte_size = retType.getPrimitiveSizeInBits() / CHAR_BIT; + auto raw_value = ReadRawValue(reg_ctx, byte_size); + + if (!SetSizedFloat(value.GetScalar(), raw_value, byte_size)) + return ValueObjectSP(); + } + // Unsupported return type. + else + return ValueObjectSP(); + + return ValueObjectConstResult::Create(thread.GetStackFrameAtIndex(0).get(), + value, ConstString("")); +} + +namespace DwarfReg { +enum { fp = 27, sp, ra = 31 /*blink*/, pc = 64 }; +} // namespace DwarfReg + +bool ABISysV_arc::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) { + unwind_plan.Clear(); + unwind_plan.SetRegisterKind(eRegisterKindDWARF); + + UnwindPlan::RowSP row(new UnwindPlan::Row); + + // Our Call Frame Address is the stack pointer value. + row->GetCFAValue().SetIsRegisterPlusOffset(DwarfReg::sp, 0); + + // The previous PC is in the BLINK. + row->SetRegisterLocationToRegister(DwarfReg::pc, DwarfReg::ra, true); + unwind_plan.AppendRow(row); + + // All other registers are the same. + + unwind_plan.SetSourceName("arc at-func-entry default"); + unwind_plan.SetSourcedFromCompiler(eLazyBoolNo); + + return true; +} + +const lldb_private::RegisterInfo * +ABISysV_arc::GetRegisterInfoArray(uint32_t &count) { + return nullptr; +} + +bool ABISysV_arc::CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) { + return false; +} + +bool ABISysV_arc::RegisterIsVolatile(const RegisterInfo *reg_info) { + if (nullptr == reg_info) + return false; + + // Volatile registers are: r0..r12. + uint32_t regnum = reg_info->kinds[eRegisterKindDWARF]; + if (regnum <= 12) + return true; + + static const std::string ra_reg_name = "blink"; + return ra_reg_name == reg_info->name; +} + +void ABISysV_arc::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + "System V ABI for ARC targets", CreateInstance); +} + +void ABISysV_arc::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +lldb_private::ConstString ABISysV_arc::GetPluginNameStatic() { + static ConstString g_name("sysv-arc"); + return g_name; +} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ + +lldb_private::ConstString ABISysV_arc::GetPluginName() { + return GetPluginNameStatic(); +} + +uint32_t ABISysV_arc::GetPluginVersion() { return 1; } Index: source/Plugins/ABI/SysV-arc/CMakeLists.txt =================================================================== --- source/Plugins/ABI/SysV-arc/CMakeLists.txt +++ source/Plugins/ABI/SysV-arc/CMakeLists.txt @@ -0,0 +1,11 @@ +add_lldb_library(lldbPluginABISysV_arc PLUGIN + ABISysV_arc.cpp + + LINK_LIBS + lldbCore + lldbSymbol + lldbTarget + lldbPluginProcessUtility + LINK_COMPONENTS + Support + )