blob: 388645fb528509b6244f8534f6232039689fd337 [file] [edit]
//===-- MipsTargetObjectFile.cpp - Mips Object Files ----------------------===//
//
// 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 "MipsTargetObjectFile.h"
#include "MCTargetDesc/MipsMCAsmInfo.h"
#include "MipsSubtarget.h"
#include "MipsTargetMachine.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCSectionELF.h"
#include "llvm/Target/TargetMachine.h"
using namespace llvm;
void MipsTargetObjectFile::initialize(MCContext &Ctx, const TargetMachine &TM,
const Module &M) {
TargetLoweringObjectFileELF::initialize(Ctx, TM, M);
SmallDataSection = getContext().getELFSection(
".sdata", ELF::SHT_PROGBITS,
ELF::SHF_WRITE | ELF::SHF_ALLOC | ELF::SHF_MIPS_GPREL);
SmallBSSSection = getContext().getELFSection(".sbss", ELF::SHT_NOBITS,
ELF::SHF_WRITE | ELF::SHF_ALLOC |
ELF::SHF_MIPS_GPREL);
}
// A address must be loaded from a small section if its size is less than the
// small section size threshold. Data in this section must be addressed using
// gp_rel operator.
static bool isInSmallSection(const MipsOptions &CLOpts, uint64_t Size) {
// gcc has traditionally not treated zero-sized objects as small data, so this
// is effectively part of the ABI.
return Size > 0 && Size <= CLOpts.ssection_threshold;
}
/// Return true if this global address should be placed into small data/bss
/// section.
bool MipsTargetObjectFile::IsGlobalInSmallSection(
const GlobalObject *GO) const {
// We first check the case where global is a declaration, because finding
// section kind using getKindForGlobal() is only allowed for global
// definitions.
if (GO->isDeclaration() || GO->hasAvailableExternallyLinkage())
return IsGlobalInSmallSectionImpl(GO);
return IsGlobalInSmallSection(GO, getKindForGlobal(GO, *TM));
}
/// Return true if this global address should be placed into small data/bss
/// section.
bool MipsTargetObjectFile::IsGlobalInSmallSection(const GlobalObject *GO,
SectionKind Kind) const {
return IsGlobalInSmallSectionImpl(GO) &&
(Kind.isData() || Kind.isBSS() || Kind.isCommon() ||
Kind.isReadOnly());
}
/// Return true if this global address should be placed into small data/bss
/// section. This method does all the work, except for checking the section
/// kind.
bool MipsTargetObjectFile::IsGlobalInSmallSectionImpl(
const GlobalObject *GO) const {
const MipsOptions &CLOpts =
static_cast<const MipsTargetMachine *>(TM)->getCLOpts();
// Only global variables, not functions.
const GlobalVariable *GVA = dyn_cast<GlobalVariable>(GO);
if (!GVA)
return false;
// If the variable has an explicit section, it is placed in that section but
// it's addressing mode may change.
if (GVA->hasSection()) {
StringRef Section = GVA->getSection();
// Explicitly placing any variable in the small data section overrides
// the global -G value.
if (Section == ".sdata" || Section == ".sbss")
return true;
// Otherwise reject accessing it through the gp pointer. There are some
// historic cases which GCC doesn't appear to respect any more. These
// are .lit4, .lit8 and .srdata. For the moment reject these as well.
return false;
}
// Implicit global placement follows the default subtarget, independently of
// the features of the functions that reference the global. Explicit small
// sections above also allow GP-relative accesses from functions that enable
// them when the default subtarget does not.
if (!UseSmallSection)
return false;
// Enforce -mlocal-sdata.
if (!CLOpts.mlocal_sdata && GVA->hasLocalLinkage())
return false;
// Enforce -mextern-sdata.
if (!CLOpts.mextern_sdata &&
((GVA->hasExternalLinkage() && GVA->isDeclaration()) ||
GVA->hasCommonLinkage()))
return false;
// Enforce -membedded-data.
if (CLOpts.membedded_data && GVA->isConstant())
return false;
Type *Ty = GVA->getValueType();
// It is possible that the type of the global is unsized, i.e. a declaration
// of a extern struct. In this case don't presume it is in the small data
// section. This happens e.g. when building the FreeBSD kernel.
if (!Ty->isSized())
return false;
return isInSmallSection(CLOpts, GVA->getDataLayout().getTypeAllocSize(Ty));
}
MCSection *MipsTargetObjectFile::SelectSectionForGlobal(
const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const {
// TODO: Could also support "weak" symbols as well with ".gnu.linkonce.s.*"
// sections?
// Handle Small Section classification here.
if (IsGlobalInSmallSection(GO, Kind)) {
if (Kind.isBSS())
return SmallBSSSection;
if (Kind.isData() || Kind.isReadOnly())
return SmallDataSection;
}
// Otherwise, we work the same as ELF.
return TargetLoweringObjectFileELF::SelectSectionForGlobal(GO, Kind, TM);
}
/// Return true if this constant should be placed into small data section.
bool MipsTargetObjectFile::IsConstantInSmallSection(const DataLayout &DL,
const Constant *CN,
const Function *F) const {
bool UseSmallSection =
F ? TM->getSubtarget<MipsSubtarget>(*F).useSmallSection()
: this->UseSmallSection;
const MipsOptions &CLOpts =
static_cast<const MipsTargetMachine *>(TM)->getCLOpts();
return UseSmallSection && CN && CLOpts.mlocal_sdata &&
isInSmallSection(CLOpts, DL.getTypeAllocSize(CN->getType()));
}
/// Return true if this constant should be placed into small data section.
MCSection *MipsTargetObjectFile::getSectionForConstant(
const DataLayout &DL, SectionKind Kind, const Constant *C, Align &Alignment,
const Function *F) const {
if (IsConstantInSmallSection(DL, C, F))
return SmallDataSection;
// Otherwise, we work the same as ELF.
return TargetLoweringObjectFileELF::getSectionForConstant(DL, Kind, C,
Alignment, F);
}
const MCExpr *
MipsTargetObjectFile::getDebugThreadLocalSymbol(const MCSymbol *Sym) const {
const MCExpr *Expr = MCSymbolRefExpr::create(Sym, getContext());
Expr = MCBinaryExpr::createAdd(
Expr, MCConstantExpr::create(0x8000, getContext()), getContext());
return MCSpecifierExpr::create(Expr, Mips::S_DTPREL, getContext());
}