Path: blob/main/contrib/llvm-project/lld/ELF/Arch/MipsArchTree.cpp
34878 views
//===- MipsArchTree.cpp --------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===---------------------------------------------------------------------===//7//8// This file contains a helper function for the Writer.9//10//===---------------------------------------------------------------------===//1112#include "InputFiles.h"13#include "SymbolTable.h"14#include "Writer.h"1516#include "lld/Common/ErrorHandler.h"17#include "llvm/BinaryFormat/ELF.h"18#include "llvm/Support/MipsABIFlags.h"1920using namespace llvm;21using namespace llvm::object;22using namespace llvm::ELF;2324using namespace lld;25using namespace lld::elf;2627namespace {28struct ArchTreeEdge {29uint32_t child;30uint32_t parent;31};3233struct FileFlags {34InputFile *file;35uint32_t flags;36};37} // namespace3839static StringRef getAbiName(uint32_t flags) {40switch (flags) {41case 0:42return "n64";43case EF_MIPS_ABI2:44return "n32";45case EF_MIPS_ABI_O32:46return "o32";47case EF_MIPS_ABI_O64:48return "o64";49case EF_MIPS_ABI_EABI32:50return "eabi32";51case EF_MIPS_ABI_EABI64:52return "eabi64";53default:54return "unknown";55}56}5758static StringRef getNanName(bool isNan2008) {59return isNan2008 ? "2008" : "legacy";60}6162static StringRef getFpName(bool isFp64) { return isFp64 ? "64" : "32"; }6364static void checkFlags(ArrayRef<FileFlags> files) {65assert(!files.empty() && "expected non-empty file list");6667uint32_t abi = files[0].flags & (EF_MIPS_ABI | EF_MIPS_ABI2);68bool nan = files[0].flags & EF_MIPS_NAN2008;69bool fp = files[0].flags & EF_MIPS_FP64;7071for (const FileFlags &f : files) {72if (config->is64 && f.flags & EF_MIPS_MICROMIPS)73error(toString(f.file) + ": microMIPS 64-bit is not supported");7475uint32_t abi2 = f.flags & (EF_MIPS_ABI | EF_MIPS_ABI2);76if (abi != abi2)77error(toString(f.file) + ": ABI '" + getAbiName(abi2) +78"' is incompatible with target ABI '" + getAbiName(abi) + "'");7980bool nan2 = f.flags & EF_MIPS_NAN2008;81if (nan != nan2)82error(toString(f.file) + ": -mnan=" + getNanName(nan2) +83" is incompatible with target -mnan=" + getNanName(nan));8485bool fp2 = f.flags & EF_MIPS_FP64;86if (fp != fp2)87error(toString(f.file) + ": -mfp" + getFpName(fp2) +88" is incompatible with target -mfp" + getFpName(fp));89}90}9192static uint32_t getMiscFlags(ArrayRef<FileFlags> files) {93uint32_t ret = 0;94for (const FileFlags &f : files)95ret |= f.flags &96(EF_MIPS_ABI | EF_MIPS_ABI2 | EF_MIPS_ARCH_ASE | EF_MIPS_NOREORDER |97EF_MIPS_MICROMIPS | EF_MIPS_NAN2008 | EF_MIPS_32BITMODE);98return ret;99}100101static uint32_t getPicFlags(ArrayRef<FileFlags> files) {102// Check PIC/non-PIC compatibility.103bool isPic = files[0].flags & (EF_MIPS_PIC | EF_MIPS_CPIC);104for (const FileFlags &f : files.slice(1)) {105bool isPic2 = f.flags & (EF_MIPS_PIC | EF_MIPS_CPIC);106if (isPic && !isPic2)107warn(toString(f.file) +108": linking non-abicalls code with abicalls code " +109toString(files[0].file));110if (!isPic && isPic2)111warn(toString(f.file) +112": linking abicalls code with non-abicalls code " +113toString(files[0].file));114}115116// Compute the result PIC/non-PIC flag.117uint32_t ret = files[0].flags & (EF_MIPS_PIC | EF_MIPS_CPIC);118for (const FileFlags &f : files.slice(1))119ret &= f.flags & (EF_MIPS_PIC | EF_MIPS_CPIC);120121// PIC code is inherently CPIC and may not set CPIC flag explicitly.122if (ret & EF_MIPS_PIC)123ret |= EF_MIPS_CPIC;124return ret;125}126127static ArchTreeEdge archTree[] = {128// MIPS32R6 and MIPS64R6 are not compatible with other extensions129// MIPS64R2 extensions.130{EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON3, EF_MIPS_ARCH_64R2},131{EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON2, EF_MIPS_ARCH_64R2},132{EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON, EF_MIPS_ARCH_64R2},133{EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_LS3A, EF_MIPS_ARCH_64R2},134// MIPS64 extensions.135{EF_MIPS_ARCH_64 | EF_MIPS_MACH_SB1, EF_MIPS_ARCH_64},136{EF_MIPS_ARCH_64 | EF_MIPS_MACH_XLR, EF_MIPS_ARCH_64},137{EF_MIPS_ARCH_64R2, EF_MIPS_ARCH_64},138// MIPS V extensions.139{EF_MIPS_ARCH_64, EF_MIPS_ARCH_5},140// R5000 extensions.141{EF_MIPS_ARCH_4 | EF_MIPS_MACH_5500, EF_MIPS_ARCH_4 | EF_MIPS_MACH_5400},142// MIPS IV extensions.143{EF_MIPS_ARCH_4 | EF_MIPS_MACH_5400, EF_MIPS_ARCH_4},144{EF_MIPS_ARCH_4 | EF_MIPS_MACH_9000, EF_MIPS_ARCH_4},145{EF_MIPS_ARCH_5, EF_MIPS_ARCH_4},146// VR4100 extensions.147{EF_MIPS_ARCH_3 | EF_MIPS_MACH_4111, EF_MIPS_ARCH_3 | EF_MIPS_MACH_4100},148{EF_MIPS_ARCH_3 | EF_MIPS_MACH_4120, EF_MIPS_ARCH_3 | EF_MIPS_MACH_4100},149// MIPS III extensions.150{EF_MIPS_ARCH_3 | EF_MIPS_MACH_4010, EF_MIPS_ARCH_3},151{EF_MIPS_ARCH_3 | EF_MIPS_MACH_4100, EF_MIPS_ARCH_3},152{EF_MIPS_ARCH_3 | EF_MIPS_MACH_4650, EF_MIPS_ARCH_3},153{EF_MIPS_ARCH_3 | EF_MIPS_MACH_5900, EF_MIPS_ARCH_3},154{EF_MIPS_ARCH_3 | EF_MIPS_MACH_LS2E, EF_MIPS_ARCH_3},155{EF_MIPS_ARCH_3 | EF_MIPS_MACH_LS2F, EF_MIPS_ARCH_3},156{EF_MIPS_ARCH_4, EF_MIPS_ARCH_3},157// MIPS32 extensions.158{EF_MIPS_ARCH_32R2, EF_MIPS_ARCH_32},159// MIPS II extensions.160{EF_MIPS_ARCH_3, EF_MIPS_ARCH_2},161{EF_MIPS_ARCH_32, EF_MIPS_ARCH_2},162// MIPS I extensions.163{EF_MIPS_ARCH_1 | EF_MIPS_MACH_3900, EF_MIPS_ARCH_1},164{EF_MIPS_ARCH_2, EF_MIPS_ARCH_1},165};166167static bool isArchMatched(uint32_t newFlags, uint32_t res) {168if (newFlags == res)169return true;170if (newFlags == EF_MIPS_ARCH_32 && isArchMatched(EF_MIPS_ARCH_64, res))171return true;172if (newFlags == EF_MIPS_ARCH_32R2 && isArchMatched(EF_MIPS_ARCH_64R2, res))173return true;174for (const auto &edge : archTree) {175if (res == edge.child) {176res = edge.parent;177if (res == newFlags)178return true;179}180}181return false;182}183184static StringRef getMachName(uint32_t flags) {185switch (flags & EF_MIPS_MACH) {186case EF_MIPS_MACH_NONE:187return "";188case EF_MIPS_MACH_3900:189return "r3900";190case EF_MIPS_MACH_4010:191return "r4010";192case EF_MIPS_MACH_4100:193return "r4100";194case EF_MIPS_MACH_4650:195return "r4650";196case EF_MIPS_MACH_4120:197return "r4120";198case EF_MIPS_MACH_4111:199return "r4111";200case EF_MIPS_MACH_5400:201return "vr5400";202case EF_MIPS_MACH_5900:203return "vr5900";204case EF_MIPS_MACH_5500:205return "vr5500";206case EF_MIPS_MACH_9000:207return "rm9000";208case EF_MIPS_MACH_LS2E:209return "loongson2e";210case EF_MIPS_MACH_LS2F:211return "loongson2f";212case EF_MIPS_MACH_LS3A:213return "loongson3a";214case EF_MIPS_MACH_OCTEON:215return "octeon";216case EF_MIPS_MACH_OCTEON2:217return "octeon2";218case EF_MIPS_MACH_OCTEON3:219return "octeon3";220case EF_MIPS_MACH_SB1:221return "sb1";222case EF_MIPS_MACH_XLR:223return "xlr";224default:225return "unknown machine";226}227}228229static StringRef getArchName(uint32_t flags) {230switch (flags & EF_MIPS_ARCH) {231case EF_MIPS_ARCH_1:232return "mips1";233case EF_MIPS_ARCH_2:234return "mips2";235case EF_MIPS_ARCH_3:236return "mips3";237case EF_MIPS_ARCH_4:238return "mips4";239case EF_MIPS_ARCH_5:240return "mips5";241case EF_MIPS_ARCH_32:242return "mips32";243case EF_MIPS_ARCH_64:244return "mips64";245case EF_MIPS_ARCH_32R2:246return "mips32r2";247case EF_MIPS_ARCH_64R2:248return "mips64r2";249case EF_MIPS_ARCH_32R6:250return "mips32r6";251case EF_MIPS_ARCH_64R6:252return "mips64r6";253default:254return "unknown arch";255}256}257258static std::string getFullArchName(uint32_t flags) {259StringRef arch = getArchName(flags);260StringRef mach = getMachName(flags);261if (mach.empty())262return arch.str();263return (arch + " (" + mach + ")").str();264}265266// There are (arguably too) many MIPS ISAs out there. Their relationships267// can be represented as a forest. If all input files have ISAs which268// reachable by repeated proceeding from the single child to the parent,269// these input files are compatible. In that case we need to return "highest"270// ISA. If there are incompatible input files, we show an error.271// For example, mips1 is a "parent" of mips2 and such files are compatible.272// Output file gets EF_MIPS_ARCH_2 flag. From the other side mips3 and mips32273// are incompatible because nor mips3 is a parent for misp32, nor mips32274// is a parent for mips3.275static uint32_t getArchFlags(ArrayRef<FileFlags> files) {276uint32_t ret = files[0].flags & (EF_MIPS_ARCH | EF_MIPS_MACH);277278for (const FileFlags &f : files.slice(1)) {279uint32_t newFlags = f.flags & (EF_MIPS_ARCH | EF_MIPS_MACH);280281// Check ISA compatibility.282if (isArchMatched(newFlags, ret))283continue;284if (!isArchMatched(ret, newFlags)) {285error("incompatible target ISA:\n>>> " + toString(files[0].file) + ": " +286getFullArchName(ret) + "\n>>> " + toString(f.file) + ": " +287getFullArchName(newFlags));288return 0;289}290ret = newFlags;291}292return ret;293}294295template <class ELFT> uint32_t elf::calcMipsEFlags() {296std::vector<FileFlags> v;297for (InputFile *f : ctx.objectFiles)298v.push_back({f, cast<ObjFile<ELFT>>(f)->getObj().getHeader().e_flags});299if (v.empty()) {300// If we don't have any input files, we'll have to rely on the information301// we can derive from emulation information, since this at least gets us302// ABI.303if (config->emulation.empty() || config->is64)304return 0;305return config->mipsN32Abi ? EF_MIPS_ABI2 : EF_MIPS_ABI_O32;306}307checkFlags(v);308return getMiscFlags(v) | getPicFlags(v) | getArchFlags(v);309}310311static int compareMipsFpAbi(uint8_t fpA, uint8_t fpB) {312if (fpA == fpB)313return 0;314if (fpB == Mips::Val_GNU_MIPS_ABI_FP_ANY)315return 1;316if (fpB == Mips::Val_GNU_MIPS_ABI_FP_64A &&317fpA == Mips::Val_GNU_MIPS_ABI_FP_64)318return 1;319if (fpB != Mips::Val_GNU_MIPS_ABI_FP_XX)320return -1;321if (fpA == Mips::Val_GNU_MIPS_ABI_FP_DOUBLE ||322fpA == Mips::Val_GNU_MIPS_ABI_FP_64 ||323fpA == Mips::Val_GNU_MIPS_ABI_FP_64A)324return 1;325return -1;326}327328static StringRef getMipsFpAbiName(uint8_t fpAbi) {329switch (fpAbi) {330case Mips::Val_GNU_MIPS_ABI_FP_ANY:331return "any";332case Mips::Val_GNU_MIPS_ABI_FP_DOUBLE:333return "-mdouble-float";334case Mips::Val_GNU_MIPS_ABI_FP_SINGLE:335return "-msingle-float";336case Mips::Val_GNU_MIPS_ABI_FP_SOFT:337return "-msoft-float";338case Mips::Val_GNU_MIPS_ABI_FP_OLD_64:339return "-mgp32 -mfp64 (old)";340case Mips::Val_GNU_MIPS_ABI_FP_XX:341return "-mfpxx";342case Mips::Val_GNU_MIPS_ABI_FP_64:343return "-mgp32 -mfp64";344case Mips::Val_GNU_MIPS_ABI_FP_64A:345return "-mgp32 -mfp64 -mno-odd-spreg";346default:347return "unknown";348}349}350351uint8_t elf::getMipsFpAbiFlag(uint8_t oldFlag, uint8_t newFlag,352StringRef fileName) {353if (compareMipsFpAbi(newFlag, oldFlag) >= 0)354return newFlag;355if (compareMipsFpAbi(oldFlag, newFlag) < 0)356error(fileName + ": floating point ABI '" + getMipsFpAbiName(newFlag) +357"' is incompatible with target floating point ABI '" +358getMipsFpAbiName(oldFlag) + "'");359return oldFlag;360}361362template <class ELFT> static bool isN32Abi(const InputFile *f) {363if (auto *ef = dyn_cast<ELFFileBase>(f))364return ef->template getObj<ELFT>().getHeader().e_flags & EF_MIPS_ABI2;365return false;366}367368bool elf::isMipsN32Abi(const InputFile *f) {369switch (config->ekind) {370case ELF32LEKind:371return isN32Abi<ELF32LE>(f);372case ELF32BEKind:373return isN32Abi<ELF32BE>(f);374case ELF64LEKind:375return isN32Abi<ELF64LE>(f);376case ELF64BEKind:377return isN32Abi<ELF64BE>(f);378default:379llvm_unreachable("unknown Config->EKind");380}381}382383bool elf::isMicroMips() { return config->eflags & EF_MIPS_MICROMIPS; }384385bool elf::isMipsR6() {386uint32_t arch = config->eflags & EF_MIPS_ARCH;387return arch == EF_MIPS_ARCH_32R6 || arch == EF_MIPS_ARCH_64R6;388}389390template uint32_t elf::calcMipsEFlags<ELF32LE>();391template uint32_t elf::calcMipsEFlags<ELF32BE>();392template uint32_t elf::calcMipsEFlags<ELF64LE>();393template uint32_t elf::calcMipsEFlags<ELF64BE>();394395396