summaryrefslogtreecommitdiff
path: root/contrib/llvm-project/lld/ELF/Arch/MipsArchTree.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/lld/ELF/Arch/MipsArchTree.cpp')
-rw-r--r--contrib/llvm-project/lld/ELF/Arch/MipsArchTree.cpp407
1 files changed, 407 insertions, 0 deletions
diff --git a/contrib/llvm-project/lld/ELF/Arch/MipsArchTree.cpp b/contrib/llvm-project/lld/ELF/Arch/MipsArchTree.cpp
new file mode 100644
index 000000000000..20e07e1114eb
--- /dev/null
+++ b/contrib/llvm-project/lld/ELF/Arch/MipsArchTree.cpp
@@ -0,0 +1,407 @@
+//===- MipsArchTree.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
+//
+//===---------------------------------------------------------------------===//
+//
+// This file contains a helper function for the Writer.
+//
+//===---------------------------------------------------------------------===//
+
+#include "InputFiles.h"
+#include "SymbolTable.h"
+#include "Writer.h"
+
+#include "lld/Common/ErrorHandler.h"
+#include "llvm/BinaryFormat/ELF.h"
+#include "llvm/Object/ELF.h"
+#include "llvm/Support/MipsABIFlags.h"
+
+using namespace llvm;
+using namespace llvm::object;
+using namespace llvm::ELF;
+
+using namespace lld;
+using namespace lld::elf;
+
+namespace {
+struct ArchTreeEdge {
+ uint32_t child;
+ uint32_t parent;
+};
+
+struct FileFlags {
+ InputFile *file;
+ uint32_t flags;
+};
+} // namespace
+
+static StringRef getAbiName(uint32_t flags) {
+ switch (flags) {
+ case 0:
+ return "n64";
+ case EF_MIPS_ABI2:
+ return "n32";
+ case EF_MIPS_ABI_O32:
+ return "o32";
+ case EF_MIPS_ABI_O64:
+ return "o64";
+ case EF_MIPS_ABI_EABI32:
+ return "eabi32";
+ case EF_MIPS_ABI_EABI64:
+ return "eabi64";
+ default:
+ return "unknown";
+ }
+}
+
+static StringRef getNanName(bool isNan2008) {
+ return isNan2008 ? "2008" : "legacy";
+}
+
+static StringRef getFpName(bool isFp64) { return isFp64 ? "64" : "32"; }
+
+static void checkFlags(ArrayRef<FileFlags> files) {
+ assert(!files.empty() && "expected non-empty file list");
+
+ uint32_t abi = files[0].flags & (EF_MIPS_ABI | EF_MIPS_ABI2);
+ bool nan = files[0].flags & EF_MIPS_NAN2008;
+ bool fp = files[0].flags & EF_MIPS_FP64;
+
+ for (const FileFlags &f : files) {
+ if (config->is64 && f.flags & EF_MIPS_MICROMIPS)
+ error(toString(f.file) + ": microMIPS 64-bit is not supported");
+
+ uint32_t abi2 = f.flags & (EF_MIPS_ABI | EF_MIPS_ABI2);
+ if (abi != abi2)
+ error(toString(f.file) + ": ABI '" + getAbiName(abi2) +
+ "' is incompatible with target ABI '" + getAbiName(abi) + "'");
+
+ bool nan2 = f.flags & EF_MIPS_NAN2008;
+ if (nan != nan2)
+ error(toString(f.file) + ": -mnan=" + getNanName(nan2) +
+ " is incompatible with target -mnan=" + getNanName(nan));
+
+ bool fp2 = f.flags & EF_MIPS_FP64;
+ if (fp != fp2)
+ error(toString(f.file) + ": -mfp" + getFpName(fp2) +
+ " is incompatible with target -mfp" + getFpName(fp));
+ }
+}
+
+static uint32_t getMiscFlags(ArrayRef<FileFlags> files) {
+ uint32_t ret = 0;
+ for (const FileFlags &f : files)
+ ret |= f.flags &
+ (EF_MIPS_ABI | EF_MIPS_ABI2 | EF_MIPS_ARCH_ASE | EF_MIPS_NOREORDER |
+ EF_MIPS_MICROMIPS | EF_MIPS_NAN2008 | EF_MIPS_32BITMODE);
+ return ret;
+}
+
+static uint32_t getPicFlags(ArrayRef<FileFlags> files) {
+ // Check PIC/non-PIC compatibility.
+ bool isPic = files[0].flags & (EF_MIPS_PIC | EF_MIPS_CPIC);
+ for (const FileFlags &f : files.slice(1)) {
+ bool isPic2 = f.flags & (EF_MIPS_PIC | EF_MIPS_CPIC);
+ if (isPic && !isPic2)
+ warn(toString(f.file) +
+ ": linking non-abicalls code with abicalls code " +
+ toString(files[0].file));
+ if (!isPic && isPic2)
+ warn(toString(f.file) +
+ ": linking abicalls code with non-abicalls code " +
+ toString(files[0].file));
+ }
+
+ // Compute the result PIC/non-PIC flag.
+ uint32_t ret = files[0].flags & (EF_MIPS_PIC | EF_MIPS_CPIC);
+ for (const FileFlags &f : files.slice(1))
+ ret &= f.flags & (EF_MIPS_PIC | EF_MIPS_CPIC);
+
+ // PIC code is inherently CPIC and may not set CPIC flag explicitly.
+ if (ret & EF_MIPS_PIC)
+ ret |= EF_MIPS_CPIC;
+ return ret;
+}
+
+static ArchTreeEdge archTree[] = {
+ // MIPS32R6 and MIPS64R6 are not compatible with other extensions
+ // MIPS64R2 extensions.
+ {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON3, EF_MIPS_ARCH_64R2},
+ {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON2, EF_MIPS_ARCH_64R2},
+ {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON, EF_MIPS_ARCH_64R2},
+ {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_LS3A, EF_MIPS_ARCH_64R2},
+ // MIPS64 extensions.
+ {EF_MIPS_ARCH_64 | EF_MIPS_MACH_SB1, EF_MIPS_ARCH_64},
+ {EF_MIPS_ARCH_64 | EF_MIPS_MACH_XLR, EF_MIPS_ARCH_64},
+ {EF_MIPS_ARCH_64R2, EF_MIPS_ARCH_64},
+ // MIPS V extensions.
+ {EF_MIPS_ARCH_64, EF_MIPS_ARCH_5},
+ // R5000 extensions.
+ {EF_MIPS_ARCH_4 | EF_MIPS_MACH_5500, EF_MIPS_ARCH_4 | EF_MIPS_MACH_5400},
+ // MIPS IV extensions.
+ {EF_MIPS_ARCH_4 | EF_MIPS_MACH_5400, EF_MIPS_ARCH_4},
+ {EF_MIPS_ARCH_4 | EF_MIPS_MACH_9000, EF_MIPS_ARCH_4},
+ {EF_MIPS_ARCH_5, EF_MIPS_ARCH_4},
+ // VR4100 extensions.
+ {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4111, EF_MIPS_ARCH_3 | EF_MIPS_MACH_4100},
+ {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4120, EF_MIPS_ARCH_3 | EF_MIPS_MACH_4100},
+ // MIPS III extensions.
+ {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4010, EF_MIPS_ARCH_3},
+ {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4100, EF_MIPS_ARCH_3},
+ {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4650, EF_MIPS_ARCH_3},
+ {EF_MIPS_ARCH_3 | EF_MIPS_MACH_5900, EF_MIPS_ARCH_3},
+ {EF_MIPS_ARCH_3 | EF_MIPS_MACH_LS2E, EF_MIPS_ARCH_3},
+ {EF_MIPS_ARCH_3 | EF_MIPS_MACH_LS2F, EF_MIPS_ARCH_3},
+ {EF_MIPS_ARCH_4, EF_MIPS_ARCH_3},
+ // MIPS32 extensions.
+ {EF_MIPS_ARCH_32R2, EF_MIPS_ARCH_32},
+ // MIPS II extensions.
+ {EF_MIPS_ARCH_3, EF_MIPS_ARCH_2},
+ {EF_MIPS_ARCH_32, EF_MIPS_ARCH_2},
+ // MIPS I extensions.
+ {EF_MIPS_ARCH_1 | EF_MIPS_MACH_3900, EF_MIPS_ARCH_1},
+ {EF_MIPS_ARCH_2, EF_MIPS_ARCH_1},
+};
+
+static bool isArchMatched(uint32_t New, uint32_t res) {
+ if (New == res)
+ return true;
+ if (New == EF_MIPS_ARCH_32 && isArchMatched(EF_MIPS_ARCH_64, res))
+ return true;
+ if (New == EF_MIPS_ARCH_32R2 && isArchMatched(EF_MIPS_ARCH_64R2, res))
+ return true;
+ for (const auto &edge : archTree) {
+ if (res == edge.child) {
+ res = edge.parent;
+ if (res == New)
+ return true;
+ }
+ }
+ return false;
+}
+
+static StringRef getMachName(uint32_t flags) {
+ switch (flags & EF_MIPS_MACH) {
+ case EF_MIPS_MACH_NONE:
+ return "";
+ case EF_MIPS_MACH_3900:
+ return "r3900";
+ case EF_MIPS_MACH_4010:
+ return "r4010";
+ case EF_MIPS_MACH_4100:
+ return "r4100";
+ case EF_MIPS_MACH_4650:
+ return "r4650";
+ case EF_MIPS_MACH_4120:
+ return "r4120";
+ case EF_MIPS_MACH_4111:
+ return "r4111";
+ case EF_MIPS_MACH_5400:
+ return "vr5400";
+ case EF_MIPS_MACH_5900:
+ return "vr5900";
+ case EF_MIPS_MACH_5500:
+ return "vr5500";
+ case EF_MIPS_MACH_9000:
+ return "rm9000";
+ case EF_MIPS_MACH_LS2E:
+ return "loongson2e";
+ case EF_MIPS_MACH_LS2F:
+ return "loongson2f";
+ case EF_MIPS_MACH_LS3A:
+ return "loongson3a";
+ case EF_MIPS_MACH_OCTEON:
+ return "octeon";
+ case EF_MIPS_MACH_OCTEON2:
+ return "octeon2";
+ case EF_MIPS_MACH_OCTEON3:
+ return "octeon3";
+ case EF_MIPS_MACH_SB1:
+ return "sb1";
+ case EF_MIPS_MACH_XLR:
+ return "xlr";
+ default:
+ return "unknown machine";
+ }
+}
+
+static StringRef getArchName(uint32_t flags) {
+ switch (flags & EF_MIPS_ARCH) {
+ case EF_MIPS_ARCH_1:
+ return "mips1";
+ case EF_MIPS_ARCH_2:
+ return "mips2";
+ case EF_MIPS_ARCH_3:
+ return "mips3";
+ case EF_MIPS_ARCH_4:
+ return "mips4";
+ case EF_MIPS_ARCH_5:
+ return "mips5";
+ case EF_MIPS_ARCH_32:
+ return "mips32";
+ case EF_MIPS_ARCH_64:
+ return "mips64";
+ case EF_MIPS_ARCH_32R2:
+ return "mips32r2";
+ case EF_MIPS_ARCH_64R2:
+ return "mips64r2";
+ case EF_MIPS_ARCH_32R6:
+ return "mips32r6";
+ case EF_MIPS_ARCH_64R6:
+ return "mips64r6";
+ default:
+ return "unknown arch";
+ }
+}
+
+static std::string getFullArchName(uint32_t flags) {
+ StringRef arch = getArchName(flags);
+ StringRef mach = getMachName(flags);
+ if (mach.empty())
+ return arch.str();
+ return (arch + " (" + mach + ")").str();
+}
+
+// There are (arguably too) many MIPS ISAs out there. Their relationships
+// can be represented as a forest. If all input files have ISAs which
+// reachable by repeated proceeding from the single child to the parent,
+// these input files are compatible. In that case we need to return "highest"
+// ISA. If there are incompatible input files, we show an error.
+// For example, mips1 is a "parent" of mips2 and such files are compatible.
+// Output file gets EF_MIPS_ARCH_2 flag. From the other side mips3 and mips32
+// are incompatible because nor mips3 is a parent for misp32, nor mips32
+// is a parent for mips3.
+static uint32_t getArchFlags(ArrayRef<FileFlags> files) {
+ uint32_t ret = files[0].flags & (EF_MIPS_ARCH | EF_MIPS_MACH);
+
+ for (const FileFlags &f : files.slice(1)) {
+ uint32_t New = f.flags & (EF_MIPS_ARCH | EF_MIPS_MACH);
+
+ // Check ISA compatibility.
+ if (isArchMatched(New, ret))
+ continue;
+ if (!isArchMatched(ret, New)) {
+ error("incompatible target ISA:\n>>> " + toString(files[0].file) + ": " +
+ getFullArchName(ret) + "\n>>> " + toString(f.file) + ": " +
+ getFullArchName(New));
+ return 0;
+ }
+ ret = New;
+ }
+ return ret;
+}
+
+// If we don't have any input files, we'll have to rely on the information we
+// can derive from emulation information, since this at least gets us ABI.
+static uint32_t getFlagsFromEmulation() {
+ uint32_t ret = 0;
+
+ if (config->emulation.empty())
+ return 0;
+
+ if (config->ekind == ELF32BEKind || config->ekind == ELF32LEKind) {
+ if (config->mipsN32Abi)
+ ret |= EF_MIPS_ABI2;
+ else
+ ret |= EF_MIPS_ABI_O32;
+ }
+
+ return ret;
+}
+
+template <class ELFT> uint32_t elf::calcMipsEFlags() {
+ std::vector<FileFlags> v;
+ for (InputFile *f : objectFiles)
+ v.push_back({f, cast<ObjFile<ELFT>>(f)->getObj().getHeader()->e_flags});
+ if (v.empty())
+ return getFlagsFromEmulation();
+ checkFlags(v);
+ return getMiscFlags(v) | getPicFlags(v) | getArchFlags(v);
+}
+
+static int compareMipsFpAbi(uint8_t fpA, uint8_t fpB) {
+ if (fpA == fpB)
+ return 0;
+ if (fpB == Mips::Val_GNU_MIPS_ABI_FP_ANY)
+ return 1;
+ if (fpB == Mips::Val_GNU_MIPS_ABI_FP_64A &&
+ fpA == Mips::Val_GNU_MIPS_ABI_FP_64)
+ return 1;
+ if (fpB != Mips::Val_GNU_MIPS_ABI_FP_XX)
+ return -1;
+ if (fpA == Mips::Val_GNU_MIPS_ABI_FP_DOUBLE ||
+ fpA == Mips::Val_GNU_MIPS_ABI_FP_64 ||
+ fpA == Mips::Val_GNU_MIPS_ABI_FP_64A)
+ return 1;
+ return -1;
+}
+
+static StringRef getMipsFpAbiName(uint8_t fpAbi) {
+ switch (fpAbi) {
+ case Mips::Val_GNU_MIPS_ABI_FP_ANY:
+ return "any";
+ case Mips::Val_GNU_MIPS_ABI_FP_DOUBLE:
+ return "-mdouble-float";
+ case Mips::Val_GNU_MIPS_ABI_FP_SINGLE:
+ return "-msingle-float";
+ case Mips::Val_GNU_MIPS_ABI_FP_SOFT:
+ return "-msoft-float";
+ case Mips::Val_GNU_MIPS_ABI_FP_OLD_64:
+ return "-mgp32 -mfp64 (old)";
+ case Mips::Val_GNU_MIPS_ABI_FP_XX:
+ return "-mfpxx";
+ case Mips::Val_GNU_MIPS_ABI_FP_64:
+ return "-mgp32 -mfp64";
+ case Mips::Val_GNU_MIPS_ABI_FP_64A:
+ return "-mgp32 -mfp64 -mno-odd-spreg";
+ default:
+ return "unknown";
+ }
+}
+
+uint8_t elf::getMipsFpAbiFlag(uint8_t oldFlag, uint8_t newFlag,
+ StringRef fileName) {
+ if (compareMipsFpAbi(newFlag, oldFlag) >= 0)
+ return newFlag;
+ if (compareMipsFpAbi(oldFlag, newFlag) < 0)
+ error(fileName + ": floating point ABI '" + getMipsFpAbiName(newFlag) +
+ "' is incompatible with target floating point ABI '" +
+ getMipsFpAbiName(oldFlag) + "'");
+ return oldFlag;
+}
+
+template <class ELFT> static bool isN32Abi(const InputFile *f) {
+ if (auto *ef = dyn_cast<ELFFileBase>(f))
+ return ef->template getObj<ELFT>().getHeader()->e_flags & EF_MIPS_ABI2;
+ return false;
+}
+
+bool elf::isMipsN32Abi(const InputFile *f) {
+ switch (config->ekind) {
+ case ELF32LEKind:
+ return isN32Abi<ELF32LE>(f);
+ case ELF32BEKind:
+ return isN32Abi<ELF32BE>(f);
+ case ELF64LEKind:
+ return isN32Abi<ELF64LE>(f);
+ case ELF64BEKind:
+ return isN32Abi<ELF64BE>(f);
+ default:
+ llvm_unreachable("unknown Config->EKind");
+ }
+}
+
+bool elf::isMicroMips() { return config->eflags & EF_MIPS_MICROMIPS; }
+
+bool elf::isMipsR6() {
+ uint32_t arch = config->eflags & EF_MIPS_ARCH;
+ return arch == EF_MIPS_ARCH_32R6 || arch == EF_MIPS_ARCH_64R6;
+}
+
+template uint32_t elf::calcMipsEFlags<ELF32LE>();
+template uint32_t elf::calcMipsEFlags<ELF32BE>();
+template uint32_t elf::calcMipsEFlags<ELF64LE>();
+template uint32_t elf::calcMipsEFlags<ELF64BE>();