//===-- ArchSpec.cpp --------------------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "lldb/Core/ArchSpec.h"

#include <stdio.h>

#include <string>

#include "llvm/Support/ELF.h"
#include "llvm/Support/MachO.h"
#include "lldb/Host/Endian.h"
#include "lldb/Host/Host.h"

using namespace lldb;
using namespace lldb_private;

#define ARCH_SPEC_SEPARATOR_CHAR '-'

struct ArchSpec::ArchDefinition
{
    ArchitectureType type;
    size_t num_entries;
    const ArchDefinitionEntry *entries;
    const char *name;
};

struct ArchSpec::ArchDefinitionEntry
{
    ByteOrder byte_order;
    uint32_t addr_byte_size;
    ArchSpec::CPU generic_cpu;
    uint32_t cpu;
    uint32_t sub;
    const char *name;
};

#define CPU_ANY (UINT32_MAX)

//===----------------------------------------------------------------------===//
// A table that gets searched linearly for matches. This table is used to
// convert cpu type and subtypes to architecture names, and to convert
// architecture names to cpu types and subtypes. The ordering is important and
// allows the precedence to be set when the table is built.
static const ArchSpec::ArchDefinitionEntry g_macho_arch_entries[] =
{
    { eByteOrderInvalid, 0, ArchSpec::eCPU_Unknown, CPU_ANY,                       CPU_ANY , "all"      },
    { eByteOrderLittle,  4, ArchSpec::eCPU_arm,     llvm::MachO::CPUTypeARM,       CPU_ANY , "arm"      },
    { eByteOrderLittle,  4, ArchSpec::eCPU_arm,     llvm::MachO::CPUTypeARM,       0       , "arm"      },
    { eByteOrderLittle,  4, ArchSpec::eCPU_arm,     llvm::MachO::CPUTypeARM,       5       , "armv4"    },
    { eByteOrderLittle,  4, ArchSpec::eCPU_arm,     llvm::MachO::CPUTypeARM,       6       , "armv6"    },
    { eByteOrderLittle,  4, ArchSpec::eCPU_arm,     llvm::MachO::CPUTypeARM,       7       , "armv5"    },
    { eByteOrderLittle,  4, ArchSpec::eCPU_arm,     llvm::MachO::CPUTypeARM,       8       , "xscale"   },
    { eByteOrderLittle,  4, ArchSpec::eCPU_arm,     llvm::MachO::CPUTypeARM,       9       , "armv7"    },
    { eByteOrderBig,     4, ArchSpec::eCPU_ppc,     llvm::MachO::CPUTypePowerPC,   CPU_ANY , "ppc"      },
    { eByteOrderBig,     4, ArchSpec::eCPU_ppc,     llvm::MachO::CPUTypePowerPC,   0       , "ppc"      },
    { eByteOrderBig,     4, ArchSpec::eCPU_ppc,     llvm::MachO::CPUTypePowerPC,   1       , "ppc601"   },
    { eByteOrderBig,     4, ArchSpec::eCPU_ppc,     llvm::MachO::CPUTypePowerPC,   2       , "ppc602"   },
    { eByteOrderBig,     4, ArchSpec::eCPU_ppc,     llvm::MachO::CPUTypePowerPC,   3       , "ppc603"   },
    { eByteOrderBig,     4, ArchSpec::eCPU_ppc,     llvm::MachO::CPUTypePowerPC,   4       , "ppc603e"  },
    { eByteOrderBig,     4, ArchSpec::eCPU_ppc,     llvm::MachO::CPUTypePowerPC,   5       , "ppc603ev" },
    { eByteOrderBig,     4, ArchSpec::eCPU_ppc,     llvm::MachO::CPUTypePowerPC,   6       , "ppc604"   },
    { eByteOrderBig,     4, ArchSpec::eCPU_ppc,     llvm::MachO::CPUTypePowerPC,   7       , "ppc604e"  },
    { eByteOrderBig,     4, ArchSpec::eCPU_ppc,     llvm::MachO::CPUTypePowerPC,   8       , "ppc620"   },
    { eByteOrderBig,     4, ArchSpec::eCPU_ppc,     llvm::MachO::CPUTypePowerPC,   9       , "ppc750"   },
    { eByteOrderBig,     4, ArchSpec::eCPU_ppc,     llvm::MachO::CPUTypePowerPC,   10      , "ppc7400"  },
    { eByteOrderBig,     4, ArchSpec::eCPU_ppc,     llvm::MachO::CPUTypePowerPC,   11      , "ppc7450"  },
    { eByteOrderBig,     4, ArchSpec::eCPU_ppc,     llvm::MachO::CPUTypePowerPC,   100     , "ppc970"   },
    { eByteOrderBig,     8, ArchSpec::eCPU_ppc64,   llvm::MachO::CPUTypePowerPC64, 0       , "ppc64"    },
    { eByteOrderBig,     8, ArchSpec::eCPU_ppc64,   llvm::MachO::CPUTypePowerPC64, 100     , "ppc970-64"},
    { eByteOrderLittle,  4, ArchSpec::eCPU_i386,    llvm::MachO::CPUTypeI386,      3       , "i386"     },
    { eByteOrderLittle,  4, ArchSpec::eCPU_i386,    llvm::MachO::CPUTypeI386,      4       , "i486"     },
    { eByteOrderLittle,  4, ArchSpec::eCPU_i386,    llvm::MachO::CPUTypeI386,      0x84    , "i486sx"   },
    { eByteOrderLittle,  4, ArchSpec::eCPU_i386,    llvm::MachO::CPUTypeI386,      CPU_ANY , "i386"     },
    { eByteOrderLittle,  8, ArchSpec::eCPU_x86_64,  llvm::MachO::CPUTypeX86_64,    3       , "x86_64"   },
    { eByteOrderLittle,  8, ArchSpec::eCPU_x86_64,  llvm::MachO::CPUTypeX86_64,    CPU_ANY , "x86_64"   },
};

static const ArchSpec::ArchDefinition g_macho_arch_def = {
    eArchTypeMachO,
    sizeof(g_macho_arch_entries)/sizeof(g_macho_arch_entries[0]),
    g_macho_arch_entries,
    "mach-o"
};

//===----------------------------------------------------------------------===//
// A table that gets searched linearly for matches. This table is used to
// convert cpu type and subtypes to architecture names, and to convert
// architecture names to cpu types and subtypes. The ordering is important and
// allows the precedence to be set when the table is built.
static const ArchSpec::ArchDefinitionEntry g_elf_arch_entries[] =
{
    { eByteOrderInvalid, 0, ArchSpec::eCPU_Unknown, llvm::ELF::EM_M32    , LLDB_INVALID_CPUTYPE, "m32"   }, // AT&T WE 32100
    { eByteOrderBig,     4, ArchSpec::eCPU_Unknown, llvm::ELF::EM_SPARC  , LLDB_INVALID_CPUTYPE, "sparc" }, // Sparc
    { eByteOrderLittle,  4, ArchSpec::eCPU_i386,    llvm::ELF::EM_386    , LLDB_INVALID_CPUTYPE, "i386"  }, // Intel 80386
    { eByteOrderBig,     4, ArchSpec::eCPU_Unknown, llvm::ELF::EM_68K    , LLDB_INVALID_CPUTYPE, "68k"   }, // Motorola 68000
    { eByteOrderBig,     4, ArchSpec::eCPU_Unknown, llvm::ELF::EM_88K    , LLDB_INVALID_CPUTYPE, "88k"   }, // Motorola 88000
    { eByteOrderLittle,  4, ArchSpec::eCPU_i386,    llvm::ELF::EM_486    , LLDB_INVALID_CPUTYPE, "i486"  }, // Intel 486 (deprecated)
    { eByteOrderLittle,  4, ArchSpec::eCPU_Unknown, llvm::ELF::EM_860    , LLDB_INVALID_CPUTYPE, "860"   }, // Intel 80860
    { eByteOrderBig,     4, ArchSpec::eCPU_Unknown, llvm::ELF::EM_MIPS   , LLDB_INVALID_CPUTYPE, "rs3000"}, // MIPS RS3000
    { eByteOrderBig,     4, ArchSpec::eCPU_ppc,     llvm::ELF::EM_PPC    , LLDB_INVALID_CPUTYPE, "ppc"   }, // PowerPC
    { eByteOrderBig,     8, ArchSpec::eCPU_ppc64,   llvm::ELF::EM_PPC64  , LLDB_INVALID_CPUTYPE, "ppc64" }, // PowerPC64
    { eByteOrderLittle,  4, ArchSpec::eCPU_arm,     llvm::ELF::EM_ARM    , LLDB_INVALID_CPUTYPE, "arm"   }, // ARM
    { eByteOrderLittle,  8, ArchSpec::eCPU_Unknown, llvm::ELF::EM_ALPHA  , LLDB_INVALID_CPUTYPE, "alpha" }, // DEC Alpha
    { eByteOrderLittle,  8, ArchSpec::eCPU_Unknown, llvm::ELF::EM_SPARCV9, LLDB_INVALID_CPUTYPE, "sparc9"}, // SPARC V9
    { eByteOrderLittle,  8, ArchSpec::eCPU_x86_64,  llvm::ELF::EM_X86_64 , LLDB_INVALID_CPUTYPE, "x86_64"}, // AMD64
};

static const ArchSpec::ArchDefinition g_elf_arch_def = {
    eArchTypeELF,
    sizeof(g_elf_arch_entries)/sizeof(g_elf_arch_entries[0]),
    g_elf_arch_entries,
    "elf",
};

//===----------------------------------------------------------------------===//
// Table of all ArchDefinitions
static const ArchSpec::ArchDefinition *g_arch_definitions[] = {
    &g_macho_arch_def,
    &g_elf_arch_def,
};

static const size_t k_num_arch_definitions =
    sizeof(g_arch_definitions) / sizeof(g_arch_definitions[0]);

//===----------------------------------------------------------------------===//
// Static helper functions.


// Get the architecture definition for a given object type.
static const ArchSpec::ArchDefinition *
FindArchDefinition (ArchitectureType arch_type)
{
    for (unsigned int i = 0; i < k_num_arch_definitions; ++i)
    {
        const ArchSpec::ArchDefinition *def = g_arch_definitions[i];
        if (def->type == arch_type)
            return def;
    }
    return NULL;
}

// Get an architecture definition by name.
static const ArchSpec::ArchDefinition *
FindArchDefinition (llvm::StringRef name)
{
    for (unsigned int i = 0; i < k_num_arch_definitions; ++i)
    {
        const ArchSpec::ArchDefinition *def = g_arch_definitions[i];
        if (name.compare_lower(def->name))
            return def;
    }
    return NULL;
}

// Get a definition entry by cpu type and subtype.
static const ArchSpec::ArchDefinitionEntry *
FindArchDefinitionEntry (const ArchSpec::ArchDefinition *def, uint32_t cpu, uint32_t sub)
{
    if (def == NULL)
        return NULL;

    const ArchSpec::ArchDefinitionEntry *entries = def->entries;
    for (size_t i = 0; i < def->num_entries; ++i)
    {
        if (entries[i].cpu == cpu && sub == entries[i].sub)
            return &entries[i];
    }
    return NULL;
}

// Get a definition entry by architecture name.
static const ArchSpec::ArchDefinitionEntry *
FindArchDefinitionEntry (const ArchSpec::ArchDefinition *def, llvm::StringRef arch_name)
{
    if (def == NULL)
        return NULL;

    const ArchSpec::ArchDefinitionEntry *entries = def->entries;
    for (size_t i = 0; i < def->num_entries; ++i)
    {
        if (arch_name.equals_lower(entries[i].name))
            return &entries[i];
    }
    return NULL;
}

// Get the object type implied by the given triple.
static ArchitectureType
GetTripleArchitectureType (const llvm::Triple &triple)
{
    ArchitectureType arch_type = eArchTypeInvalid;

    switch (triple.getOS())
    {
    default:
        break;

    case llvm::Triple::Darwin:
        arch_type = eArchTypeMachO;
        break;

    case llvm::Triple::AuroraUX:
    case llvm::Triple::DragonFly:
    case llvm::Triple::FreeBSD:
    case llvm::Triple::Linux:
    case llvm::Triple::NetBSD:
    case llvm::Triple::OpenBSD:
    case llvm::Triple::Solaris:
        arch_type = eArchTypeELF;
        break;
    }
    return arch_type;
}

//===----------------------------------------------------------------------===//
// Constructors and destructors.

ArchSpec::ArchSpec() :
    m_def (NULL),
    m_entry (NULL),
    m_byte_order (eByteOrderInvalid),
    m_triple (),
    m_flags (0)
{
}

ArchSpec::ArchSpec(const llvm::Triple &triple) :
    m_def (NULL),
    m_entry (NULL),
    m_byte_order (eByteOrderInvalid),
    m_triple (),
    m_flags (0)
{
    SetTriple(triple);
}

ArchSpec::ArchSpec (ArchitectureType arch_type, llvm::StringRef arch_name) :
    m_def (FindArchDefinition(arch_type)),
    m_entry (NULL),
    m_byte_order (eByteOrderInvalid),
    m_triple (),
    m_flags (0)
{
    if (m_def != NULL)
    {
        m_entry = FindArchDefinitionEntry(m_def, arch_name);
        ArchUpdated();
    }
}

ArchSpec::~ArchSpec()
{
}

//===----------------------------------------------------------------------===//
// Assignment and initialization.

const ArchSpec&
ArchSpec::operator= (const ArchSpec& rhs)
{
    if (this != &rhs)
    {
        m_def = rhs.m_def;
        m_entry = rhs.m_entry;
        m_flags = rhs.m_flags;
        m_triple = rhs.m_triple;
        m_byte_order = rhs.m_byte_order;
    }
    return *this;
}

void
ArchSpec::Clear()
{
    m_def = NULL;
    m_entry = NULL;
    m_flags = 0;
    m_triple = llvm::Triple();
    m_byte_order = eByteOrderInvalid;
}

//===----------------------------------------------------------------------===//
// Predicates.

bool
ArchSpec::IsValid() const
{
    return (m_def != NULL && m_entry != NULL);
}

//===----------------------------------------------------------------------===//
// Accessors.

ArchitectureType
ArchSpec::GetType () const
{
    if (m_def == NULL)
        return eArchTypeInvalid;

    return m_def->type;
}

size_t
ArchSpec::GetAsCString(char *dst, size_t len) const
{
    if (m_def != NULL && m_entry != NULL)
    {
        int ret = ::snprintf(dst, len, "%s%c%u%c%u",
                             m_def->name, ARCH_SPEC_SEPARATOR_CHAR,
                             m_entry->cpu, ARCH_SPEC_SEPARATOR_CHAR,
                             m_entry->sub);
        assert(ret >= 0 && "snprintf failed?");
        return ret;
    }

    if (m_def != NULL)
    {
        int ret = ::snprintf(dst, len, "%s", m_def->name);
        assert(ret >= 0 && "snprintf failed?");
        return ret;
    }

    return 0;
}

const char *
ArchSpec::GetArchitectureName () const
{
    if (m_entry == NULL)
        return "unknown";

    return m_entry->name;
}

uint32_t
ArchSpec::GetCPUSubtype() const
{
    if (m_entry == NULL)
        return LLDB_INVALID_CPUTYPE;

    return m_entry->sub;
}

uint32_t
ArchSpec::GetCPUType() const
{
    if (m_entry == NULL)
        return LLDB_INVALID_CPUTYPE;

    return m_entry->cpu;
}

ArchSpec::CPU
ArchSpec::GetGenericCPUType () const
{
    if (m_entry == NULL)
        return eCPU_Unknown;

    return m_entry->generic_cpu;
}

uint32_t
ArchSpec::GetAddressByteSize() const
{
    if (m_entry == NULL)
        return 0;

    return m_entry->addr_byte_size;
}

ByteOrder
ArchSpec::GetDefaultEndian () const
{
    if (m_entry == NULL)
        return eByteOrderInvalid;

    return m_entry->byte_order;
}

lldb::ByteOrder
ArchSpec::GetByteOrder () const
{
    return m_byte_order;
}

ArchSpec
ArchSpec::GetDefaultArch (lldb::ArchitectureType arch_type)
{
    return ArchSpec(arch_type, LLDB_ARCH_DEFAULT);
}

ArchSpec
ArchSpec::GetDefaultArch32(lldb::ArchitectureType arch_type)
{
    return ArchSpec(arch_type, LLDB_ARCH_DEFAULT_32BIT);
}

ArchSpec
ArchSpec::GetDefaultArch64(lldb::ArchitectureType arch_type)
{
    return ArchSpec(arch_type, LLDB_ARCH_DEFAULT_64BIT);
}

//===----------------------------------------------------------------------===//
// Mutators.

bool
ArchSpec::SetTriple (const llvm::Triple &triple)
{
    ArchitectureType arch_type = GetTripleArchitectureType(triple);

    if ((m_def = FindArchDefinition(arch_type)) == NULL)
        return false;

    // FIXME: Triples can contain endian and ABI info too (little endian armv5
    // using the GNU EABI with uClibc runtime would be written
    // "armv5l-unknown-linux-uclibceabi" for example).  Consider teaching
    // llvm::Triple to parse these types of specifications completely.
    m_entry = FindArchDefinitionEntry(m_def, triple.getArchName());
    m_triple = triple;

    // Can't call ArchUpdated here or we would reset the triple.
    m_flags = 0;
    if (m_entry != NULL)
        m_byte_order = m_entry->byte_order;
}

bool
ArchSpec::SetArch (llvm::StringRef arch_name)
{
    if (arch_name.empty())
        return false;

    // All default architecture names start with LLDB_ARCH_DEFAULT.
    if (arch_name.startswith (LLDB_ARCH_DEFAULT))
    {
        const ArchSpec *host_spec;

        // Special case for the current host default architectures...
        if (arch_name.equals (LLDB_ARCH_DEFAULT_32BIT))
            host_spec = &Host::GetArchitecture (Host::eSystemDefaultArchitecture32);
        else if (arch_name.equals (LLDB_ARCH_DEFAULT_64BIT))
            host_spec = &Host::GetArchitecture (Host::eSystemDefaultArchitecture64);
        else
            host_spec = &Host::GetArchitecture (Host::eSystemDefaultArchitecture);

        if (host_spec->IsValid())
        {
            *this = *host_spec;
            return true;
        }
        else
            return false;
    }

    // Check for a simple match over the architecture name.
    if (m_def != NULL)
    {
        const ArchDefinitionEntry *entry = FindArchDefinitionEntry(m_def, arch_name);
        if (entry != NULL)
        {
            m_entry = entry;
            ArchUpdated();
            return true;
        }
    }

    // Check for a numeric cpu followed by an optional separator char and
    // numeric subtype.  This allows for support of new cpu type/subtypes
    // without having to have a recompiled debug core. Examples:
    //
    //   "macho-12-6"                  : armv6 on darwin
    //   "macho-0x0000000c-0x00000006" : likewise
    //
    size_t pos = arch_name.find(ARCH_SPEC_SEPARATOR_CHAR);
    llvm::StringRef prefix = arch_name.substr(0, pos);
    const ArchDefinition *def = FindArchDefinition(arch_name);
    if (def == NULL)
        return false;

    // If all we have is a prefix do not worry about the cpu type and subtype.
    if (pos == llvm::StringRef::npos)
    {
        Clear();
        m_def = def;
        m_entry = NULL;
        ArchUpdated();
        return true;
    }

    // Parse the numeric cpu type and subtype encodings.
    size_t cpu_start = ++pos;
    size_t sub_start = arch_name.find(ARCH_SPEC_SEPARATOR_CHAR, cpu_start);
    sub_start += (sub_start == llvm::StringRef::npos) ? 0 : 1;

    llvm::StringRef cpu_number = arch_name.substr(cpu_start, sub_start);
    llvm::StringRef sub_number = arch_name.substr(sub_start);
    uint32_t cpu;
    uint32_t sub;

    if (!cpu_number.getAsInteger(0, cpu))
    {
        Clear();
        m_def = def;
        m_entry = NULL;
        ArchUpdated();
        return true;
    }

    if (!sub_number.getAsInteger(0, sub))
        sub = CPU_ANY;

    const ArchDefinitionEntry *entry = FindArchDefinitionEntry(def, cpu, sub);
    if (entry == NULL)
        return false;

    // Looks good.  Commit.
    Clear();
    m_def = def;
    m_entry = entry;
    ArchUpdated();
    return true;
}

bool
ArchSpec::SetArch (lldb::ArchitectureType arch_type, llvm::StringRef arch_name)
{
    const ArchDefinition *def;
    const ArchDefinitionEntry *entry;

    if ((def = FindArchDefinition(arch_type)) == NULL)
        return false;

    if ((entry = FindArchDefinitionEntry(def, arch_name)) == NULL)
        return false;

    Clear();
    m_def = def;
    m_entry = entry;
    ArchUpdated();
    return true;
}

bool
ArchSpec::SetArch (lldb::ArchitectureType arch_type, llvm::Triple::ArchType cpu)
{
    const ArchDefinition *def;
    const ArchDefinitionEntry *entry;

    if ((def = FindArchDefinition(arch_type)) == NULL)
        return false;

    if ((entry = FindArchDefinitionEntry(def, cpu, CPU_ANY)) == NULL)
        return false;

    Clear();
    m_def = def;
    m_entry = entry;
    ArchUpdated();
    return true;
}

bool
ArchSpec::SetCPUSubtype (uint32_t sub)
{
    if (m_def == NULL || m_entry == NULL)
        return false;

    uint32_t cpu = m_entry->cpu;
    const ArchDefinitionEntry *entry = FindArchDefinitionEntry(m_def, cpu, sub);

    if (entry == NULL)
        return false;

    // FIXME: Update the target triple.
    m_entry = entry;
    m_byte_order = entry->byte_order;
    return true;
}

bool
ArchSpec::SetByteOrder (lldb::ByteOrder byte_order)
{
    if (m_entry == NULL)
        return false;

    m_byte_order = byte_order;
    return true;
}

//===----------------------------------------------------------------------===//
// Helper methods.

void
ArchSpec::ArchUpdated ()
{
    if (m_def == NULL || m_entry == NULL)
        return;

    if (m_def->type == eArchTypeMachO)
        m_triple = llvm::Triple(m_entry->name, "apple", "darwin");
    else
        m_triple = llvm::Triple(m_entry->name, "unknown", "unknown");

    m_byte_order = m_entry->byte_order;
    m_flags = 0;
}

//===----------------------------------------------------------------------===//
// Operators.

bool
lldb_private::operator== (const ArchSpec& lhs, const ArchSpec& rhs)
{
    uint32_t lhs_cpu = lhs.GetCPUType();
    uint32_t rhs_cpu = rhs.GetCPUType();

    if (lhs_cpu == CPU_ANY || rhs_cpu == CPU_ANY)
        return true;

    else if (lhs_cpu == rhs_cpu)
    {
        uint32_t lhs_subtype = lhs.GetCPUSubtype();
        uint32_t rhs_subtype = rhs.GetCPUSubtype();
        if (lhs_subtype == CPU_ANY || rhs_subtype == CPU_ANY)
            return true;
        return lhs_subtype == rhs_subtype;
    }
    return false;
}

bool
lldb_private::operator!= (const ArchSpec& lhs, const ArchSpec& rhs)
{
    return !(lhs == rhs);
}

bool
lldb_private::operator<(const ArchSpec& lhs, const ArchSpec& rhs)
{
    uint32_t lhs_cpu = lhs.GetCPUType();
    uint32_t rhs_cpu = rhs.GetCPUType();

    if (lhs_cpu == rhs_cpu)
        return lhs.GetCPUSubtype() < rhs.GetCPUSubtype();

    return lhs_cpu < rhs_cpu;
}
