blob: 86e81cd08ea26cc52ec80b3e1a2449c409c5b3de [file] [log] [blame]
//===-- M68kSubtarget.cpp - M68k Subtarget Information ----------*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file implements the M68k specific subclass of TargetSubtargetInfo.
///
//===----------------------------------------------------------------------===//
#include "M68kSubtarget.h"
#include "GISel/M68kCallLowering.h"
#include "GISel/M68kLegalizerInfo.h"
#include "GISel/M68kRegisterBankInfo.h"
#include "M68k.h"
#include "M68kMachineFunction.h"
#include "M68kRegisterInfo.h"
#include "M68kTargetMachine.h"
#include "llvm/CodeGen/MachineJumpTableInfo.h"
#include "llvm/IR/Attributes.h"
#include "llvm/IR/Function.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ErrorHandling.h"
using namespace llvm;
#define DEBUG_TYPE "m68k-subtarget"
#define GET_SUBTARGETINFO_TARGET_DESC
#define GET_SUBTARGETINFO_CTOR
#include "M68kGenSubtargetInfo.inc"
extern bool FixGlobalBaseReg;
/// Select the M68k CPU for the given triple and cpu name.
static StringRef selectM68kCPU(Triple TT, StringRef CPU) {
if (CPU.empty() || CPU == "generic") {
CPU = "M68000";
}
return CPU;
}
void M68kSubtarget::anchor() {}
M68kSubtarget::M68kSubtarget(const Triple &TT, StringRef CPU, StringRef FS,
const M68kTargetMachine &TM)
: M68kGenSubtargetInfo(TT, CPU, /*TuneCPU*/ CPU, FS), TM(TM), TSInfo(),
InstrInfo(initializeSubtargetDependencies(CPU, TT, FS, TM)),
FrameLowering(*this, this->getStackAlignment()), TLInfo(TM, *this),
TargetTriple(TT) {
CallLoweringInfo.reset(new M68kCallLowering(*getTargetLowering()));
Legalizer.reset(new M68kLegalizerInfo(*this));
auto *RBI = new M68kRegisterBankInfo(*getRegisterInfo());
RegBankInfo.reset(RBI);
InstSelector.reset(createM68kInstructionSelector(TM, *this, *RBI));
}
const CallLowering *M68kSubtarget::getCallLowering() const {
return CallLoweringInfo.get();
}
InstructionSelector *M68kSubtarget::getInstructionSelector() const {
return InstSelector.get();
}
const LegalizerInfo *M68kSubtarget::getLegalizerInfo() const {
return Legalizer.get();
}
const RegisterBankInfo *M68kSubtarget::getRegBankInfo() const {
return RegBankInfo.get();
}
bool M68kSubtarget::isPositionIndependent() const {
return TM.isPositionIndependent();
}
bool M68kSubtarget::isLegalToCallImmediateAddr() const { return true; }
M68kSubtarget &M68kSubtarget::initializeSubtargetDependencies(
StringRef CPU, Triple TT, StringRef FS, const M68kTargetMachine &TM) {
std::string CPUName = selectM68kCPU(TT, CPU).str();
// Parse features string.
ParseSubtargetFeatures(CPUName, CPUName, FS);
// Initialize scheduling itinerary for the specified CPU.
InstrItins = getInstrItineraryForCPU(CPUName);
stackAlignment = 8;
return *this;
}
//===----------------------------------------------------------------------===//
// Code Model
//
// Key assumptions:
// - Whenever possible we use pc-rel encoding since it is smaller(16 bit) than
// absolute(32 bit).
// - GOT is reachable within 16 bit offset for both Small and Medium models.
// - Code section is reachable within 16 bit offset for both models.
//
// ---------------------+-------------------------+--------------------------
// | Small | Medium
// +-------------------------+------------+-------------
// | Static | PIC | Static | PIC
// ---------------------+------------+------------+------------+-------------
// branch | pc-rel | pc-rel | pc-rel | pc-rel
// ---------------------+------------+------------+------------+-------------
// call global | @PLT | @PLT | @PLT | @PLT
// ---------------------+------------+------------+------------+-------------
// call internal | pc-rel | pc-rel | pc-rel | pc-rel
// ---------------------+------------+------------+------------+-------------
// data local | pc-rel | pc-rel | ~pc-rel | ^pc-rel
// ---------------------+------------+------------+------------+-------------
// data local big* | pc-rel | pc-rel | absolute | @GOTOFF
// ---------------------+------------+------------+------------+-------------
// data global | pc-rel | @GOTPCREL | ~pc-rel | @GOTPCREL
// ---------------------+------------+------------+------------+-------------
// data global big* | pc-rel | @GOTPCREL | absolute | @GOTPCREL
// ---------------------+------------+------------+------------+-------------
//
// * Big data potentially cannot be reached within 16 bit offset and requires
// special handling for old(x00 and x10) CPUs. Normally these symbols go into
// separate .ldata section which mapped after normal .data and .text, but I
// don't really know how this must be done for M68k atm... will try to dig
// this info out from GCC. For now CPUs prior to M68020 will use static ref
// for Static Model and @GOT based references for PIC.
//
// ~ These are absolute for older CPUs for now.
// ^ These are @GOTOFF for older CPUs for now.
//===----------------------------------------------------------------------===//
/// Classify a blockaddress reference for the current subtarget according to how
/// we should reference it in a non-pcrel context.
unsigned char M68kSubtarget::classifyBlockAddressReference() const {
// Unless we start to support Large Code Model branching is always pc-rel
return M68kII::MO_PC_RELATIVE_ADDRESS;
}
unsigned char
M68kSubtarget::classifyLocalReference(const GlobalValue *GV) const {
switch (TM.getCodeModel()) {
default:
llvm_unreachable("Unsupported code model");
case CodeModel::Small:
case CodeModel::Kernel: {
return M68kII::MO_PC_RELATIVE_ADDRESS;
}
case CodeModel::Medium: {
if (isPositionIndependent()) {
// On M68020 and better we can fit big any data offset into dips field.
if (atLeastM68020()) {
return M68kII::MO_PC_RELATIVE_ADDRESS;
}
// Otherwise we could check the data size and make sure it will fit into
// 16 bit offset. For now we will be conservative and go with @GOTOFF
return M68kII::MO_GOTOFF;
} else {
if (atLeastM68020()) {
return M68kII::MO_PC_RELATIVE_ADDRESS;
}
return M68kII::MO_ABSOLUTE_ADDRESS;
}
}
}
}
unsigned char M68kSubtarget::classifyExternalReference(const Module &M) const {
if (TM.shouldAssumeDSOLocal(M, nullptr))
return classifyLocalReference(nullptr);
if (isPositionIndependent())
return M68kII::MO_GOTPCREL;
return M68kII::MO_GOT;
}
unsigned char
M68kSubtarget::classifyGlobalReference(const GlobalValue *GV) const {
return classifyGlobalReference(GV, *GV->getParent());
}
unsigned char M68kSubtarget::classifyGlobalReference(const GlobalValue *GV,
const Module &M) const {
if (TM.shouldAssumeDSOLocal(M, GV))
return classifyLocalReference(GV);
switch (TM.getCodeModel()) {
default:
llvm_unreachable("Unsupported code model");
case CodeModel::Small:
case CodeModel::Kernel: {
if (isPositionIndependent())
return M68kII::MO_GOTPCREL;
return M68kII::MO_PC_RELATIVE_ADDRESS;
}
case CodeModel::Medium: {
if (isPositionIndependent())
return M68kII::MO_GOTPCREL;
if (atLeastM68020())
return M68kII::MO_PC_RELATIVE_ADDRESS;
return M68kII::MO_ABSOLUTE_ADDRESS;
}
}
}
unsigned M68kSubtarget::getJumpTableEncoding() const {
if (isPositionIndependent()) {
// The only time we want to use GOTOFF(used when with EK_Custom32) is when
// the potential delta between the jump target and table base can be larger
// than displacement field, which is True for older CPUs(16 bit disp)
// in Medium model(can have large data way beyond 16 bit).
if (TM.getCodeModel() == CodeModel::Medium && !atLeastM68020())
return MachineJumpTableInfo::EK_Custom32;
return MachineJumpTableInfo::EK_LabelDifference32;
}
// In non-pic modes, just use the address of a block.
return MachineJumpTableInfo::EK_BlockAddress;
}
unsigned char
M68kSubtarget::classifyGlobalFunctionReference(const GlobalValue *GV) const {
return classifyGlobalFunctionReference(GV, *GV->getParent());
}
unsigned char
M68kSubtarget::classifyGlobalFunctionReference(const GlobalValue *GV,
const Module &M) const {
// local always use pc-rel referencing
if (TM.shouldAssumeDSOLocal(M, GV))
return M68kII::MO_NO_FLAG;
// If the function is marked as non-lazy, generate an indirect call
// which loads from the GOT directly. This avoids run-time overhead
// at the cost of eager binding.
auto *F = dyn_cast_or_null<Function>(GV);
if (F && F->hasFnAttribute(Attribute::NonLazyBind)) {
return M68kII::MO_GOTPCREL;
}
// otherwise linker will figure this out
return M68kII::MO_PLT;
}