//===-- ABISysV_hexagon.cpp -------------------------------------*- 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
//
//===----------------------------------------------------------------------===//

#include "ABISysV_hexagon.h"

#include "llvm/ADT/Triple.h"
#include "llvm/IR/DerivedTypes.h"

#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/DataExtractor.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/RegisterValue.h"
#include "lldb/Utility/Status.h"

using namespace lldb;
using namespace lldb_private;

static RegisterInfo g_register_infos[] = {
    // hexagon-core.xml
    {"r00",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {0, 0, LLDB_INVALID_REGNUM, 0, 0},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"r01",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {1, 1, LLDB_INVALID_REGNUM, 1, 1},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"r02",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {2, 2, LLDB_INVALID_REGNUM, 2, 2},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"r03",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {3, 3, LLDB_INVALID_REGNUM, 3, 3},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"r04",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {4, 4, LLDB_INVALID_REGNUM, 4, 4},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"r05",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {5, 5, LLDB_INVALID_REGNUM, 5, 5},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"r06",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {6, 6, LLDB_INVALID_REGNUM, 6, 6},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"r07",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {7, 7, LLDB_INVALID_REGNUM, 7, 7},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"r08",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {8, 8, LLDB_INVALID_REGNUM, 8, 8},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"r09",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {9, 9, LLDB_INVALID_REGNUM, 9, 9},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"r10",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {10, 10, LLDB_INVALID_REGNUM, 10, 10},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"r11",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {11, 11, LLDB_INVALID_REGNUM, 11, 11},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"r12",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {12, 12, LLDB_INVALID_REGNUM, 12, 12},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"r13",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {13, 13, LLDB_INVALID_REGNUM, 13, 13},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"r14",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {14, 14, LLDB_INVALID_REGNUM, 14, 14},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"r15",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {15, 15, LLDB_INVALID_REGNUM, 15, 15},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"r16",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {16, 16, LLDB_INVALID_REGNUM, 16, 16},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"r17",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {17, 17, LLDB_INVALID_REGNUM, 17, 17},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"r18",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {18, 18, LLDB_INVALID_REGNUM, 18, 18},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"r19",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {19, 19, LLDB_INVALID_REGNUM, 19, 19},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"r20",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {20, 20, LLDB_INVALID_REGNUM, 20, 20},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"r21",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {21, 21, LLDB_INVALID_REGNUM, 21, 21},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"r22",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {22, 22, LLDB_INVALID_REGNUM, 22, 22},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"r23",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {23, 23, LLDB_INVALID_REGNUM, 23, 23},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"r24",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {24, 24, LLDB_INVALID_REGNUM, 24, 24},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"r25",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {25, 25, LLDB_INVALID_REGNUM, 25, 25},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"r26",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {26, 26, LLDB_INVALID_REGNUM, 26, 26},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"r27",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {27, 27, LLDB_INVALID_REGNUM, 27, 27},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"r28",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {28, 28, LLDB_INVALID_REGNUM, 28, 28},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"sp",
     "r29",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {29, 29, LLDB_REGNUM_GENERIC_SP, 29, 29},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"fp",
     "r30",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {30, 30, LLDB_REGNUM_GENERIC_FP, 30, 30},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"lr",
     "r31",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {31, 31, LLDB_REGNUM_GENERIC_RA, 31, 31},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"sa0",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {32, 32, LLDB_INVALID_REGNUM, 32, 32},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"lc0",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {33, 33, LLDB_INVALID_REGNUM, 33, 33},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"sa1",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {34, 34, LLDB_INVALID_REGNUM, 34, 34},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"lc1",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {35, 35, LLDB_INVALID_REGNUM, 35, 35},
     nullptr,
     nullptr,
     nullptr,
     0},
    // --> hexagon-v4/5/55/56-sim.xml
    {"p3_0",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {36, 36, LLDB_INVALID_REGNUM, 36, 36},
     nullptr,
     nullptr,
     nullptr,
     0},
    // PADDING {
    {"p00",
     "",
     4,
     0,
     eEncodingInvalid,
     eFormatInvalid,
     {37, 37, LLDB_INVALID_REGNUM, 37, 37},
     nullptr,
     nullptr,
     nullptr,
     0},
    // }
    {"m0",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {38, 38, LLDB_INVALID_REGNUM, 38, 38},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"m1",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {39, 39, LLDB_INVALID_REGNUM, 39, 39},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"usr",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {40, 40, LLDB_INVALID_REGNUM, 40, 40},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"pc",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {41, 41, LLDB_REGNUM_GENERIC_PC, 41, 41},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"ugp",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {42, 42, LLDB_INVALID_REGNUM, 42, 42},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"gp",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {43, 43, LLDB_INVALID_REGNUM, 43, 43},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"cs0",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {44, 44, LLDB_INVALID_REGNUM, 44, 44},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"cs1",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {45, 45, LLDB_INVALID_REGNUM, 45, 45},
     nullptr,
     nullptr,
     nullptr,
     0},
    // PADDING {
    {"p01",
     "",
     4,
     0,
     eEncodingInvalid,
     eFormatInvalid,
     {46, 46, LLDB_INVALID_REGNUM, 46, 46},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"p02",
     "",
     4,
     0,
     eEncodingInvalid,
     eFormatInvalid,
     {47, 47, LLDB_INVALID_REGNUM, 47, 47},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"p03",
     "",
     4,
     0,
     eEncodingInvalid,
     eFormatInvalid,
     {48, 48, LLDB_INVALID_REGNUM, 48, 48},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"p04",
     "",
     4,
     0,
     eEncodingInvalid,
     eFormatInvalid,
     {49, 49, LLDB_INVALID_REGNUM, 49, 49},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"p05",
     "",
     4,
     0,
     eEncodingInvalid,
     eFormatInvalid,
     {50, 50, LLDB_INVALID_REGNUM, 50, 50},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"p06",
     "",
     4,
     0,
     eEncodingInvalid,
     eFormatInvalid,
     {51, 51, LLDB_INVALID_REGNUM, 51, 51},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"p07",
     "",
     4,
     0,
     eEncodingInvalid,
     eFormatInvalid,
     {52, 52, LLDB_INVALID_REGNUM, 52, 52},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"p08",
     "",
     4,
     0,
     eEncodingInvalid,
     eFormatInvalid,
     {53, 53, LLDB_INVALID_REGNUM, 53, 53},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"p09",
     "",
     4,
     0,
     eEncodingInvalid,
     eFormatInvalid,
     {54, 54, LLDB_INVALID_REGNUM, 54, 54},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"p10",
     "",
     4,
     0,
     eEncodingInvalid,
     eFormatInvalid,
     {55, 55, LLDB_INVALID_REGNUM, 55, 55},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"p11",
     "",
     4,
     0,
     eEncodingInvalid,
     eFormatInvalid,
     {56, 56, LLDB_INVALID_REGNUM, 56, 56},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"p12",
     "",
     4,
     0,
     eEncodingInvalid,
     eFormatInvalid,
     {57, 57, LLDB_INVALID_REGNUM, 57, 57},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"p13",
     "",
     4,
     0,
     eEncodingInvalid,
     eFormatInvalid,
     {58, 58, LLDB_INVALID_REGNUM, 58, 58},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"p14",
     "",
     4,
     0,
     eEncodingInvalid,
     eFormatInvalid,
     {59, 59, LLDB_INVALID_REGNUM, 59, 59},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"p15",
     "",
     4,
     0,
     eEncodingInvalid,
     eFormatInvalid,
     {60, 60, LLDB_INVALID_REGNUM, 60, 60},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"p16",
     "",
     4,
     0,
     eEncodingInvalid,
     eFormatInvalid,
     {61, 61, LLDB_INVALID_REGNUM, 61, 61},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"p17",
     "",
     4,
     0,
     eEncodingInvalid,
     eFormatInvalid,
     {62, 62, LLDB_INVALID_REGNUM, 62, 62},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"p18",
     "",
     4,
     0,
     eEncodingInvalid,
     eFormatInvalid,
     {63, 63, LLDB_INVALID_REGNUM, 63, 63},
     nullptr,
     nullptr,
     nullptr,
     0},
    // }
    {"sgp0",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {64, 64, LLDB_INVALID_REGNUM, 64, 64},
     nullptr,
     nullptr,
     nullptr,
     0},
    // PADDING {
    {"p19",
     "",
     4,
     0,
     eEncodingInvalid,
     eFormatInvalid,
     {65, 65, LLDB_INVALID_REGNUM, 65, 65},
     nullptr,
     nullptr,
     nullptr,
     0},
    // }
    {"stid",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {66, 66, LLDB_INVALID_REGNUM, 66, 66},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"elr",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {67, 67, LLDB_INVALID_REGNUM, 67, 67},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"badva0",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {68, 68, LLDB_INVALID_REGNUM, 68, 68},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"badva1",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {69, 69, LLDB_INVALID_REGNUM, 69, 69},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"ssr",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {70, 70, LLDB_INVALID_REGNUM, 70, 70},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"ccr",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {71, 71, LLDB_INVALID_REGNUM, 71, 71},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"htid",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {72, 72, LLDB_INVALID_REGNUM, 72, 72},
     nullptr,
     nullptr,
     nullptr,
     0},
    // PADDING {
    {"p20",
     "",
     4,
     0,
     eEncodingInvalid,
     eFormatInvalid,
     {73, 73, LLDB_INVALID_REGNUM, 73, 73},
     nullptr,
     nullptr,
     nullptr,
     0},
    // }
    {"imask",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {74, 74, LLDB_INVALID_REGNUM, 74, 74},
     nullptr,
     nullptr,
     nullptr,
     0},
    // PADDING {
    {"p21",
     "",
     4,
     0,
     eEncodingInvalid,
     eFormatInvalid,
     {75, 75, LLDB_INVALID_REGNUM, 75, 75},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"p22",
     "",
     4,
     0,
     eEncodingInvalid,
     eFormatInvalid,
     {76, 76, LLDB_INVALID_REGNUM, 76, 76},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"p23",
     "",
     4,
     0,
     eEncodingInvalid,
     eFormatInvalid,
     {77, 77, LLDB_INVALID_REGNUM, 77, 77},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"p24",
     "",
     4,
     0,
     eEncodingInvalid,
     eFormatInvalid,
     {78, 78, LLDB_INVALID_REGNUM, 78, 78},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"p25",
     "",
     4,
     0,
     eEncodingInvalid,
     eFormatInvalid,
     {79, 79, LLDB_INVALID_REGNUM, 79, 79},
     nullptr,
     nullptr,
     nullptr,
     0},
    // }
    {"g0",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {80, 80, LLDB_INVALID_REGNUM, 80, 80},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"g1",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {81, 81, LLDB_INVALID_REGNUM, 81, 81},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"g2",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {82, 82, LLDB_INVALID_REGNUM, 82, 82},
     nullptr,
     nullptr,
     nullptr,
     0},
    {"g3",
     "",
     4,
     0,
     eEncodingUint,
     eFormatAddressInfo,
     {83, 83, LLDB_INVALID_REGNUM, 83, 83},
     nullptr,
     nullptr,
     nullptr,
     0}};

static const uint32_t k_num_register_infos =
    sizeof(g_register_infos) / sizeof(RegisterInfo);
static bool g_register_info_names_constified = false;

const lldb_private::RegisterInfo *
ABISysV_hexagon::GetRegisterInfoArray(uint32_t &count) {
  // Make the C-string names and alt_names for the register infos into const
  // C-string values by having the ConstString unique the names in the global
  // constant C-string pool.
  if (!g_register_info_names_constified) {
    g_register_info_names_constified = true;
    for (uint32_t i = 0; i < k_num_register_infos; ++i) {
      if (g_register_infos[i].name)
        g_register_infos[i].name =
            ConstString(g_register_infos[i].name).GetCString();
      if (g_register_infos[i].alt_name)
        g_register_infos[i].alt_name =
            ConstString(g_register_infos[i].alt_name).GetCString();
    }
  }
  count = k_num_register_infos;
  return g_register_infos;
}

/*
    http://en.wikipedia.org/wiki/Red_zone_%28computing%29

    In computing, a red zone is a fixed size area in memory beyond the stack
   pointer that has not been
    "allocated". This region of memory is not to be modified by
   interrupt/exception/signal handlers.
    This allows the space to be used for temporary data without the extra
   overhead of modifying the
    stack pointer. The x86-64 ABI mandates a 128 byte red zone.[1] The OpenRISC
   toolchain assumes a
    128 byte red zone though it is not documented.
*/
size_t ABISysV_hexagon::GetRedZoneSize() const { return 0; }

// Static Functions

ABISP
ABISysV_hexagon::CreateInstance(lldb::ProcessSP process_sp, const ArchSpec &arch) {
  if (arch.GetTriple().getArch() == llvm::Triple::hexagon) {
    return ABISP(
        new ABISysV_hexagon(std::move(process_sp), MakeMCRegisterInfo(arch)));
  }
  return ABISP();
}

bool ABISysV_hexagon::PrepareTrivialCall(Thread &thread, lldb::addr_t sp,
                                         lldb::addr_t pc, lldb::addr_t ra,
                                         llvm::ArrayRef<addr_t> args) const {
  // we don't use the traditional trivial call specialized for jit
  return false;
}

/*

// AD:
//  . safeguard the current stack
//  . how can we know that the called function will create its own frame
properly?
//  . we could manually make a new stack first:
//      2. push RA
//      3. push FP
//      4. FP = SP
//      5. SP = SP ( since no locals in our temp frame )

// AD 6/05/2014
//  . variable argument list parameters are not passed via registers, they are
passed on
//    the stack.  This presents us with a problem, since we need to know when
the valist
//    starts.  Currently I can find out if a function is varg, but not how many
//    real parameters it takes.  Thus I don't know when to start spilling the
vargs.  For
//    the time being, to progress, I will assume that it takes on real parameter
before
//    the vargs list starts.

// AD 06/05/2014
//  . how do we adhere to the stack alignment requirements

// AD 06/05/2014
//  . handle 64bit values and their register / stack requirements

*/
#define HEX_ABI_DEBUG 0
bool ABISysV_hexagon::PrepareTrivialCall(
    Thread &thread, lldb::addr_t sp, lldb::addr_t pc, lldb::addr_t ra,
    llvm::Type &prototype, llvm::ArrayRef<ABI::CallArgument> args) const {
  // default number of register passed arguments for varg functions
  const int nVArgRegParams = 1;
  Status error;

  // grab the process so we have access to the memory for spilling
  lldb::ProcessSP proc = thread.GetProcess();

  // get the register context for modifying all of the registers
  RegisterContext *reg_ctx = thread.GetRegisterContext().get();
  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;

  // push host data onto target
  for (size_t i = 0; i < args.size(); i++) {
    const ABI::CallArgument &arg = args[i];
    // skip over target values
    if (arg.type == ABI::CallArgument::TargetValue)
      continue;
    // round up to 8 byte multiple
    size_t argSize = (arg.size | 0x7) + 1;

    // create space on the stack for this data
    sp -= argSize;

    // write this argument onto the stack of the host process
    proc->WriteMemory(sp, arg.data_up.get(), arg.size, error);
    if (error.Fail())
      return false;

    // update the argument with the target pointer
    // XXX: This is a gross hack for getting around the const
    *const_cast<lldb::addr_t *>(&arg.value) = sp;
  }

#if HEX_ABI_DEBUG
  // print the original stack pointer
  printf("sp : %04" PRIx64 " \n", sp);
#endif

  // make sure number of parameters matches prototype
  assert(prototype.getFunctionNumParams() == args.size());

  // check if this is a variable argument function
  bool isVArg = prototype.isFunctionVarArg();

  // number of arguments passed by register
  int nRegArgs = nVArgRegParams;
  if (!isVArg) {
    // number of arguments is limited by [R0 : R5] space
    nRegArgs = args.size();
    if (nRegArgs > 6)
      nRegArgs = 6;
  }

  // pass arguments that are passed via registers
  for (int i = 0; i < nRegArgs; i++) {
    // get the parameter as a u32
    uint32_t param = (uint32_t)args[i].value;
    // write argument into register
    if (!reg_ctx->WriteRegisterFromUnsigned(i, param))
      return false;
  }

  // number of arguments to spill onto stack
  int nSpillArgs = args.size() - nRegArgs;
  // make space on the stack for arguments
  sp -= 4 * nSpillArgs;
  // align stack on an 8 byte boundary
  if (sp & 7)
    sp -= 4;

  // arguments that are passed on the stack
  for (size_t i = nRegArgs, offs = 0; i < args.size(); i++) {
    // get the parameter as a u32
    uint32_t param = (uint32_t)args[i].value;
    // write argument to stack
    proc->WriteMemory(sp + offs, (void *)&param, sizeof(param), error);
    if (!error.Success())
      return false;
    //
    offs += 4;
  }

  // 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);

#if HEX_ABI_DEBUG
  // quick and dirty stack dumper for debugging
  for (int i = -8; i < 8; i++) {
    uint32_t data = 0;
    lldb::addr_t addr = sp + i * 4;
    proc->ReadMemory(addr, (void *)&data, sizeof(data), error);
    printf("\n0x%04" PRIx64 " 0x%08x ", addr, data);
    if (i == 0)
      printf("<<-- sp");
  }
  printf("\n");
#endif

  return true;
}

bool ABISysV_hexagon::GetArgumentValues(Thread &thread,
                                        ValueList &values) const {
  return false;
}

Status
ABISysV_hexagon::SetReturnValueObject(lldb::StackFrameSP &frame_sp,
                                      lldb::ValueObjectSP &new_value_sp) {
  Status error;
  return error;
}

ValueObjectSP ABISysV_hexagon::GetReturnValueObjectSimple(
    Thread &thread, CompilerType &return_compiler_type) const {
  ValueObjectSP return_valobj_sp;
  return return_valobj_sp;
}

ValueObjectSP ABISysV_hexagon::GetReturnValueObjectImpl(
    Thread &thread, CompilerType &return_compiler_type) const {
  ValueObjectSP return_valobj_sp;
  return return_valobj_sp;
}

// called when we are on the first instruction of a new function for hexagon
// the return address is in RA (R31)
bool ABISysV_hexagon::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) {
  unwind_plan.Clear();
  unwind_plan.SetRegisterKind(eRegisterKindGeneric);
  unwind_plan.SetReturnAddressRegister(LLDB_REGNUM_GENERIC_RA);

  UnwindPlan::RowSP row(new UnwindPlan::Row);

  // Our Call Frame Address is the stack pointer value
  row->GetCFAValue().SetIsRegisterPlusOffset(LLDB_REGNUM_GENERIC_SP, 4);
  row->SetOffset(0);

  // The previous PC is in the LR
  row->SetRegisterLocationToRegister(LLDB_REGNUM_GENERIC_PC,
                                     LLDB_REGNUM_GENERIC_RA, true);
  unwind_plan.AppendRow(row);

  unwind_plan.SetSourceName("hexagon at-func-entry default");
  unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
  return true;
}

bool ABISysV_hexagon::CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) {
  unwind_plan.Clear();
  unwind_plan.SetRegisterKind(eRegisterKindGeneric);

  uint32_t fp_reg_num = LLDB_REGNUM_GENERIC_FP;
  uint32_t sp_reg_num = LLDB_REGNUM_GENERIC_SP;
  uint32_t pc_reg_num = LLDB_REGNUM_GENERIC_PC;

  UnwindPlan::RowSP row(new UnwindPlan::Row);

  row->GetCFAValue().SetIsRegisterPlusOffset(LLDB_REGNUM_GENERIC_FP, 8);

  row->SetRegisterLocationToAtCFAPlusOffset(fp_reg_num, -8, true);
  row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, -4, true);
  row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true);

  unwind_plan.AppendRow(row);
  unwind_plan.SetSourceName("hexagon default unwind plan");
  unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
  unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
  unwind_plan.SetUnwindPlanForSignalTrap(eLazyBoolNo);
  return true;
}

/*
    Register		Usage					Saved By

    R0  - R5		parameters(a)			-
    R6  - R15		Scratch(b)				Caller
    R16 - R27		Scratch					Callee
    R28				Scratch(b)				Caller
    R29 - R31		Stack Frames			Callee(c)
    P3:0			Processor State			Caller

    a = the caller can change parameter values
    b = R14 - R15 and R28 are used by the procedure linkage table
    c = R29 - R31 are saved and restored by allocframe() and deallocframe()
*/
bool ABISysV_hexagon::RegisterIsVolatile(const RegisterInfo *reg_info) {
  return !RegisterIsCalleeSaved(reg_info);
}

bool ABISysV_hexagon::RegisterIsCalleeSaved(const RegisterInfo *reg_info) {
  int reg = ((reg_info->byte_offset) / 4);

  bool save = (reg >= 16) && (reg <= 27);
  save |= (reg >= 29) && (reg <= 32);

  return save;
}

void ABISysV_hexagon::Initialize() {
  PluginManager::RegisterPlugin(GetPluginNameStatic(),
                                "System V ABI for hexagon targets",
                                CreateInstance);
}

void ABISysV_hexagon::Terminate() {
  PluginManager::UnregisterPlugin(CreateInstance);
}

lldb_private::ConstString ABISysV_hexagon::GetPluginNameStatic() {
  static ConstString g_name("sysv-hexagon");
  return g_name;
}

// PluginInterface protocol

lldb_private::ConstString ABISysV_hexagon::GetPluginName() {
  return GetPluginNameStatic();
}

uint32_t ABISysV_hexagon::GetPluginVersion() { return 1; }

// get value object specialized to work with llvm IR types
lldb::ValueObjectSP
ABISysV_hexagon::GetReturnValueObjectImpl(lldb_private::Thread &thread,
                                          llvm::Type &retType) const {
  Value value;
  ValueObjectSP vObjSP;

  // get the current register context
  RegisterContext *reg_ctx = thread.GetRegisterContext().get();
  if (!reg_ctx)
    return vObjSP;

  // for now just pop R0 to find the return value
  const lldb_private::RegisterInfo *r0_info =
      reg_ctx->GetRegisterInfoAtIndex(0);
  if (r0_info == nullptr)
    return vObjSP;

  // void return type
  if (retType.isVoidTy()) {
    value.GetScalar() = 0;
  }
  // integer / pointer return type
  else if (retType.isIntegerTy() || retType.isPointerTy()) {
    // read r0 register value
    lldb_private::RegisterValue r0_value;
    if (!reg_ctx->ReadRegister(r0_info, r0_value))
      return vObjSP;

    // push r0 into value
    uint32_t r0_u32 = r0_value.GetAsUInt32();

    // account for integer size
    if (retType.isIntegerTy() && retType.isSized()) {
      uint64_t size = retType.getScalarSizeInBits();
      uint64_t mask = (1ull << size) - 1;
      // mask out higher order bits then the type we expect
      r0_u32 &= mask;
    }

    value.GetScalar() = r0_u32;
  }
  // unsupported return type
  else
    return vObjSP;

  // pack the value into a ValueObjectSP
  vObjSP = ValueObjectConstResult::Create(thread.GetStackFrameAtIndex(0).get(),
                                          value, ConstString(""));
  return vObjSP;
}
