blob: ff17284ea5455d60e04bc78abad0e6c8da3f1c33 [file] [log] [blame]
//===- MipsAbiFlags.cpp ---------------------------------------------------===//
//
// The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "MipsAbiFlags.h"
#include "mcld/Fragment/RegionFragment.h"
#include "mcld/LD/LDSection.h"
#include "mcld/LD/SectionData.h"
#include "mcld/MC/Input.h"
#include "mcld/Support/MsgHandling.h"
#include <llvm/Support/Casting.h>
#include <llvm/Support/MipsABIFlags.h>
namespace mcld {
// SHT_MIPS_ABIFLAGS has the same format for both 32/64-bit targets.
// We do not support linking of big-endian code now so 32-bit LE
// combination is Okay.
typedef llvm::object::ELFType<llvm::support::little, false> ELF32LE;
typedef llvm::object::Elf_Mips_ABIFlags<ELF32LE> ElfMipsAbiFlags;
uint64_t MipsAbiFlags::size() {
return sizeof(ElfMipsAbiFlags);
}
uint64_t MipsAbiFlags::emit(const MipsAbiFlags& pInfo, MemoryRegion& pRegion) {
auto* buf = reinterpret_cast<ElfMipsAbiFlags*>(pRegion.begin());
buf->version = 0;
buf->isa_level = pInfo.m_IsaLevel;
buf->isa_rev = pInfo.m_IsaRev;
buf->gpr_size = pInfo.m_GprSize;
buf->cpr1_size = pInfo.m_Cpr1Size;
buf->cpr2_size = pInfo.m_Cpr2Size;
buf->fp_abi = pInfo.m_FpAbi;
buf->isa_ext = pInfo.m_IsaExt;
buf->ases = pInfo.m_Ases;
buf->flags1 = pInfo.m_Flags1;
buf->flags2 = 0;
return size();
}
bool MipsAbiFlags::fillBySection(const Input& pInput, const LDSection& pSection,
MipsAbiFlags& mipsAbi) {
assert(pSection.type() == llvm::ELF::SHT_MIPS_ABIFLAGS &&
"Unexpected section type");
if (pSection.size() != size()) {
error(diag::error_Mips_abiflags_invalid_size) << pInput.name();
return false;
}
const SectionData* secData = pSection.getSectionData();
if (secData->size() != 2 || !llvm::isa<RegionFragment>(secData->front())) {
error(diag::error_Mips_abiflags_invalid_size) << pInput.name();
return false;
}
const auto& frag = llvm::cast<RegionFragment>(secData->front());
auto* data =
reinterpret_cast<const ElfMipsAbiFlags*>(frag.getRegion().data());
if (data->version != 0) {
error(diag::error_Mips_abiflags_invalid_version) << int(data->version)
<< pInput.name();
return false;
}
mipsAbi.m_IsaLevel = data->isa_level;
mipsAbi.m_IsaRev = data->isa_rev;
mipsAbi.m_GprSize = data->gpr_size;
mipsAbi.m_Cpr1Size = data->cpr1_size;
mipsAbi.m_Cpr2Size = data->cpr2_size;
mipsAbi.m_FpAbi = data->fp_abi;
mipsAbi.m_IsaExt = data->isa_ext;
mipsAbi.m_Ases = data->ases;
mipsAbi.m_Flags1 = data->flags1;
return true;
}
static unsigned getIsaLevel(uint64_t flags) {
switch (flags & llvm::ELF::EF_MIPS_ARCH) {
case llvm::ELF::EF_MIPS_ARCH_1:
return 1;
case llvm::ELF::EF_MIPS_ARCH_2:
return 2;
case llvm::ELF::EF_MIPS_ARCH_3:
return 3;
case llvm::ELF::EF_MIPS_ARCH_4:
return 4;
case llvm::ELF::EF_MIPS_ARCH_5:
return 5;
case llvm::ELF::EF_MIPS_ARCH_32:
case llvm::ELF::EF_MIPS_ARCH_32R2:
case llvm::ELF::EF_MIPS_ARCH_32R6:
return 32;
case llvm::ELF::EF_MIPS_ARCH_64:
case llvm::ELF::EF_MIPS_ARCH_64R2:
case llvm::ELF::EF_MIPS_ARCH_64R6:
return 64;
default:
// We check ELF flags and show error in case
// of unknown value in other place.
llvm_unreachable("Unknown MIPS architecture flag");
}
}
static unsigned getIsaRev(uint64_t flags) {
switch (flags & llvm::ELF::EF_MIPS_ARCH) {
case llvm::ELF::EF_MIPS_ARCH_1:
case llvm::ELF::EF_MIPS_ARCH_2:
case llvm::ELF::EF_MIPS_ARCH_3:
case llvm::ELF::EF_MIPS_ARCH_4:
case llvm::ELF::EF_MIPS_ARCH_5:
return 0;
case llvm::ELF::EF_MIPS_ARCH_32:
case llvm::ELF::EF_MIPS_ARCH_64:
return 1;
case llvm::ELF::EF_MIPS_ARCH_32R2:
case llvm::ELF::EF_MIPS_ARCH_64R2:
return 2;
case llvm::ELF::EF_MIPS_ARCH_32R6:
case llvm::ELF::EF_MIPS_ARCH_64R6:
return 6;
default:
// We check ELF flags and show error in case
// of unknown value in other place.
llvm_unreachable("Unknown MIPS architecture flag");
}
}
static unsigned getIsaExt(uint64_t flags) {
switch (flags & llvm::ELF::EF_MIPS_MACH) {
case 0:
return llvm::Mips::AFL_EXT_NONE;
case llvm::ELF::EF_MIPS_MACH_3900: return llvm::Mips::AFL_EXT_3900;
case llvm::ELF::EF_MIPS_MACH_4010: return llvm::Mips::AFL_EXT_4010;
case llvm::ELF::EF_MIPS_MACH_4100: return llvm::Mips::AFL_EXT_4010;
case llvm::ELF::EF_MIPS_MACH_4111: return llvm::Mips::AFL_EXT_4111;
case llvm::ELF::EF_MIPS_MACH_4120: return llvm::Mips::AFL_EXT_4120;
case llvm::ELF::EF_MIPS_MACH_4650: return llvm::Mips::AFL_EXT_4650;
case llvm::ELF::EF_MIPS_MACH_5400: return llvm::Mips::AFL_EXT_5400;
case llvm::ELF::EF_MIPS_MACH_5500: return llvm::Mips::AFL_EXT_5500;
case llvm::ELF::EF_MIPS_MACH_5900: return llvm::Mips::AFL_EXT_5900;
case llvm::ELF::EF_MIPS_MACH_SB1: return llvm::Mips::AFL_EXT_SB1;
case llvm::ELF::EF_MIPS_MACH_LS2E: return llvm::Mips::AFL_EXT_LOONGSON_2E;
case llvm::ELF::EF_MIPS_MACH_LS2F: return llvm::Mips::AFL_EXT_LOONGSON_2F;
case llvm::ELF::EF_MIPS_MACH_LS3A: return llvm::Mips::AFL_EXT_LOONGSON_3A;
case llvm::ELF::EF_MIPS_MACH_OCTEON3: return llvm::Mips::AFL_EXT_OCTEON3;
case llvm::ELF::EF_MIPS_MACH_OCTEON2: return llvm::Mips::AFL_EXT_OCTEON2;
case llvm::ELF::EF_MIPS_MACH_OCTEON: return llvm::Mips::AFL_EXT_OCTEON;
case llvm::ELF::EF_MIPS_MACH_XLR: return llvm::Mips::AFL_EXT_XLR;
default:
// We check ELF flags and show error in case
// of unknown value in other place.
llvm_unreachable("Unknown MIPS extension flag");
}
}
static bool is32BitElfFlags(uint64_t flags) {
if (flags & llvm::ELF::EF_MIPS_32BITMODE)
return true;
uint64_t arch = flags & llvm::ELF::EF_MIPS_ARCH;
if (arch == llvm::ELF::EF_MIPS_ARCH_1 ||
arch == llvm::ELF::EF_MIPS_ARCH_2 ||
arch == llvm::ELF::EF_MIPS_ARCH_32 ||
arch == llvm::ELF::EF_MIPS_ARCH_32R2 ||
arch == llvm::ELF::EF_MIPS_ARCH_32R6)
return true;
uint64_t abi = flags & llvm::ELF::EF_MIPS_ABI;
if (abi == llvm::ELF::EF_MIPS_ABI_O32 || abi == llvm::ELF::EF_MIPS_ABI_EABI32)
return true;
return false;
}
bool MipsAbiFlags::fillByElfFlags(const Input& pInput, uint64_t elfFlags,
MipsAbiFlags& mipsAbi) {
mipsAbi.m_IsaLevel = getIsaLevel(elfFlags);
mipsAbi.m_IsaRev = getIsaRev(elfFlags);
mipsAbi.m_IsaExt = getIsaExt(elfFlags);
mipsAbi.m_GprSize = is32BitElfFlags(elfFlags) ?
llvm::Mips::AFL_REG_32 : llvm::Mips::AFL_REG_64;
mipsAbi.m_Cpr1Size = llvm::Mips::AFL_REG_NONE;
mipsAbi.m_Cpr2Size = llvm::Mips::AFL_REG_NONE;
mipsAbi.m_FpAbi = llvm::Mips::Val_GNU_MIPS_ABI_FP_ANY;
mipsAbi.m_Ases = 0;
if (elfFlags & llvm::ELF::EF_MIPS_MICROMIPS)
mipsAbi.m_Ases |= llvm::Mips::AFL_ASE_MICROMIPS;
if (elfFlags & llvm::ELF::EF_MIPS_ARCH_ASE_M16)
mipsAbi.m_Ases |= llvm::Mips::AFL_ASE_MIPS16;
if (elfFlags & llvm::ELF::EF_MIPS_ARCH_ASE_MDMX)
mipsAbi.m_Ases |= llvm::Mips::AFL_ASE_MDMX;
mipsAbi.m_Flags1 = 0;
return true;
}
bool MipsAbiFlags::isCompatible(const Input& pInput, const MipsAbiFlags& elf,
const MipsAbiFlags& abi) {
unsigned isaRev = abi.m_IsaRev;
if (isaRev == 3 || isaRev == 5)
isaRev = 2;
if (abi.m_IsaLevel != elf.m_IsaLevel || isaRev != elf.m_IsaRev) {
warning(diag::warn_Mips_isa_incompatible) << pInput.name();
return false;
}
if (abi.m_IsaExt != elf.m_IsaExt) {
warning(diag::warn_Mips_isa_ext_incompatible) << pInput.name();
return false;
}
if ((abi.m_Ases & elf.m_Ases) != elf.m_Ases) {
warning(diag::warn_Mips_ases_incompatible) << pInput.name();
return false;
}
return true;
}
static bool isFpGreater(uint64_t fpA, uint64_t fpB) {
if (fpB == llvm::Mips::Val_GNU_MIPS_ABI_FP_ANY)
return true;
if (fpB == llvm::Mips::Val_GNU_MIPS_ABI_FP_64A &&
fpA == llvm::Mips::Val_GNU_MIPS_ABI_FP_64)
return true;
if (fpB != llvm::Mips::Val_GNU_MIPS_ABI_FP_XX)
return false;
return fpA == llvm::Mips::Val_GNU_MIPS_ABI_FP_DOUBLE ||
fpA == llvm::Mips::Val_GNU_MIPS_ABI_FP_64 ||
fpA == llvm::Mips::Val_GNU_MIPS_ABI_FP_64A;
}
static llvm::StringRef getFpAbiName(uint64_t abi) {
switch (abi) {
case llvm::Mips::Val_GNU_MIPS_ABI_FP_ANY:
return "<any>";
case llvm::Mips::Val_GNU_MIPS_ABI_FP_DOUBLE:
return "-mdouble-float";
case llvm::Mips::Val_GNU_MIPS_ABI_FP_SINGLE:
return "-msingle-float";
case llvm::Mips::Val_GNU_MIPS_ABI_FP_SOFT:
return "-msoft-float";
case llvm::Mips::Val_GNU_MIPS_ABI_FP_OLD_64:
return "-mips32r2 -mfp64 (old)";
case llvm::Mips::Val_GNU_MIPS_ABI_FP_XX:
return "-mfpxx";
case llvm::Mips::Val_GNU_MIPS_ABI_FP_64:
return "-mgp32 -mfp64";
case llvm::Mips::Val_GNU_MIPS_ABI_FP_64A:
return "-mgp32 -mfp64 -mno-odd-spreg";
default:
return "<unknown>";
}
}
bool MipsAbiFlags::merge(const Input& pInput, MipsAbiFlags& oldFlags,
const MipsAbiFlags& newFlags) {
if (oldFlags.m_IsaLevel == 0) {
oldFlags = newFlags;
return true;
}
if (newFlags.m_IsaLevel > oldFlags.m_IsaLevel)
oldFlags.m_IsaLevel = newFlags.m_IsaLevel;
oldFlags.m_IsaRev = std::max(oldFlags.m_IsaRev, newFlags.m_IsaRev);
oldFlags.m_GprSize = std::max(oldFlags.m_GprSize, newFlags.m_GprSize);
oldFlags.m_Cpr1Size = std::max(oldFlags.m_Cpr1Size, newFlags.m_Cpr1Size);
oldFlags.m_Cpr2Size = std::max(oldFlags.m_Cpr2Size, newFlags.m_Cpr2Size);
oldFlags.m_Ases |= newFlags.m_Ases;
oldFlags.m_Flags1 |= newFlags.m_Flags1;
if (oldFlags.m_FpAbi == newFlags.m_FpAbi)
return true;
if (isFpGreater(newFlags.m_FpAbi, oldFlags.m_FpAbi)) {
oldFlags.m_FpAbi = newFlags.m_FpAbi;
return true;
}
if (isFpGreater(oldFlags.m_FpAbi, newFlags.m_FpAbi))
return true;
llvm::StringRef oldAbiName = getFpAbiName(oldFlags.m_FpAbi);
llvm::StringRef newAbiName = getFpAbiName(newFlags.m_FpAbi);
warning(diag::warn_Mips_fp_abi_incompatible) << oldAbiName << newAbiName
<< pInput.name();
return false;
}
} // namespace mcld