1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-19 19:12:56 +02:00

[M68k](5/8) Target-specific lowering

- TargetMachine implementation for M68k
 - ISel, ISched for M68k
 - Other lowering (e.g. FrameLowering)
 - AsmPrinter

Authors: myhsu, m4yers, glaubitz

Differential Revision: https://reviews.llvm.org/D88391
This commit is contained in:
Min-Yih Hsu 2021-03-07 16:32:18 -08:00
parent c693335922
commit 1924b30c0e
27 changed files with 9062 additions and 4 deletions

View File

@ -14,7 +14,19 @@ tablegen(LLVM M68kGenAsmWriter.inc -gen-asm-writer)
add_public_tablegen_target(M68kCommonTableGen)
add_llvm_target(M68kCodeGen
M68kAsmPrinter.cpp
M68kCollapseMOVEMPass.cpp
M68kExpandPseudo.cpp
M68kFrameLowering.cpp
M68kInstrInfo.cpp
M68kISelLowering.cpp
M68kISelDAGToDAG.cpp
M68kMachineFunction.cpp
M68kMCInstLower.cpp
M68kRegisterInfo.cpp
M68kSubtarget.cpp
M68kTargetMachine.cpp
M68kTargetObjectFile.cpp
LINK_COMPONENTS
Analysis

50
lib/Target/M68k/M68k.h Normal file
View File

@ -0,0 +1,50 @@
//===- M68k.h - Top-level interface for M68k representation -*- 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 contains the entry points for global functions defined in the
/// M68k target library, as used by the LLVM JIT.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIB_TARGET_M68K_M68K_H
#define LLVM_LIB_TARGET_M68K_M68K_H
namespace llvm {
class FunctionPass;
class M68kTargetMachine;
/// This pass converts a legalized DAG into a M68k-specific DAG, ready for
/// instruction scheduling.
FunctionPass *createM68kISelDag(M68kTargetMachine &TM);
/// Return a Machine IR pass that expands M68k-specific pseudo
/// instructions into a sequence of actual instructions. This pass
/// must run after prologue/epilogue insertion and before lowering
/// the MachineInstr to MC.
FunctionPass *createM68kExpandPseudoPass();
/// This pass initializes a global base register for PIC on M68k.
FunctionPass *createM68kGlobalBaseRegPass();
/// Finds sequential MOVEM instruction and collapse them into a single one. This
/// pass has to be run after all pseudo expansions and prologue/epilogue
/// emission so that all possible MOVEM are already in place.
FunctionPass *createM68kCollapseMOVEMPass();
/// Finds MOVE instructions before any conditioanl branch instruction and
/// replaces them with MOVEM instruction. Motorola's MOVEs do trash(V,C) flags
/// register which prevents branch from taking the correct route. This pass
/// has to be run after all pseudo expansions and prologue/epilogue emission
/// so that all possible MOVEs are present.
FunctionPass *createM68kConvertMOVToMOVMPass();
} // namespace llvm
#endif

View File

@ -0,0 +1,69 @@
//===----- M68kAsmPrinter.cpp - M68k LLVM Assembly Printer -----*- 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 contains a printer that converts from our internal representation
/// of machine-dependent LLVM code to GAS-format M68k assembly language.
///
//===----------------------------------------------------------------------===//
// TODO Conform to Motorola ASM syntax
#include "M68kAsmPrinter.h"
#include "M68k.h"
#include "M68kMachineFunction.h"
#include "llvm/Support/TargetRegistry.h"
using namespace llvm;
#define DEBUG_TYPE "m68k-asm-printer"
bool M68kAsmPrinter::runOnMachineFunction(MachineFunction &MF) {
MMFI = MF.getInfo<M68kMachineFunctionInfo>();
MCInstLowering = std::make_unique<M68kMCInstLower>(MF, *this);
AsmPrinter::runOnMachineFunction(MF);
return true;
}
void M68kAsmPrinter::emitInstruction(const MachineInstr *MI) {
switch (MI->getOpcode()) {
default: {
if (MI->isPseudo()) {
LLVM_DEBUG(dbgs() << "Pseudo opcode(" << MI->getOpcode()
<< ") found in EmitInstruction()\n");
llvm_unreachable("Cannot proceed");
}
break;
}
case M68k::TAILJMPj:
case M68k::TAILJMPq:
// Lower these as normal, but add some comments.
OutStreamer->AddComment("TAILCALL");
break;
}
MCInst TmpInst0;
MCInstLowering->Lower(MI, TmpInst0);
OutStreamer->emitInstruction(TmpInst0, getSubtargetInfo());
}
void M68kAsmPrinter::emitFunctionBodyStart() {}
void M68kAsmPrinter::emitFunctionBodyEnd() {}
void M68kAsmPrinter::emitStartOfAsmFile(Module &M) {
OutStreamer->emitSyntaxDirective();
}
void M68kAsmPrinter::emitEndOfAsmFile(Module &M) {}
extern "C" void LLVMInitializeM68kAsmPrinter() {
RegisterAsmPrinter<M68kAsmPrinter> X(TheM68kTarget);
}

View File

@ -0,0 +1,64 @@
//===----- M68kAsmPrinter.h - M68k LLVM Assembly Printer -------- 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 contains M68k assembler printer declarations.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIB_TARGET_M68K_M68KASMPRINTER_H
#define LLVM_LIB_TARGET_M68K_M68KASMPRINTER_H
#include "M68kMCInstLower.h"
#include "M68kTargetMachine.h"
#include "llvm/CodeGen/AsmPrinter.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Target/TargetMachine.h"
#include <memory>
#include <utility>
namespace llvm {
class MCStreamer;
class MachineInstr;
class MachineBasicBlock;
class Module;
class raw_ostream;
class M68kSubtarget;
class M68kMachineFunctionInfo;
class LLVM_LIBRARY_VISIBILITY M68kAsmPrinter : public AsmPrinter {
void EmitInstrWithMacroNoAT(const MachineInstr *MI);
public:
const M68kSubtarget *Subtarget;
const M68kMachineFunctionInfo *MMFI;
std::unique_ptr<M68kMCInstLower> MCInstLowering;
explicit M68kAsmPrinter(TargetMachine &TM,
std::unique_ptr<MCStreamer> Streamer)
: AsmPrinter(TM, std::move(Streamer)) {
Subtarget = static_cast<M68kTargetMachine &>(TM).getSubtargetImpl();
}
StringRef getPassName() const override { return "M68k Assembly Printer"; }
virtual bool runOnMachineFunction(MachineFunction &MF) override;
void emitInstruction(const MachineInstr *MI) override;
void emitFunctionBodyStart() override;
void emitFunctionBodyEnd() override;
void emitStartOfAsmFile(Module &M) override;
void emitEndOfAsmFile(Module &M) override;
};
} // namespace llvm
#endif

View File

@ -0,0 +1,77 @@
//===-- M68kCallingConv.h - M68k Custom CC Routines ---------*- 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 contains the custom routines for the M68k Calling Convention
/// that aren't done by tablegen.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIB_TARGET_M68K_M68KCALLINGCONV_H
#define LLVM_LIB_TARGET_M68K_M68KCALLINGCONV_H
#include "MCTargetDesc/M68kMCTargetDesc.h"
#include "llvm/CodeGen/CallingConvLower.h"
#include "llvm/IR/CallingConv.h"
#include "llvm/IR/Function.h"
namespace llvm {
/// Custom state to propagate llvm type info to register CC assigner
class M68kCCState : public CCState {
public:
const llvm::Function &F;
M68kCCState(const llvm::Function &F, CallingConv::ID CC, bool IsVarArg,
MachineFunction &MF, SmallVectorImpl<CCValAssign> &Locs,
LLVMContext &C)
: CCState(CC, IsVarArg, MF, Locs, C), F(F) {}
};
/// NOTE this function is used to select registers for formal arguments and call
/// FIXME: Handling on pointer arguments is not complete
inline bool CC_M68k_Any_AssignToReg(unsigned &ValNo, MVT &ValVT, MVT &LocVT,
CCValAssign::LocInfo &LocInfo,
ISD::ArgFlagsTy &ArgFlags, CCState &State) {
M68kCCState CCInfo = static_cast<M68kCCState &>(State);
static const MCPhysReg DataRegList[] = {M68k::D0, M68k::D1, M68k::A0,
M68k::A1};
// Address registers have %a register priority
static const MCPhysReg AddrRegList[] = {
M68k::A0,
M68k::A1,
M68k::D0,
M68k::D1,
};
auto I = CCInfo.F.arg_begin();
int No = ValNo;
while (No > 0) {
No -= I->getType()->isIntegerTy(64) ? 2 : 1;
I++;
}
bool IsPtr = I != CCInfo.F.arg_end() && I->getType()->isPointerTy();
unsigned Reg =
IsPtr ? State.AllocateReg(AddrRegList) : State.AllocateReg(DataRegList);
if (Reg) {
State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo));
return true;
}
return false;
}
} // namespace llvm
#endif

View File

@ -0,0 +1,307 @@
//===----- M68kCollapseMOVEMPass.cpp - Expand MOVEM pass --------*- 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
/// `MOVEM` is an instruction that moves multiple registers a time according to
/// the given mask. Thus sometimes it's pretty expensive.
/// This file contains a pass that collapses sequential MOVEM instructions into
/// a single one.
///
//===----------------------------------------------------------------------===//
#include "M68k.h"
#include "M68kFrameLowering.h"
#include "M68kInstrInfo.h"
#include "M68kMachineFunction.h"
#include "M68kSubtarget.h"
#include "llvm/Analysis/EHPersonalities.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/IR/GlobalValue.h"
#include "llvm/Support/MathExtras.h"
using namespace llvm;
#define DEBUG_TYPE "M68k-collapse-movem"
namespace {
enum UpdateType { Ascending, Descending, Intermixed };
/// An abtraction of the MOVEM chain currently processing
class MOVEMState {
MachineBasicBlock::iterator Begin;
MachineBasicBlock::iterator End;
unsigned Base;
int Start;
int Stop;
unsigned Mask;
enum class AccessTy { None, Load, Store };
AccessTy Access;
public:
MOVEMState()
: Begin(nullptr), End(nullptr), Base(0), Start(INT_MIN), Stop(INT_MAX),
Mask(0), Access(AccessTy::None) {}
void setBegin(MachineBasicBlock::iterator &MI) {
assert(Begin == nullptr);
Begin = MI;
}
void setEnd(MachineBasicBlock::iterator &MI) {
assert(End == nullptr);
End = MI;
}
bool hasBase() const { return Base != 0; }
unsigned getBase() const {
assert(Base);
return Base;
}
MachineBasicBlock::iterator begin() {
assert(Begin != nullptr);
return Begin;
}
MachineBasicBlock::iterator end() {
assert(End != nullptr);
return End;
}
unsigned getMask() const { return Mask; }
void setBase(int Value) {
assert(!hasBase());
Base = Value;
}
// You need to call this before Mask update
UpdateType classifyUpdateByMask(unsigned NewMask) const {
assert(NewMask && "Mask needs to select at least one register");
if (NewMask > Mask) {
return Ascending;
} else if (NewMask < Mask) {
return Descending;
}
return Intermixed;
}
bool update(int O, int M) {
UpdateType Type = classifyUpdateByMask(M);
if (Type == Intermixed)
return false;
if (Start == INT_MIN) {
Start = Stop = O;
updateMask(M);
return true;
} else if (Type == Descending && O == Start - 4) {
Start -= 4;
updateMask(M);
return true;
} else if (Type == Ascending && O == Stop + 4) {
Stop += 4;
updateMask(M);
return true;
}
return false;
}
int getFinalOffset() const {
assert(
Start != INT_MIN &&
"MOVEM in control mode should increment the address in each iteration");
return Start;
}
bool updateMask(unsigned Value) {
assert(isUInt<16>(Value) && "Mask must fit 16 bit");
assert(!(Value & Mask) &&
"This is weird, there should be no intersections");
Mask |= Value;
return true;
}
void setLoad() { Access = AccessTy::Load; }
void setStore() { Access = AccessTy::Store; }
bool isLoad() const { return Access == AccessTy::Load; }
bool isStore() const { return Access == AccessTy::Store; }
};
/// This Pass first walks through all the MOVEM instructions
/// that are chained together and record each of the
/// instruction's properties like register mask and data
/// access type into a `MOVEState` instance.
/// Then we perform reduction / collapsing on this `MOVEMState`
/// representation before creating a new `MOVEM` instruction
/// based on the collapsed result, as well as removing
/// redundant `MOVEM` instructions.
class M68kCollapseMOVEM : public MachineFunctionPass {
public:
static char ID;
const M68kSubtarget *STI;
const M68kInstrInfo *TII;
const M68kRegisterInfo *TRI;
const M68kMachineFunctionInfo *MFI;
const M68kFrameLowering *FL;
M68kCollapseMOVEM() : MachineFunctionPass(ID) {}
void Finish(MachineBasicBlock &MBB, MOVEMState &State) {
auto MI = State.begin();
auto End = State.end();
DebugLoc DL = MI->getDebugLoc();
// No need to delete then add a single instruction
if (std::next(MI) == End) {
State = MOVEMState();
return;
}
// Delete all the MOVEM instruction till the end
while (MI != End) {
auto Next = std::next(MI);
MBB.erase(MI);
MI = Next;
}
// Add a unified one
if (State.isLoad()) {
BuildMI(MBB, End, DL, TII->get(M68k::MOVM32mp))
.addImm(State.getMask())
.addImm(State.getFinalOffset())
.addReg(State.getBase());
} else {
BuildMI(MBB, End, DL, TII->get(M68k::MOVM32pm))
.addImm(State.getFinalOffset())
.addReg(State.getBase())
.addImm(State.getMask());
}
State = MOVEMState();
}
bool ProcessMI(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI,
MOVEMState &State, unsigned Mask, int Offset, unsigned Reg,
bool IsStore = false) {
if (State.hasBase()) {
// If current Type, Reg, Offset and Mask is in proper order then
// merge in the state
MOVEMState Temp = State;
if (State.isStore() == IsStore && State.getBase() == Reg &&
State.update(Offset, Mask)) {
return true;
// Otherwise we Finish processing of the current MOVEM sequance and
// start a new one
} else {
State = Temp;
State.setEnd(MI);
Finish(MBB, State);
return ProcessMI(MBB, MI, State, Mask, Offset, Reg, IsStore);
}
// If this is the first instruction is sequance then initialize the State
} else if (Reg == TRI->getStackRegister() ||
Reg == TRI->getBaseRegister() ||
Reg == TRI->getFrameRegister(*MBB.getParent())) {
State.setBegin(MI);
State.setBase(Reg);
State.update(Offset, Mask);
IsStore ? State.setStore() : State.setLoad();
return true;
}
return false;
}
bool runOnMachineFunction(MachineFunction &MF) override {
STI = &static_cast<const M68kSubtarget &>(MF.getSubtarget());
TII = STI->getInstrInfo();
TRI = STI->getRegisterInfo();
MFI = MF.getInfo<M68kMachineFunctionInfo>();
FL = STI->getFrameLowering();
bool Modified = false;
MOVEMState State;
unsigned Mask = 0;
unsigned Reg = 0;
int Offset = 0;
for (auto &MBB : MF) {
auto MI = MBB.begin(), E = MBB.end();
while (MI != E) {
// Processing might change current instruction, save next first
auto NMI = std::next(MI);
switch (MI->getOpcode()) {
default:
if (State.hasBase()) {
State.setEnd(MI);
Finish(MBB, State);
Modified = true;
}
break;
case M68k::MOVM32jm:
Mask = MI->getOperand(1).getImm();
Reg = MI->getOperand(0).getReg();
Offset = 0;
Modified |= ProcessMI(MBB, MI, State, Mask, Offset, Reg, true);
break;
case M68k::MOVM32pm:
Mask = MI->getOperand(2).getImm();
Reg = MI->getOperand(1).getReg();
Offset = MI->getOperand(0).getImm();
Modified |= ProcessMI(MBB, MI, State, Mask, Offset, Reg, true);
break;
case M68k::MOVM32mj:
Mask = MI->getOperand(0).getImm();
Reg = MI->getOperand(1).getReg();
Offset = 0;
Modified |= ProcessMI(MBB, MI, State, Mask, Offset, Reg, false);
break;
case M68k::MOVM32mp:
Mask = MI->getOperand(0).getImm();
Reg = MI->getOperand(2).getReg();
Offset = MI->getOperand(1).getImm();
Modified |= ProcessMI(MBB, MI, State, Mask, Offset, Reg, false);
break;
}
MI = NMI;
}
if (State.hasBase()) {
State.setEnd(MI);
Finish(MBB, State);
}
}
return Modified;
}
StringRef getPassName() const override { return "M68k MOVEM collapser pass"; }
};
char M68kCollapseMOVEM::ID = 0;
} // anonymous namespace.
/// Returns an instance of the pseudo instruction expansion pass.
FunctionPass *llvm::createM68kCollapseMOVEMPass() {
return new M68kCollapseMOVEM();
}

View File

@ -0,0 +1,320 @@
//===--M68kExpandPseudo.cpp - Expand pseudo instructions ------*- 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 contains a pass that expands pseudo instructions into target
/// instructions to allow proper scheduling, if-conversion, other late
/// optimizations, or simply the encoding of the instructions.
///
//===----------------------------------------------------------------------===//
#include "M68k.h"
#include "M68kFrameLowering.h"
#include "M68kInstrInfo.h"
#include "M68kMachineFunction.h"
#include "M68kSubtarget.h"
#include "llvm/Analysis/EHPersonalities.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/Passes.h" // For IDs of passes that are preserved.
#include "llvm/IR/GlobalValue.h"
using namespace llvm;
#define DEBUG_TYPE "M68k-expand-pseudos"
namespace {
class M68kExpandPseudo : public MachineFunctionPass {
public:
static char ID;
M68kExpandPseudo() : MachineFunctionPass(ID) {}
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.setPreservesCFG();
AU.addPreservedID(MachineLoopInfoID);
AU.addPreservedID(MachineDominatorsID);
MachineFunctionPass::getAnalysisUsage(AU);
}
const M68kSubtarget *STI;
const M68kInstrInfo *TII;
const M68kRegisterInfo *TRI;
const M68kMachineFunctionInfo *MFI;
const M68kFrameLowering *FL;
bool runOnMachineFunction(MachineFunction &Fn) override;
MachineFunctionProperties getRequiredProperties() const override {
return MachineFunctionProperties().set(
MachineFunctionProperties::Property::NoVRegs);
}
StringRef getPassName() const override {
return "M68k pseudo instruction expansion pass";
}
private:
bool ExpandMI(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI);
bool ExpandMBB(MachineBasicBlock &MBB);
};
char M68kExpandPseudo::ID = 0;
} // End anonymous namespace.
/// If \p MBBI is a pseudo instruction, this method expands
/// it to the corresponding (sequence of) actual instruction(s).
/// \returns true if \p MBBI has been expanded.
bool M68kExpandPseudo::ExpandMI(MachineBasicBlock &MBB,
MachineBasicBlock::iterator MBBI) {
MachineInstr &MI = *MBBI;
MachineInstrBuilder MIB(*MI.getParent()->getParent(), MI);
unsigned Opcode = MI.getOpcode();
DebugLoc DL = MBBI->getDebugLoc();
/// TODO infer argument size to create less switch cases
switch (Opcode) {
default:
return false;
case M68k::MOVXd16d8:
return TII->ExpandMOVX_RR(MIB, MVT::i16, MVT::i8);
case M68k::MOVXd32d8:
return TII->ExpandMOVX_RR(MIB, MVT::i32, MVT::i8);
case M68k::MOVXd32d16:
return TII->ExpandMOVX_RR(MIB, MVT::i32, MVT::i16);
case M68k::MOVSXd16d8:
return TII->ExpandMOVSZX_RR(MIB, true, MVT::i16, MVT::i8);
case M68k::MOVSXd32d8:
return TII->ExpandMOVSZX_RR(MIB, true, MVT::i32, MVT::i8);
case M68k::MOVSXd32d16:
return TII->ExpandMOVSZX_RR(MIB, true, MVT::i32, MVT::i16);
case M68k::MOVZXd16d8:
return TII->ExpandMOVSZX_RR(MIB, false, MVT::i16, MVT::i8);
case M68k::MOVZXd32d8:
return TII->ExpandMOVSZX_RR(MIB, false, MVT::i32, MVT::i8);
case M68k::MOVZXd32d16:
return TII->ExpandMOVSZX_RR(MIB, false, MVT::i32, MVT::i16);
case M68k::MOVSXd16j8:
return TII->ExpandMOVSZX_RM(MIB, true, TII->get(M68k::MOV8dj), MVT::i16,
MVT::i8);
case M68k::MOVSXd32j8:
return TII->ExpandMOVSZX_RM(MIB, true, TII->get(M68k::MOV8dj), MVT::i32,
MVT::i8);
case M68k::MOVSXd32j16:
return TII->ExpandMOVSZX_RM(MIB, true, TII->get(M68k::MOV16rj), MVT::i32,
MVT::i16);
case M68k::MOVZXd16j8:
return TII->ExpandMOVSZX_RM(MIB, false, TII->get(M68k::MOV8dj), MVT::i16,
MVT::i8);
case M68k::MOVZXd32j8:
return TII->ExpandMOVSZX_RM(MIB, false, TII->get(M68k::MOV8dj), MVT::i32,
MVT::i8);
case M68k::MOVZXd32j16:
return TII->ExpandMOVSZX_RM(MIB, false, TII->get(M68k::MOV16rj), MVT::i32,
MVT::i16);
case M68k::MOVSXd16p8:
return TII->ExpandMOVSZX_RM(MIB, true, TII->get(M68k::MOV8dp), MVT::i16,
MVT::i8);
case M68k::MOVSXd32p8:
return TII->ExpandMOVSZX_RM(MIB, true, TII->get(M68k::MOV8dp), MVT::i32,
MVT::i8);
case M68k::MOVSXd32p16:
return TII->ExpandMOVSZX_RM(MIB, true, TII->get(M68k::MOV16rp), MVT::i32,
MVT::i16);
case M68k::MOVZXd16p8:
return TII->ExpandMOVSZX_RM(MIB, false, TII->get(M68k::MOV8dp), MVT::i16,
MVT::i8);
case M68k::MOVZXd32p8:
return TII->ExpandMOVSZX_RM(MIB, false, TII->get(M68k::MOV8dp), MVT::i32,
MVT::i8);
case M68k::MOVZXd32p16:
return TII->ExpandMOVSZX_RM(MIB, false, TII->get(M68k::MOV16rp), MVT::i32,
MVT::i16);
case M68k::MOVSXd16f8:
return TII->ExpandMOVSZX_RM(MIB, true, TII->get(M68k::MOV8df), MVT::i16,
MVT::i8);
case M68k::MOVSXd32f8:
return TII->ExpandMOVSZX_RM(MIB, true, TII->get(M68k::MOV8df), MVT::i32,
MVT::i8);
case M68k::MOVSXd32f16:
return TII->ExpandMOVSZX_RM(MIB, true, TII->get(M68k::MOV16rf), MVT::i32,
MVT::i16);
case M68k::MOVZXd16f8:
return TII->ExpandMOVSZX_RM(MIB, false, TII->get(M68k::MOV8df), MVT::i16,
MVT::i8);
case M68k::MOVZXd32f8:
return TII->ExpandMOVSZX_RM(MIB, false, TII->get(M68k::MOV8df), MVT::i32,
MVT::i8);
case M68k::MOVZXd32f16:
return TII->ExpandMOVSZX_RM(MIB, false, TII->get(M68k::MOV16rf), MVT::i32,
MVT::i16);
case M68k::MOV8cd:
return TII->ExpandCCR(MIB, /*IsToCCR=*/true);
case M68k::MOV8dc:
return TII->ExpandCCR(MIB, /*IsToCCR=*/false);
case M68k::MOVM8jm_P:
return TII->ExpandMOVEM(MIB, TII->get(M68k::MOVM32jm), /*IsRM=*/false);
case M68k::MOVM16jm_P:
return TII->ExpandMOVEM(MIB, TII->get(M68k::MOVM32jm), /*IsRM=*/false);
case M68k::MOVM32jm_P:
return TII->ExpandMOVEM(MIB, TII->get(M68k::MOVM32jm), /*IsRM=*/false);
case M68k::MOVM8pm_P:
return TII->ExpandMOVEM(MIB, TII->get(M68k::MOVM32pm), /*IsRM=*/false);
case M68k::MOVM16pm_P:
return TII->ExpandMOVEM(MIB, TII->get(M68k::MOVM32pm), /*IsRM=*/false);
case M68k::MOVM32pm_P:
return TII->ExpandMOVEM(MIB, TII->get(M68k::MOVM32pm), /*IsRM=*/false);
case M68k::MOVM8mj_P:
return TII->ExpandMOVEM(MIB, TII->get(M68k::MOVM32mj), /*IsRM=*/true);
case M68k::MOVM16mj_P:
return TII->ExpandMOVEM(MIB, TII->get(M68k::MOVM32mj), /*IsRM=*/true);
case M68k::MOVM32mj_P:
return TII->ExpandMOVEM(MIB, TII->get(M68k::MOVM32mj), /*IsRM=*/true);
case M68k::MOVM8mp_P:
return TII->ExpandMOVEM(MIB, TII->get(M68k::MOVM32mp), /*IsRM=*/true);
case M68k::MOVM16mp_P:
return TII->ExpandMOVEM(MIB, TII->get(M68k::MOVM32mp), /*IsRM=*/true);
case M68k::MOVM32mp_P:
return TII->ExpandMOVEM(MIB, TII->get(M68k::MOVM32mp), /*IsRM=*/true);
case M68k::TCRETURNq:
case M68k::TCRETURNj: {
MachineOperand &JumpTarget = MI.getOperand(0);
MachineOperand &StackAdjust = MI.getOperand(1);
assert(StackAdjust.isImm() && "Expecting immediate value.");
// Adjust stack pointer.
int StackAdj = StackAdjust.getImm();
int MaxTCDelta = MFI->getTCReturnAddrDelta();
int Offset = 0;
assert(MaxTCDelta <= 0 && "MaxTCDelta should never be positive");
// Incoporate the retaddr area.
Offset = StackAdj - MaxTCDelta;
assert(Offset >= 0 && "Offset should never be negative");
if (Offset) {
// Check for possible merge with preceding ADD instruction.
Offset += FL->mergeSPUpdates(MBB, MBBI, true);
FL->emitSPUpdate(MBB, MBBI, Offset, /*InEpilogue=*/true);
}
// Jump to label or value in register.
if (Opcode == M68k::TCRETURNq) {
MachineInstrBuilder MIB =
BuildMI(MBB, MBBI, DL, TII->get(M68k::TAILJMPq));
if (JumpTarget.isGlobal()) {
MIB.addGlobalAddress(JumpTarget.getGlobal(), JumpTarget.getOffset(),
JumpTarget.getTargetFlags());
} else {
assert(JumpTarget.isSymbol());
MIB.addExternalSymbol(JumpTarget.getSymbolName(),
JumpTarget.getTargetFlags());
}
} else {
BuildMI(MBB, MBBI, DL, TII->get(M68k::TAILJMPj))
.addReg(JumpTarget.getReg(), RegState::Kill);
}
MachineInstr &NewMI = *std::prev(MBBI);
NewMI.copyImplicitOps(*MBBI->getParent()->getParent(), *MBBI);
// Delete the pseudo instruction TCRETURN.
MBB.erase(MBBI);
return true;
}
case M68k::RET: {
// Adjust stack to erase error code
int64_t StackAdj = MBBI->getOperand(0).getImm();
MachineInstrBuilder MIB;
if (StackAdj == 0) {
MIB = BuildMI(MBB, MBBI, DL, TII->get(M68k::RTS));
} else if (isUInt<16>(StackAdj)) {
if (STI->atLeastM68020()) {
llvm_unreachable("RTD is not implemented");
} else {
// Copy PC from stack to a free address(A0 or A1) register
// TODO check if pseudo expand uses free address register
BuildMI(MBB, MBBI, DL, TII->get(M68k::MOV32aj), M68k::A1)
.addReg(M68k::SP);
// Adjust SP
FL->emitSPUpdate(MBB, MBBI, StackAdj, /*InEpilogue=*/true);
// Put the return address on stack
BuildMI(MBB, MBBI, DL, TII->get(M68k::MOV32ja))
.addReg(M68k::SP)
.addReg(M68k::A1);
// RTS
BuildMI(MBB, MBBI, DL, TII->get(M68k::RTS));
}
} else {
// TODO: RTD can only handle immediates as big as 2**16-1.
// If we need to pop off bytes before the return address, we
// must do it manually.
llvm_unreachable("Stack adjustment size not supported");
}
// FIXME: Can rest of the operands be ignored, if there is any?
MBB.erase(MBBI);
return true;
}
}
llvm_unreachable("Previous switch has a fallthrough?");
}
/// Expand all pseudo instructions contained in \p MBB.
/// \returns true if any expansion occurred for \p MBB.
bool M68kExpandPseudo::ExpandMBB(MachineBasicBlock &MBB) {
bool Modified = false;
// MBBI may be invalidated by the expansion.
MachineBasicBlock::iterator MBBI = MBB.begin(), E = MBB.end();
while (MBBI != E) {
MachineBasicBlock::iterator NMBBI = std::next(MBBI);
Modified |= ExpandMI(MBB, MBBI);
MBBI = NMBBI;
}
return Modified;
}
bool M68kExpandPseudo::runOnMachineFunction(MachineFunction &MF) {
STI = &static_cast<const M68kSubtarget &>(MF.getSubtarget());
TII = STI->getInstrInfo();
TRI = STI->getRegisterInfo();
MFI = MF.getInfo<M68kMachineFunctionInfo>();
FL = STI->getFrameLowering();
bool Modified = false;
for (MachineBasicBlock &MBB : MF)
Modified |= ExpandMBB(MBB);
return Modified;
}
/// Returns an instance of the pseudo instruction expansion pass.
FunctionPass *llvm::createM68kExpandPseudoPass() {
return new M68kExpandPseudo();
}

View File

@ -0,0 +1,896 @@
//===-- M68kFrameLowering.cpp - M68k Frame 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 contains the M68k implementation of TargetFrameLowering class.
///
//===----------------------------------------------------------------------===//
#include "M68kFrameLowering.h"
#include "M68kInstrBuilder.h"
#include "M68kInstrInfo.h"
#include "M68kMachineFunction.h"
#include "M68kSubtarget.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineModuleInfo.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/Function.h"
#include "llvm/Support/Alignment.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetOptions.h"
using namespace llvm;
M68kFrameLowering::M68kFrameLowering(const M68kSubtarget &STI, Align Alignment)
: TargetFrameLowering(StackGrowsDown, Alignment, -4), STI(STI),
TII(*STI.getInstrInfo()), TRI(STI.getRegisterInfo()) {
SlotSize = STI.getSlotSize();
StackPtr = TRI->getStackRegister();
}
bool M68kFrameLowering::hasFP(const MachineFunction &MF) const {
const MachineFrameInfo &MFI = MF.getFrameInfo();
const TargetRegisterInfo *TRI = STI.getRegisterInfo();
return MF.getTarget().Options.DisableFramePointerElim(MF) ||
MFI.hasVarSizedObjects() || MFI.isFrameAddressTaken() ||
TRI->needsStackRealignment(MF);
}
// FIXME Make sure no other factors prevent us from reserving call frame
bool M68kFrameLowering::hasReservedCallFrame(const MachineFunction &MF) const {
return !MF.getFrameInfo().hasVarSizedObjects() &&
!MF.getInfo<M68kMachineFunctionInfo>()->getHasPushSequences();
}
bool M68kFrameLowering::canSimplifyCallFramePseudos(
const MachineFunction &MF) const {
return hasReservedCallFrame(MF) ||
(hasFP(MF) && !TRI->needsStackRealignment(MF)) ||
TRI->hasBasePointer(MF);
}
bool M68kFrameLowering::needsFrameIndexResolution(
const MachineFunction &MF) const {
return MF.getFrameInfo().hasStackObjects() ||
MF.getInfo<M68kMachineFunctionInfo>()->getHasPushSequences();
}
// NOTE: this only has a subset of the full frame index logic. In
// particular, the FI < 0 and AfterFPPop logic is handled in
// M68kRegisterInfo::eliminateFrameIndex, but not here. Possibly
// (probably?) it should be moved into here.
StackOffset
M68kFrameLowering::getFrameIndexReference(const MachineFunction &MF, int FI,
Register &FrameReg) const {
const MachineFrameInfo &MFI = MF.getFrameInfo();
// We can't calculate offset from frame pointer if the stack is realigned,
// so enforce usage of stack/base pointer. The base pointer is used when we
// have dynamic allocas in addition to dynamic realignment.
if (TRI->hasBasePointer(MF))
FrameReg = TRI->getBaseRegister();
else if (TRI->needsStackRealignment(MF))
FrameReg = TRI->getStackRegister();
else
FrameReg = TRI->getFrameRegister(MF);
// Offset will hold the offset from the stack pointer at function entry to the
// object.
// We need to factor in additional offsets applied during the prologue to the
// frame, base, and stack pointer depending on which is used.
int Offset = MFI.getObjectOffset(FI) - getOffsetOfLocalArea();
const M68kMachineFunctionInfo *MMFI = MF.getInfo<M68kMachineFunctionInfo>();
uint64_t StackSize = MFI.getStackSize();
bool HasFP = hasFP(MF);
// TODO: Support tail calls
if (TRI->hasBasePointer(MF)) {
assert(HasFP && "VLAs and dynamic stack realign, but no FP?!");
if (FI < 0) {
// Skip the saved FP.
return StackOffset::getFixed(Offset + SlotSize);
}
assert((-(Offset + StackSize)) % MFI.getObjectAlign(FI).value() == 0);
return StackOffset::getFixed(Offset + StackSize);
}
if (TRI->needsStackRealignment(MF)) {
if (FI < 0) {
// Skip the saved FP.
return StackOffset::getFixed(Offset + SlotSize);
}
assert((-(Offset + StackSize)) % MFI.getObjectAlign(FI).value() == 0);
return StackOffset::getFixed(Offset + StackSize);
}
if (!HasFP)
return StackOffset::getFixed(Offset + StackSize);
// Skip the saved FP.
Offset += SlotSize;
// Skip the RETADDR move area
int TailCallReturnAddrDelta = MMFI->getTCReturnAddrDelta();
if (TailCallReturnAddrDelta < 0)
Offset -= TailCallReturnAddrDelta;
return StackOffset::getFixed(Offset);
}
/// Return a caller-saved register that isn't live
/// when it reaches the "return" instruction. We can then pop a stack object
/// to this register without worry about clobbering it.
static unsigned findDeadCallerSavedReg(MachineBasicBlock &MBB,
MachineBasicBlock::iterator &MBBI,
const M68kRegisterInfo *TRI) {
const MachineFunction *MF = MBB.getParent();
if (MF->callsEHReturn())
return 0;
const TargetRegisterClass &AvailableRegs = *TRI->getRegsForTailCall(*MF);
if (MBBI == MBB.end())
return 0;
switch (MBBI->getOpcode()) {
default:
return 0;
case TargetOpcode::PATCHABLE_RET:
case M68k::RET: {
SmallSet<uint16_t, 8> Uses;
for (unsigned i = 0, e = MBBI->getNumOperands(); i != e; ++i) {
MachineOperand &MO = MBBI->getOperand(i);
if (!MO.isReg() || MO.isDef())
continue;
unsigned Reg = MO.getReg();
if (!Reg)
continue;
for (MCRegAliasIterator AI(Reg, TRI, true); AI.isValid(); ++AI)
Uses.insert(*AI);
}
for (auto CS : AvailableRegs)
if (!Uses.count(CS))
return CS;
}
}
return 0;
}
static bool isRegLiveIn(MachineBasicBlock &MBB, unsigned Reg) {
return llvm::any_of(MBB.liveins(),
[Reg](MachineBasicBlock::RegisterMaskPair RegMask) {
return RegMask.PhysReg == Reg;
});
}
uint64_t
M68kFrameLowering::calculateMaxStackAlign(const MachineFunction &MF) const {
const MachineFrameInfo &MFI = MF.getFrameInfo();
uint64_t MaxAlign = MFI.getMaxAlign().value(); // Desired stack alignment.
unsigned StackAlign = getStackAlignment(); // ABI alignment
if (MF.getFunction().hasFnAttribute("stackrealign")) {
if (MFI.hasCalls())
MaxAlign = (StackAlign > MaxAlign) ? StackAlign : MaxAlign;
else if (MaxAlign < SlotSize)
MaxAlign = SlotSize;
}
return MaxAlign;
}
void M68kFrameLowering::BuildStackAlignAND(MachineBasicBlock &MBB,
MachineBasicBlock::iterator MBBI,
const DebugLoc &DL, unsigned Reg,
uint64_t MaxAlign) const {
uint64_t Val = -MaxAlign;
unsigned AndOp = M68k::AND32di;
unsigned MovOp = M68k::MOV32rr;
// This function is normally used with SP which is Address Register, but AND,
// or any other logical instructions in M68k do not support ARs so we need
// to use a temp Data Register to perform the op.
unsigned Tmp = M68k::D0;
BuildMI(MBB, MBBI, DL, TII.get(MovOp), Tmp)
.addReg(Reg)
.setMIFlag(MachineInstr::FrameSetup);
MachineInstr *MI = BuildMI(MBB, MBBI, DL, TII.get(AndOp), Tmp)
.addReg(Tmp)
.addImm(Val)
.setMIFlag(MachineInstr::FrameSetup);
// The CCR implicit def is dead.
MI->getOperand(3).setIsDead();
BuildMI(MBB, MBBI, DL, TII.get(MovOp), Reg)
.addReg(Tmp)
.setMIFlag(MachineInstr::FrameSetup);
}
MachineBasicBlock::iterator M68kFrameLowering::eliminateCallFramePseudoInstr(
MachineFunction &MF, MachineBasicBlock &MBB,
MachineBasicBlock::iterator I) const {
bool ReserveCallFrame = hasReservedCallFrame(MF);
unsigned Opcode = I->getOpcode();
bool IsDestroy = Opcode == TII.getCallFrameDestroyOpcode();
DebugLoc DL = I->getDebugLoc();
uint64_t Amount = !ReserveCallFrame ? I->getOperand(0).getImm() : 0;
uint64_t InternalAmt = (IsDestroy && Amount) ? I->getOperand(1).getImm() : 0;
I = MBB.erase(I);
if (!ReserveCallFrame) {
// If the stack pointer can be changed after prologue, turn the
// adjcallstackup instruction into a 'sub %SP, <amt>' and the
// adjcallstackdown instruction into 'add %SP, <amt>'
// We need to keep the stack aligned properly. To do this, we round the
// amount of space needed for the outgoing arguments up to the next
// alignment boundary.
unsigned StackAlign = getStackAlignment();
Amount = alignTo(Amount, StackAlign);
MachineModuleInfo &MMI = MF.getMMI();
const auto &Fn = MF.getFunction();
bool DwarfCFI = MMI.hasDebugInfo() || Fn.needsUnwindTableEntry();
// If we have any exception handlers in this function, and we adjust
// the SP before calls, we may need to indicate this to the unwinder
// using GNU_ARGS_SIZE. Note that this may be necessary even when
// Amount == 0, because the preceding function may have set a non-0
// GNU_ARGS_SIZE.
// TODO: We don't need to reset this between subsequent functions,
// if it didn't change.
bool HasDwarfEHHandlers = !MF.getLandingPads().empty();
if (HasDwarfEHHandlers && !IsDestroy &&
MF.getInfo<M68kMachineFunctionInfo>()->getHasPushSequences()) {
BuildCFI(MBB, I, DL,
MCCFIInstruction::createGnuArgsSize(nullptr, Amount));
}
if (Amount == 0)
return I;
// Factor out the amount that gets handled inside the sequence
// (Pushes of argument for frame setup, callee pops for frame destroy)
Amount -= InternalAmt;
// TODO: This is needed only if we require precise CFA.
// If this is a callee-pop calling convention, emit a CFA adjust for
// the amount the callee popped.
if (IsDestroy && InternalAmt && DwarfCFI && !hasFP(MF))
BuildCFI(MBB, I, DL,
MCCFIInstruction::createAdjustCfaOffset(nullptr, -InternalAmt));
// Add Amount to SP to destroy a frame, or subtract to setup.
int64_t StackAdjustment = IsDestroy ? Amount : -Amount;
int64_t CfaAdjustment = -StackAdjustment;
if (StackAdjustment) {
// Merge with any previous or following adjustment instruction. Note: the
// instructions merged with here do not have CFI, so their stack
// adjustments do not feed into CfaAdjustment.
StackAdjustment += mergeSPUpdates(MBB, I, true);
StackAdjustment += mergeSPUpdates(MBB, I, false);
if (StackAdjustment) {
BuildStackAdjustment(MBB, I, DL, StackAdjustment, false);
}
}
if (DwarfCFI && !hasFP(MF)) {
// If we don't have FP, but need to generate unwind information,
// we need to set the correct CFA offset after the stack adjustment.
// How much we adjust the CFA offset depends on whether we're emitting
// CFI only for EH purposes or for debugging. EH only requires the CFA
// offset to be correct at each call site, while for debugging we want
// it to be more precise.
// TODO: When not using precise CFA, we also need to adjust for the
// InternalAmt here.
if (CfaAdjustment) {
BuildCFI(
MBB, I, DL,
MCCFIInstruction::createAdjustCfaOffset(nullptr, CfaAdjustment));
}
}
return I;
}
if (IsDestroy && InternalAmt) {
// If we are performing frame pointer elimination and if the callee pops
// something off the stack pointer, add it back. We do this until we have
// more advanced stack pointer tracking ability.
// We are not tracking the stack pointer adjustment by the callee, so make
// sure we restore the stack pointer immediately after the call, there may
// be spill code inserted between the CALL and ADJCALLSTACKUP instructions.
MachineBasicBlock::iterator CI = I;
MachineBasicBlock::iterator B = MBB.begin();
while (CI != B && !std::prev(CI)->isCall())
--CI;
BuildStackAdjustment(MBB, CI, DL, -InternalAmt, /*InEpilogue=*/false);
}
return I;
}
/// Emit a series of instructions to increment / decrement the stack pointer by
/// a constant value.
void M68kFrameLowering::emitSPUpdate(MachineBasicBlock &MBB,
MachineBasicBlock::iterator &MBBI,
int64_t NumBytes, bool InEpilogue) const {
bool IsSub = NumBytes < 0;
uint64_t Offset = IsSub ? -NumBytes : NumBytes;
uint64_t Chunk = (1LL << 31) - 1;
DebugLoc DL = MBB.findDebugLoc(MBBI);
while (Offset) {
if (Offset > Chunk) {
// Rather than emit a long series of instructions for large offsets,
// load the offset into a register and do one sub/add
Register Reg;
if (IsSub && !isRegLiveIn(MBB, M68k::D0))
Reg = M68k::D0;
else
Reg = findDeadCallerSavedReg(MBB, MBBI, TRI);
if (Reg) {
unsigned Opc = M68k::MOV32ri;
BuildMI(MBB, MBBI, DL, TII.get(Opc), Reg).addImm(Offset);
Opc = IsSub ? M68k::SUB32rr : M68k::ADD32rr;
MachineInstr *MI = BuildMI(MBB, MBBI, DL, TII.get(Opc), StackPtr)
.addReg(StackPtr)
.addReg(Reg);
// ??? still no CCR
MI->getOperand(3).setIsDead(); // The CCR implicit def is dead.
Offset = 0;
continue;
}
}
uint64_t ThisVal = std::min(Offset, Chunk);
MachineInstrBuilder MI = BuildStackAdjustment(
MBB, MBBI, DL, IsSub ? -ThisVal : ThisVal, InEpilogue);
if (IsSub)
MI.setMIFlag(MachineInstr::FrameSetup);
else
MI.setMIFlag(MachineInstr::FrameDestroy);
Offset -= ThisVal;
}
}
int M68kFrameLowering::mergeSPUpdates(MachineBasicBlock &MBB,
MachineBasicBlock::iterator &MBBI,
bool MergeWithPrevious) const {
if ((MergeWithPrevious && MBBI == MBB.begin()) ||
(!MergeWithPrevious && MBBI == MBB.end()))
return 0;
MachineBasicBlock::iterator PI = MergeWithPrevious ? std::prev(MBBI) : MBBI;
MachineBasicBlock::iterator NI =
MergeWithPrevious ? nullptr : std::next(MBBI);
unsigned Opc = PI->getOpcode();
int Offset = 0;
if (!MergeWithPrevious && NI != MBB.end() &&
NI->getOpcode() == TargetOpcode::CFI_INSTRUCTION) {
// Don't merge with the next instruction if it has CFI.
return Offset;
}
if (Opc == M68k::ADD32ri && PI->getOperand(0).getReg() == StackPtr) {
assert(PI->getOperand(1).getReg() == StackPtr);
Offset += PI->getOperand(2).getImm();
MBB.erase(PI);
if (!MergeWithPrevious)
MBBI = NI;
} else if (Opc == M68k::SUB32ri && PI->getOperand(0).getReg() == StackPtr) {
assert(PI->getOperand(1).getReg() == StackPtr);
Offset -= PI->getOperand(2).getImm();
MBB.erase(PI);
if (!MergeWithPrevious)
MBBI = NI;
}
return Offset;
}
MachineInstrBuilder M68kFrameLowering::BuildStackAdjustment(
MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI,
const DebugLoc &DL, int64_t Offset, bool InEpilogue) const {
assert(Offset != 0 && "zero offset stack adjustment requested");
// TODO can `lea` be used to adjust stack?
bool IsSub = Offset < 0;
uint64_t AbsOffset = IsSub ? -Offset : Offset;
unsigned Opc = IsSub ? M68k::SUB32ri : M68k::ADD32ri;
MachineInstrBuilder MI = BuildMI(MBB, MBBI, DL, TII.get(Opc), StackPtr)
.addReg(StackPtr)
.addImm(AbsOffset);
// FIXME Update CCR as well. For now we just
// conservatively say CCR implicit def is dead
MI->getOperand(3).setIsDead();
return MI;
}
void M68kFrameLowering::BuildCFI(MachineBasicBlock &MBB,
MachineBasicBlock::iterator MBBI,
const DebugLoc &DL,
const MCCFIInstruction &CFIInst) const {
MachineFunction &MF = *MBB.getParent();
unsigned CFIIndex = MF.addFrameInst(CFIInst);
BuildMI(MBB, MBBI, DL, TII.get(TargetOpcode::CFI_INSTRUCTION))
.addCFIIndex(CFIIndex);
}
void M68kFrameLowering::emitCalleeSavedFrameMoves(
MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI,
const DebugLoc &DL) const {
MachineFunction &MF = *MBB.getParent();
MachineFrameInfo &MFI = MF.getFrameInfo();
MachineModuleInfo &MMI = MF.getMMI();
const MCRegisterInfo *MRI = MMI.getContext().getRegisterInfo();
// Add callee saved registers to move list.
const auto &CSI = MFI.getCalleeSavedInfo();
if (CSI.empty())
return;
// Calculate offsets.
for (const auto &I : CSI) {
int64_t Offset = MFI.getObjectOffset(I.getFrameIdx());
unsigned Reg = I.getReg();
unsigned DwarfReg = MRI->getDwarfRegNum(Reg, true);
BuildCFI(MBB, MBBI, DL,
MCCFIInstruction::createOffset(nullptr, DwarfReg, Offset));
}
}
void M68kFrameLowering::emitPrologue(MachineFunction &MF,
MachineBasicBlock &MBB) const {
assert(&STI == &MF.getSubtarget<M68kSubtarget>() &&
"MF used frame lowering for wrong subtarget");
MachineBasicBlock::iterator MBBI = MBB.begin();
MachineFrameInfo &MFI = MF.getFrameInfo();
const auto &Fn = MF.getFunction();
MachineModuleInfo &MMI = MF.getMMI();
M68kMachineFunctionInfo *MMFI = MF.getInfo<M68kMachineFunctionInfo>();
uint64_t MaxAlign = calculateMaxStackAlign(MF); // Desired stack alignment.
uint64_t StackSize = MFI.getStackSize(); // Number of bytes to allocate.
bool HasFP = hasFP(MF);
bool NeedsDwarfCFI = MMI.hasDebugInfo() || Fn.needsUnwindTableEntry();
unsigned FramePtr = TRI->getFrameRegister(MF);
const unsigned MachineFramePtr = FramePtr;
unsigned BasePtr = TRI->getBaseRegister();
// Debug location must be unknown since the first debug location is used
// to determine the end of the prologue.
DebugLoc DL;
// Add RETADDR move area to callee saved frame size.
int TailCallReturnAddrDelta = MMFI->getTCReturnAddrDelta();
if (TailCallReturnAddrDelta < 0) {
MMFI->setCalleeSavedFrameSize(MMFI->getCalleeSavedFrameSize() -
TailCallReturnAddrDelta);
}
// Insert stack pointer adjustment for later moving of return addr. Only
// applies to tail call optimized functions where the callee argument stack
// size is bigger than the callers.
if (TailCallReturnAddrDelta < 0) {
BuildStackAdjustment(MBB, MBBI, DL, TailCallReturnAddrDelta,
/*InEpilogue=*/false)
.setMIFlag(MachineInstr::FrameSetup);
}
// Mapping for machine moves:
//
// DST: VirtualFP AND
// SRC: VirtualFP => DW_CFA_def_cfa_offset
// ELSE => DW_CFA_def_cfa
//
// SRC: VirtualFP AND
// DST: Register => DW_CFA_def_cfa_register
//
// ELSE
// OFFSET < 0 => DW_CFA_offset_extended_sf
// REG < 64 => DW_CFA_offset + Reg
// ELSE => DW_CFA_offset_extended
uint64_t NumBytes = 0;
int stackGrowth = -SlotSize;
if (HasFP) {
// Calculate required stack adjustment.
uint64_t FrameSize = StackSize - SlotSize;
// If required, include space for extra hidden slot for stashing base
// pointer.
if (MMFI->getRestoreBasePointer())
FrameSize += SlotSize;
NumBytes = FrameSize - MMFI->getCalleeSavedFrameSize();
// Callee-saved registers are pushed on stack before the stack is realigned.
if (TRI->needsStackRealignment(MF))
NumBytes = alignTo(NumBytes, MaxAlign);
// Get the offset of the stack slot for the FP register, which is
// guaranteed to be the last slot by processFunctionBeforeFrameFinalized.
// Update the frame offset adjustment.
MFI.setOffsetAdjustment(-NumBytes);
// Save FP into the appropriate stack slot.
BuildMI(MBB, MBBI, DL, TII.get(M68k::PUSH32r))
.addReg(MachineFramePtr, RegState::Kill)
.setMIFlag(MachineInstr::FrameSetup);
if (NeedsDwarfCFI) {
// Mark the place where FP was saved.
// Define the current CFA rule to use the provided offset.
assert(StackSize);
BuildCFI(MBB, MBBI, DL,
MCCFIInstruction::cfiDefCfaOffset(nullptr, 2 * stackGrowth));
// Change the rule for the FramePtr to be an "offset" rule.
int DwarfFramePtr = TRI->getDwarfRegNum(MachineFramePtr, true);
assert(DwarfFramePtr > 0);
BuildCFI(MBB, MBBI, DL,
MCCFIInstruction::createOffset(nullptr, DwarfFramePtr,
2 * stackGrowth));
}
// Update FP with the new base value.
BuildMI(MBB, MBBI, DL, TII.get(M68k::MOV32aa), FramePtr)
.addReg(StackPtr)
.setMIFlag(MachineInstr::FrameSetup);
if (NeedsDwarfCFI) {
// Mark effective beginning of when frame pointer becomes valid.
// Define the current CFA to use the FP register.
unsigned DwarfFramePtr = TRI->getDwarfRegNum(MachineFramePtr, true);
BuildCFI(MBB, MBBI, DL,
MCCFIInstruction::createDefCfaRegister(nullptr, DwarfFramePtr));
}
// Mark the FramePtr as live-in in every block. Don't do this again for
// funclet prologues.
for (MachineBasicBlock &EveryMBB : MF)
EveryMBB.addLiveIn(MachineFramePtr);
} else {
NumBytes = StackSize - MMFI->getCalleeSavedFrameSize();
}
// Skip the callee-saved push instructions.
bool PushedRegs = false;
int StackOffset = 2 * stackGrowth;
while (MBBI != MBB.end() && MBBI->getFlag(MachineInstr::FrameSetup) &&
MBBI->getOpcode() == M68k::PUSH32r) {
PushedRegs = true;
++MBBI;
if (!HasFP && NeedsDwarfCFI) {
// Mark callee-saved push instruction.
// Define the current CFA rule to use the provided offset.
assert(StackSize);
BuildCFI(MBB, MBBI, DL,
MCCFIInstruction::cfiDefCfaOffset(nullptr, StackOffset));
StackOffset += stackGrowth;
}
}
// Realign stack after we pushed callee-saved registers (so that we'll be
// able to calculate their offsets from the frame pointer).
if (TRI->needsStackRealignment(MF)) {
assert(HasFP && "There should be a frame pointer if stack is realigned.");
BuildStackAlignAND(MBB, MBBI, DL, StackPtr, MaxAlign);
}
// If there is an SUB32ri of SP immediately before this instruction, merge
// the two. This can be the case when tail call elimination is enabled and
// the callee has more arguments then the caller.
NumBytes -= mergeSPUpdates(MBB, MBBI, true);
// Adjust stack pointer: ESP -= numbytes.
emitSPUpdate(MBB, MBBI, -(int64_t)NumBytes, /*InEpilogue=*/false);
unsigned SPOrEstablisher = StackPtr;
// If we need a base pointer, set it up here. It's whatever the value
// of the stack pointer is at this point. Any variable size objects
// will be allocated after this, so we can still use the base pointer
// to reference locals.
if (TRI->hasBasePointer(MF)) {
// Update the base pointer with the current stack pointer.
BuildMI(MBB, MBBI, DL, TII.get(M68k::MOV32aa), BasePtr)
.addReg(SPOrEstablisher)
.setMIFlag(MachineInstr::FrameSetup);
if (MMFI->getRestoreBasePointer()) {
// Stash value of base pointer. Saving SP instead of FP shortens
// dependence chain. Used by SjLj EH.
unsigned Opm = M68k::MOV32ja;
M68k::addRegIndirectWithDisp(BuildMI(MBB, MBBI, DL, TII.get(Opm)),
FramePtr, true,
MMFI->getRestoreBasePointerOffset())
.addReg(SPOrEstablisher)
.setMIFlag(MachineInstr::FrameSetup);
}
}
if (((!HasFP && NumBytes) || PushedRegs) && NeedsDwarfCFI) {
// Mark end of stack pointer adjustment.
if (!HasFP && NumBytes) {
// Define the current CFA rule to use the provided offset.
assert(StackSize);
BuildCFI(
MBB, MBBI, DL,
MCCFIInstruction::cfiDefCfaOffset(nullptr, -StackSize + stackGrowth));
}
// Emit DWARF info specifying the offsets of the callee-saved registers.
if (PushedRegs)
emitCalleeSavedFrameMoves(MBB, MBBI, DL);
}
// TODO Interrupt handlers
// M68k Interrupt handling function cannot assume anything about the
// direction flag (DF in CCR register). Clear this flag by creating "cld"
// instruction in each prologue of interrupt handler function. The "cld"
// instruction should only in these cases:
// 1. The interrupt handling function uses any of the "rep" instructions.
// 2. Interrupt handling function calls another function.
}
static bool isTailCallOpcode(unsigned Opc) {
return Opc == M68k::TCRETURNj || Opc == M68k::TCRETURNq;
}
void M68kFrameLowering::emitEpilogue(MachineFunction &MF,
MachineBasicBlock &MBB) const {
const MachineFrameInfo &MFI = MF.getFrameInfo();
M68kMachineFunctionInfo *MMFI = MF.getInfo<M68kMachineFunctionInfo>();
MachineBasicBlock::iterator MBBI = MBB.getFirstTerminator();
Optional<unsigned> RetOpcode;
if (MBBI != MBB.end())
RetOpcode = MBBI->getOpcode();
DebugLoc DL;
if (MBBI != MBB.end())
DL = MBBI->getDebugLoc();
unsigned FramePtr = TRI->getFrameRegister(MF);
unsigned MachineFramePtr = FramePtr;
// Get the number of bytes to allocate from the FrameInfo.
uint64_t StackSize = MFI.getStackSize();
uint64_t MaxAlign = calculateMaxStackAlign(MF);
unsigned CSSize = MMFI->getCalleeSavedFrameSize();
uint64_t NumBytes = 0;
if (hasFP(MF)) {
// Calculate required stack adjustment.
uint64_t FrameSize = StackSize - SlotSize;
NumBytes = FrameSize - CSSize;
// Callee-saved registers were pushed on stack before the stack was
// realigned.
if (TRI->needsStackRealignment(MF))
NumBytes = alignTo(FrameSize, MaxAlign);
// Pop FP.
BuildMI(MBB, MBBI, DL, TII.get(M68k::POP32r), MachineFramePtr)
.setMIFlag(MachineInstr::FrameDestroy);
} else {
NumBytes = StackSize - CSSize;
}
// Skip the callee-saved pop instructions.
while (MBBI != MBB.begin()) {
MachineBasicBlock::iterator PI = std::prev(MBBI);
unsigned Opc = PI->getOpcode();
if ((Opc != M68k::POP32r || !PI->getFlag(MachineInstr::FrameDestroy)) &&
Opc != M68k::DBG_VALUE && !PI->isTerminator())
break;
--MBBI;
}
MachineBasicBlock::iterator FirstCSPop = MBBI;
if (MBBI != MBB.end())
DL = MBBI->getDebugLoc();
// If there is an ADD32ri or SUB32ri of SP immediately before this
// instruction, merge the two instructions.
if (NumBytes || MFI.hasVarSizedObjects())
NumBytes += mergeSPUpdates(MBB, MBBI, true);
// If dynamic alloca is used, then reset SP to point to the last callee-saved
// slot before popping them off! Same applies for the case, when stack was
// realigned. Don't do this if this was a funclet epilogue, since the funclets
// will not do realignment or dynamic stack allocation.
if ((TRI->needsStackRealignment(MF) || MFI.hasVarSizedObjects())) {
if (TRI->needsStackRealignment(MF))
MBBI = FirstCSPop;
uint64_t LEAAmount = -CSSize;
// 'move %FramePtr, SP' will not be recognized as an epilogue sequence.
// However, we may use this sequence if we have a frame pointer because the
// effects of the prologue can safely be undone.
if (LEAAmount != 0) {
unsigned Opc = M68k::LEA32p;
M68k::addRegIndirectWithDisp(
BuildMI(MBB, MBBI, DL, TII.get(Opc), StackPtr), FramePtr, false,
LEAAmount);
--MBBI;
} else {
unsigned Opc = (M68k::MOV32rr);
BuildMI(MBB, MBBI, DL, TII.get(Opc), StackPtr).addReg(FramePtr);
--MBBI;
}
} else if (NumBytes) {
// Adjust stack pointer back: SP += numbytes.
emitSPUpdate(MBB, MBBI, NumBytes, /*InEpilogue=*/true);
--MBBI;
}
if (!RetOpcode || !isTailCallOpcode(*RetOpcode)) {
// Add the return addr area delta back since we are not tail calling.
int Offset = -1 * MMFI->getTCReturnAddrDelta();
assert(Offset >= 0 && "TCDelta should never be positive");
if (Offset) {
MBBI = MBB.getFirstTerminator();
// Check for possible merge with preceding ADD instruction.
Offset += mergeSPUpdates(MBB, MBBI, true);
emitSPUpdate(MBB, MBBI, Offset, /*InEpilogue=*/true);
}
}
}
void M68kFrameLowering::determineCalleeSaves(MachineFunction &MF,
BitVector &SavedRegs,
RegScavenger *RS) const {
TargetFrameLowering::determineCalleeSaves(MF, SavedRegs, RS);
MachineFrameInfo &MFI = MF.getFrameInfo();
M68kMachineFunctionInfo *M68kFI = MF.getInfo<M68kMachineFunctionInfo>();
int64_t TailCallReturnAddrDelta = M68kFI->getTCReturnAddrDelta();
if (TailCallReturnAddrDelta < 0) {
// create RETURNADDR area
// arg
// arg
// RETADDR
// { ...
// RETADDR area
// ...
// }
// [FP]
MFI.CreateFixedObject(-TailCallReturnAddrDelta,
TailCallReturnAddrDelta - SlotSize, true);
}
// Spill the BasePtr if it's used.
if (TRI->hasBasePointer(MF)) {
SavedRegs.set(TRI->getBaseRegister());
}
}
bool M68kFrameLowering::assignCalleeSavedSpillSlots(
MachineFunction &MF, const TargetRegisterInfo *TRI,
std::vector<CalleeSavedInfo> &CSI) const {
MachineFrameInfo &MFI = MF.getFrameInfo();
M68kMachineFunctionInfo *M68kFI = MF.getInfo<M68kMachineFunctionInfo>();
int SpillSlotOffset = getOffsetOfLocalArea() + M68kFI->getTCReturnAddrDelta();
if (hasFP(MF)) {
// emitPrologue always spills frame register the first thing.
SpillSlotOffset -= SlotSize;
MFI.CreateFixedSpillStackObject(SlotSize, SpillSlotOffset);
// Since emitPrologue and emitEpilogue will handle spilling and restoring of
// the frame register, we can delete it from CSI list and not have to worry
// about avoiding it later.
unsigned FPReg = TRI->getFrameRegister(MF);
for (unsigned i = 0, e = CSI.size(); i < e; ++i) {
if (TRI->regsOverlap(CSI[i].getReg(), FPReg)) {
CSI.erase(CSI.begin() + i);
break;
}
}
}
// The rest is fine
return false;
}
bool M68kFrameLowering::spillCalleeSavedRegisters(
MachineBasicBlock &MBB, MachineBasicBlock::iterator MI,
ArrayRef<CalleeSavedInfo> CSI, const TargetRegisterInfo *TRI) const {
auto &MRI = *static_cast<const M68kRegisterInfo *>(TRI);
auto DL = MBB.findDebugLoc(MI);
int FI = 0;
unsigned Mask = 0;
for (const auto &Info : CSI) {
FI = std::max(FI, Info.getFrameIdx());
unsigned Reg = Info.getReg();
unsigned Shift = MRI.getSpillRegisterOrder(Reg);
Mask |= 1 << Shift;
}
auto I =
M68k::addFrameReference(BuildMI(MBB, MI, DL, TII.get(M68k::MOVM32pm)), FI)
.addImm(Mask)
.setMIFlag(MachineInstr::FrameSetup);
// Append implicit registers and mem locations
const MachineFunction &MF = *MBB.getParent();
const MachineRegisterInfo &RI = MF.getRegInfo();
for (const auto &Info : CSI) {
unsigned Reg = Info.getReg();
bool IsLiveIn = RI.isLiveIn(Reg);
if (!IsLiveIn)
MBB.addLiveIn(Reg);
I.addReg(Reg, IsLiveIn ? RegState::Implicit : RegState::ImplicitKill);
M68k::addMemOperand(I, Info.getFrameIdx(), 0);
}
return true;
}
bool M68kFrameLowering::restoreCalleeSavedRegisters(
MachineBasicBlock &MBB, MachineBasicBlock::iterator MI,
MutableArrayRef<CalleeSavedInfo> CSI, const TargetRegisterInfo *TRI) const {
auto &MRI = *static_cast<const M68kRegisterInfo *>(TRI);
auto DL = MBB.findDebugLoc(MI);
int FI = 0;
unsigned Mask = 0;
for (const auto &Info : CSI) {
FI = std::max(FI, Info.getFrameIdx());
unsigned Reg = Info.getReg();
unsigned Shift = MRI.getSpillRegisterOrder(Reg);
Mask |= 1 << Shift;
}
auto I = M68k::addFrameReference(
BuildMI(MBB, MI, DL, TII.get(M68k::MOVM32mp)).addImm(Mask), FI)
.setMIFlag(MachineInstr::FrameDestroy);
// Append implicit registers and mem locations
for (const auto &Info : CSI) {
I.addReg(Info.getReg(), RegState::ImplicitDefine);
M68k::addMemOperand(I, Info.getFrameIdx(), 0);
}
return true;
}

View File

@ -0,0 +1,172 @@
//===- M68kFrameLowering.h - Define frame lowering for M68k -*- 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 contains the M68k declaration of TargetFrameLowering class.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIB_TARGET_M68K_M68KFRAMELOWERING_H
#define LLVM_LIB_TARGET_M68K_M68KFRAMELOWERING_H
#include "M68k.h"
#include "llvm/CodeGen/TargetFrameLowering.h"
namespace llvm {
class MachineInstrBuilder;
class MCCFIInstruction;
class M68kSubtarget;
class M68kRegisterInfo;
struct Align;
class M68kFrameLowering : public TargetFrameLowering {
// Cached subtarget predicates.
const M68kSubtarget &STI;
const TargetInstrInfo &TII;
const M68kRegisterInfo *TRI;
/// Stack slot size in bytes.
unsigned SlotSize;
unsigned StackPtr;
/// If we're forcing a stack realignment we can't rely on just the frame
/// info, we need to know the ABI stack alignment as well in case we have a
/// call out. Otherwise just make sure we have some alignment - we'll go
/// with the minimum SlotSize.
uint64_t calculateMaxStackAlign(const MachineFunction &MF) const;
/// Adjusts the stack pointer using LEA, SUB, or ADD.
MachineInstrBuilder BuildStackAdjustment(MachineBasicBlock &MBB,
MachineBasicBlock::iterator MBBI,
const DebugLoc &DL, int64_t Offset,
bool InEpilogue) const;
/// Aligns the stack pointer by ANDing it with -MaxAlign.
void BuildStackAlignAND(MachineBasicBlock &MBB,
MachineBasicBlock::iterator MBBI, const DebugLoc &DL,
unsigned Reg, uint64_t MaxAlign) const;
/// Wraps up getting a CFI index and building a MachineInstr for it.
void BuildCFI(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI,
const DebugLoc &DL, const MCCFIInstruction &CFIInst) const;
void emitCalleeSavedFrameMoves(MachineBasicBlock &MBB,
MachineBasicBlock::iterator MBBI,
const DebugLoc &DL) const;
unsigned getPSPSlotOffsetFromSP(const MachineFunction &MF) const;
public:
explicit M68kFrameLowering(const M68kSubtarget &sti, Align Alignment);
static const M68kFrameLowering *create(const M68kSubtarget &ST);
/// This method is called during prolog/epilog code insertion to eliminate
/// call frame setup and destroy pseudo instructions (but only if the Target
/// is using them). It is responsible for eliminating these instructions,
/// replacing them with concrete instructions. This method need only be
/// implemented if using call frame setup/destroy pseudo instructions.
/// Returns an iterator pointing to the instruction after the replaced one.
MachineBasicBlock::iterator
eliminateCallFramePseudoInstr(MachineFunction &MF, MachineBasicBlock &MBB,
MachineBasicBlock::iterator MI) const override;
/// Insert prolog code into the function.
void emitPrologue(MachineFunction &MF, MachineBasicBlock &MBB) const override;
/// Insert epilog code into the function.
void emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const override;
/// This method determines which of the registers reported by
/// TargetRegisterInfo::getCalleeSavedRegs() should actually get saved.
/// The default implementation checks populates the \p SavedRegs bitset with
/// all registers which are modified in the function, targets may override
/// this function to save additional registers.
/// This method also sets up the register scavenger ensuring there is a free
/// register or a frameindex available.
void determineCalleeSaves(MachineFunction &MF, BitVector &SavedRegs,
RegScavenger *RS = nullptr) const override;
/// Allows target to override spill slot assignment logic. If implemented,
/// assignCalleeSavedSpillSlots() should assign frame slots to all CSI
/// entries and return true. If this method returns false, spill slots will
/// be assigned using generic implementation. assignCalleeSavedSpillSlots()
/// may add, delete or rearrange elements of CSI.
bool
assignCalleeSavedSpillSlots(MachineFunction &MF,
const TargetRegisterInfo *TRI,
std::vector<CalleeSavedInfo> &CSI) const override;
/// Issues instruction(s) to spill all callee saved registers and returns
/// true if it isn't possible / profitable to do so by issuing a series of
/// store instructions via storeRegToStackSlot(). Returns false otherwise.
bool spillCalleeSavedRegisters(MachineBasicBlock &MBB,
MachineBasicBlock::iterator MI,
ArrayRef<CalleeSavedInfo> CSI,
const TargetRegisterInfo *TRI) const override;
/// Issues instruction(s) to restore all callee saved registers and returns
/// true if it isn't possible / profitable to do so by issuing a series of
/// load instructions via loadRegToStackSlot(). Returns false otherwise.
bool
restoreCalleeSavedRegisters(MachineBasicBlock &MBB,
MachineBasicBlock::iterator MI,
MutableArrayRef<CalleeSavedInfo> CSI,
const TargetRegisterInfo *TRI) const override;
/// Return true if the specified function should have a dedicated frame
/// pointer register. This is true if the function has variable sized
/// allocas, if it needs dynamic stack realignment, if frame pointer
/// elimination is disabled, or if the frame address is taken.
bool hasFP(const MachineFunction &MF) const override;
/// Under normal circumstances, when a frame pointer is not required, we
/// reserve argument space for call sites in the function immediately on
/// entry to the current function. This eliminates the need for add/sub sp
/// brackets around call sites. Returns true if the call frame is included as
/// part of the stack frame.
bool hasReservedCallFrame(const MachineFunction &MF) const override;
/// If there is a reserved call frame, the call frame pseudos can be
/// simplified. Having a FP, as in the default implementation, is not
/// sufficient here since we can't always use it. Use a more nuanced
/// condition.
bool canSimplifyCallFramePseudos(const MachineFunction &MF) const override;
// Do we need to perform FI resolution for this function. Normally, this is
// required only when the function has any stack objects. However, FI
// resolution actually has another job, not apparent from the title - it
// resolves callframe setup/destroy that were not simplified earlier.
//
// So, this is required for M68k functions that have push sequences even
// when there are no stack objects.
bool needsFrameIndexResolution(const MachineFunction &MF) const override;
/// This method should return the base register and offset used to reference
/// a frame index location. The offset is returned directly, and the base
/// register is returned via FrameReg.
StackOffset getFrameIndexReference(const MachineFunction &MF, int FI,
Register &FrameReg) const override;
/// Check the instruction before/after the passed instruction. If
/// it is an ADD/SUB/LEA instruction it is deleted argument and the
/// stack adjustment is returned as a positive value for ADD/LEA and
/// a negative for SUB.
int mergeSPUpdates(MachineBasicBlock &MBB, MachineBasicBlock::iterator &MBBI,
bool doMergeWithPrevious) const;
/// Emit a series of instructions to increment / decrement the stack
/// pointer by a constant value.
void emitSPUpdate(MachineBasicBlock &MBB, MachineBasicBlock::iterator &MBBI,
int64_t NumBytes, bool InEpilogue) const;
};
} // namespace llvm
#endif

View File

@ -0,0 +1,899 @@
//===- M68kISelDAGToDAG.cpp - M68k Dag to Dag Inst Selector -*- 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 defines an instruction selector for the M68K target.
///
//===----------------------------------------------------------------------===//
#include "M68k.h"
#include "M68kMachineFunction.h"
#include "M68kRegisterInfo.h"
#include "M68kTargetMachine.h"
#include "llvm/CodeGen/MachineConstantPool.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/SelectionDAGISel.h"
#include "llvm/CodeGen/SelectionDAGNodes.h"
#include "llvm/IR/CFG.h"
#include "llvm/IR/GlobalValue.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/Type.h"
#include "llvm/Support/Alignment.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetMachine.h"
using namespace llvm;
#define DEBUG_TYPE "m68k-isel"
namespace {
// For reference, the full order of operands for memory references is:
// (Operand), Displacement, Base, Index, Scale
struct M68kISelAddressMode {
enum class AddrType {
ARI, // Address Register Indirect
ARIPI, // Address Register Indirect with Postincrement
ARIPD, // Address Register Indirect with Postdecrement
ARID, // Address Register Indirect with Displacement
ARII, // Address Register Indirect with Index
PCD, // Program Counter Indirect with Displacement
PCI, // Program Counter Indirect with Index
AL, // Absolute
};
AddrType AM;
enum class Base { RegBase, FrameIndexBase };
Base BaseType;
int64_t Disp;
// This is really a union, discriminated by BaseType!
SDValue BaseReg;
int BaseFrameIndex;
SDValue IndexReg;
unsigned Scale;
const GlobalValue *GV;
const Constant *CP;
const BlockAddress *BlockAddr;
const char *ES;
MCSymbol *MCSym;
int JT;
Align Alignment; // CP alignment.
unsigned char SymbolFlags; // M68kII::MO_*
M68kISelAddressMode(AddrType AT)
: AM(AT), BaseType(Base::RegBase), Disp(0), BaseFrameIndex(0), IndexReg(),
Scale(1), GV(nullptr), CP(nullptr), BlockAddr(nullptr), ES(nullptr),
MCSym(nullptr), JT(-1), Alignment(), SymbolFlags(M68kII::MO_NO_FLAG) {}
bool hasSymbolicDisplacement() const {
return GV != nullptr || CP != nullptr || ES != nullptr ||
MCSym != nullptr || JT != -1 || BlockAddr != nullptr;
}
bool hasBase() const {
return BaseType == Base::FrameIndexBase || BaseReg.getNode() != nullptr;
}
bool hasFrameIndex() const { return BaseType == Base::FrameIndexBase; }
bool hasBaseReg() const {
return BaseType == Base::RegBase && BaseReg.getNode() != nullptr;
}
bool hasIndexReg() const {
return BaseType == Base::RegBase && IndexReg.getNode() != nullptr;
}
/// True if address mode type supports displacement
bool isDispAddrType() const {
return AM == AddrType::ARII || AM == AddrType::PCI ||
AM == AddrType::ARID || AM == AddrType::PCD || AM == AddrType::AL;
}
unsigned getDispSize() const {
switch (AM) {
default:
return 0;
case AddrType::ARII:
case AddrType::PCI:
return 8;
// These two in the next chip generations can hold upto 32 bit
case AddrType::ARID:
case AddrType::PCD:
return 16;
case AddrType::AL:
return 32;
}
}
bool hasDisp() const { return getDispSize() != 0; }
bool isDisp8() const { return getDispSize() == 8; }
bool isDisp16() const { return getDispSize() == 16; }
bool isDisp32() const { return getDispSize() == 32; }
/// Return true if this addressing mode is already PC-relative.
bool isPCRelative() const {
if (BaseType != Base::RegBase)
return false;
if (auto *RegNode = dyn_cast_or_null<RegisterSDNode>(BaseReg.getNode()))
return RegNode->getReg() == M68k::PC;
return false;
}
void setBaseReg(SDValue Reg) {
BaseType = Base::RegBase;
BaseReg = Reg;
}
void setIndexReg(SDValue Reg) { IndexReg = Reg; }
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
void dump() {
dbgs() << "M68kISelAddressMode " << this;
dbgs() << "\nDisp: " << Disp;
dbgs() << ", BaseReg: ";
if (BaseReg.getNode())
BaseReg.getNode()->dump();
else
dbgs() << "null";
dbgs() << ", BaseFI: " << BaseFrameIndex;
dbgs() << ", IndexReg: ";
if (IndexReg.getNode()) {
IndexReg.getNode()->dump();
} else {
dbgs() << "null";
dbgs() << ", Scale: " << Scale;
}
dbgs() << '\n';
}
#endif
};
} // end anonymous namespace
namespace {
class M68kDAGToDAGISel : public SelectionDAGISel {
public:
explicit M68kDAGToDAGISel(M68kTargetMachine &TM)
: SelectionDAGISel(TM), Subtarget(nullptr) {}
StringRef getPassName() const override {
return "M68k DAG->DAG Pattern Instruction Selection";
}
bool runOnMachineFunction(MachineFunction &MF) override;
private:
/// Keep a pointer to the M68kSubtarget around so that we can
/// make the right decision when generating code for different targets.
const M68kSubtarget *Subtarget;
// Include the pieces autogenerated from the target description.
#include "M68kGenDAGISel.inc"
/// getTargetMachine - Return a reference to the TargetMachine, casted
/// to the target-specific type.
const M68kTargetMachine &getTargetMachine() {
return static_cast<const M68kTargetMachine &>(TM);
}
void Select(SDNode *N) override;
// Insert instructions to initialize the global base register in the
// first MBB of the function.
// HMM... do i need this?
void initGlobalBaseReg(MachineFunction &MF);
bool foldOffsetIntoAddress(uint64_t Offset, M68kISelAddressMode &AM);
bool matchLoadInAddress(LoadSDNode *N, M68kISelAddressMode &AM);
bool matchAddress(SDValue N, M68kISelAddressMode &AM);
bool matchAddressBase(SDValue N, M68kISelAddressMode &AM);
bool matchAddressRecursively(SDValue N, M68kISelAddressMode &AM,
unsigned Depth);
bool matchADD(SDValue &N, M68kISelAddressMode &AM, unsigned Depth);
bool matchWrapper(SDValue N, M68kISelAddressMode &AM);
std::pair<bool, SDNode *> selectNode(SDNode *Node);
bool SelectARI(SDNode *Parent, SDValue N, SDValue &Base);
bool SelectARIPI(SDNode *Parent, SDValue N, SDValue &Base);
bool SelectARIPD(SDNode *Parent, SDValue N, SDValue &Base);
bool SelectARID(SDNode *Parent, SDValue N, SDValue &Imm, SDValue &Base);
bool SelectARII(SDNode *Parent, SDValue N, SDValue &Imm, SDValue &Base,
SDValue &Index);
bool SelectAL(SDNode *Parent, SDValue N, SDValue &Sym);
bool SelectPCD(SDNode *Parent, SDValue N, SDValue &Imm);
bool SelectPCI(SDNode *Parent, SDValue N, SDValue &Imm, SDValue &Index);
// If Address Mode represents Frame Index store FI in Disp and
// Displacement bit size in Base. These values are read symmetrically by
// M68kRegisterInfo::eliminateFrameIndex method
inline bool getFrameIndexAddress(M68kISelAddressMode &AM, const SDLoc &DL,
SDValue &Disp, SDValue &Base) {
if (AM.BaseType == M68kISelAddressMode::Base::FrameIndexBase) {
Disp = getI32Imm(AM.Disp, DL);
Base = CurDAG->getTargetFrameIndex(
AM.BaseFrameIndex, TLI->getPointerTy(CurDAG->getDataLayout()));
return true;
}
return false;
}
// Gets a symbol plus optional displacement
inline bool getSymbolicDisplacement(M68kISelAddressMode &AM, const SDLoc &DL,
SDValue &Sym) {
if (AM.GV) {
Sym = CurDAG->getTargetGlobalAddress(AM.GV, SDLoc(), MVT::i32, AM.Disp,
AM.SymbolFlags);
return true;
}
if (AM.CP) {
Sym = CurDAG->getTargetConstantPool(AM.CP, MVT::i32, AM.Alignment,
AM.Disp, AM.SymbolFlags);
return true;
}
if (AM.ES) {
assert(!AM.Disp && "Non-zero displacement is ignored with ES.");
Sym = CurDAG->getTargetExternalSymbol(AM.ES, MVT::i32, AM.SymbolFlags);
return true;
}
if (AM.MCSym) {
assert(!AM.Disp && "Non-zero displacement is ignored with MCSym.");
assert(AM.SymbolFlags == 0 && "oo");
Sym = CurDAG->getMCSymbol(AM.MCSym, MVT::i32);
return true;
}
if (AM.JT != -1) {
assert(!AM.Disp && "Non-zero displacement is ignored with JT.");
Sym = CurDAG->getTargetJumpTable(AM.JT, MVT::i32, AM.SymbolFlags);
return true;
}
if (AM.BlockAddr) {
Sym = CurDAG->getTargetBlockAddress(AM.BlockAddr, MVT::i32, AM.Disp,
AM.SymbolFlags);
return true;
}
return false;
}
/// Return a target constant with the specified value of type i8.
inline SDValue getI8Imm(int64_t Imm, const SDLoc &DL) {
return CurDAG->getTargetConstant(Imm, DL, MVT::i8);
}
/// Return a target constant with the specified value of type i8.
inline SDValue getI16Imm(int64_t Imm, const SDLoc &DL) {
return CurDAG->getTargetConstant(Imm, DL, MVT::i16);
}
/// Return a target constant with the specified value, of type i32.
inline SDValue getI32Imm(int64_t Imm, const SDLoc &DL) {
return CurDAG->getTargetConstant(Imm, DL, MVT::i32);
}
/// Return a reference to the TargetInstrInfo, casted to the target-specific
/// type.
const M68kInstrInfo *getInstrInfo() const {
return Subtarget->getInstrInfo();
}
/// Return an SDNode that returns the value of the global base register.
/// Output instructions required to initialize the global base register,
/// if necessary.
SDNode *getGlobalBaseReg();
};
} // namespace
bool M68kDAGToDAGISel::runOnMachineFunction(MachineFunction &MF) {
Subtarget = &static_cast<const M68kSubtarget &>(MF.getSubtarget());
return SelectionDAGISel::runOnMachineFunction(MF);
}
/// This pass converts a legalized DAG into a M68k-specific DAG,
/// ready for instruction scheduling.
FunctionPass *llvm::createM68kISelDag(M68kTargetMachine &TM) {
return new M68kDAGToDAGISel(TM);
}
static bool doesDispFitFI(M68kISelAddressMode &AM) {
if (!AM.isDispAddrType())
return false;
// -1 to make sure that resolved FI will fit into Disp field
return isIntN(AM.getDispSize() - 1, AM.Disp);
}
static bool doesDispFit(M68kISelAddressMode &AM, int64_t Val) {
if (!AM.isDispAddrType())
return false;
return isIntN(AM.getDispSize(), Val);
}
/// Return an SDNode that returns the value of the global base register.
/// Output instructions required to initialize the global base register,
/// if necessary.
SDNode *M68kDAGToDAGISel::getGlobalBaseReg() {
unsigned GlobalBaseReg = getInstrInfo()->getGlobalBaseReg(MF);
auto &DL = MF->getDataLayout();
return CurDAG->getRegister(GlobalBaseReg, TLI->getPointerTy(DL)).getNode();
}
bool M68kDAGToDAGISel::foldOffsetIntoAddress(uint64_t Offset,
M68kISelAddressMode &AM) {
// Cannot combine ExternalSymbol displacements with integer offsets.
if (Offset != 0 && (AM.ES || AM.MCSym))
return false;
int64_t Val = AM.Disp + Offset;
if (doesDispFit(AM, Val)) {
AM.Disp = Val;
return true;
}
return false;
}
//===----------------------------------------------------------------------===//
// Matchers
//===----------------------------------------------------------------------===//
/// Helper for MatchAddress. Add the specified node to the
/// specified addressing mode without any further recursion.
bool M68kDAGToDAGISel::matchAddressBase(SDValue N, M68kISelAddressMode &AM) {
// Is the base register already occupied?
if (AM.hasBase()) {
// If so, check to see if the scale index register is set.
if (!AM.hasIndexReg()) {
AM.IndexReg = N;
AM.Scale = 1;
return true;
}
// Otherwise, we cannot select it.
return false;
}
// Default, generate it as a register.
AM.BaseType = M68kISelAddressMode::Base::RegBase;
AM.BaseReg = N;
return true;
}
/// TODO Add TLS support
bool M68kDAGToDAGISel::matchLoadInAddress(LoadSDNode *N,
M68kISelAddressMode &AM) {
return false;
}
bool M68kDAGToDAGISel::matchAddressRecursively(SDValue N,
M68kISelAddressMode &AM,
unsigned Depth) {
SDLoc DL(N);
// Limit recursion.
if (Depth > 5)
return matchAddressBase(N, AM);
// If this is already a %PC relative address, we can only merge immediates
// into it. Instead of handling this in every case, we handle it here.
// PC relative addressing: %PC + 16-bit displacement!
if (AM.isPCRelative()) {
// FIXME JumpTable and ExternalSymbol address currently don't like
// displacements. It isn't very important, but should be fixed for
// consistency.
if (ConstantSDNode *Cst = dyn_cast<ConstantSDNode>(N))
if (foldOffsetIntoAddress(Cst->getSExtValue(), AM))
return true;
return false;
}
switch (N.getOpcode()) {
default:
break;
case ISD::Constant: {
uint64_t Val = cast<ConstantSDNode>(N)->getSExtValue();
if (foldOffsetIntoAddress(Val, AM))
return true;
break;
}
case M68kISD::Wrapper:
case M68kISD::WrapperPC:
if (matchWrapper(N, AM))
return true;
break;
case ISD::LOAD:
if (matchLoadInAddress(cast<LoadSDNode>(N), AM))
return true;
break;
case ISD::OR:
// We want to look through a transform in InstCombine and DAGCombiner that
// turns 'add' into 'or', so we can treat this 'or' exactly like an 'add'.
// Example: (or (and x, 1), (shl y, 3)) --> (add (and x, 1), (shl y, 3))
// An 'lea' can then be used to match the shift (multiply) and add:
// and $1, %esi
// lea (%rsi, %rdi, 8), %rax
if (CurDAG->haveNoCommonBitsSet(N.getOperand(0), N.getOperand(1)) &&
matchADD(N, AM, Depth))
return true;
break;
case ISD::ADD:
if (matchADD(N, AM, Depth))
return true;
break;
case ISD::FrameIndex:
if (AM.isDispAddrType() &&
AM.BaseType == M68kISelAddressMode::Base::RegBase &&
AM.BaseReg.getNode() == nullptr && doesDispFitFI(AM)) {
AM.BaseType = M68kISelAddressMode::Base::FrameIndexBase;
AM.BaseFrameIndex = cast<FrameIndexSDNode>(N)->getIndex();
return true;
}
break;
}
return matchAddressBase(N, AM);
}
/// Add the specified node to the specified addressing mode, returning true if
/// it cannot be done. This just pattern matches for the addressing mode.
bool M68kDAGToDAGISel::matchAddress(SDValue N, M68kISelAddressMode &AM) {
// TODO: Post-processing: Convert lea(,%reg,2) to lea(%reg,%reg), which has
// a smaller encoding and avoids a scaled-index.
// And make sure it is an indexed mode
// TODO: Post-processing: Convert foo to foo(%pc), even in non-PIC mode,
// because it has a smaller encoding.
// Make sure this must be done only if PC* modes are currently being matched
return matchAddressRecursively(N, AM, 0);
}
bool M68kDAGToDAGISel::matchADD(SDValue &N, M68kISelAddressMode &AM,
unsigned Depth) {
// Add an artificial use to this node so that we can keep track of
// it if it gets CSE'd with a different node.
HandleSDNode Handle(N);
M68kISelAddressMode Backup = AM;
if (matchAddressRecursively(N.getOperand(0), AM, Depth + 1) &&
matchAddressRecursively(Handle.getValue().getOperand(1), AM, Depth + 1)) {
return true;
}
AM = Backup;
// Try again after commuting the operands.
if (matchAddressRecursively(Handle.getValue().getOperand(1), AM, Depth + 1) &&
matchAddressRecursively(Handle.getValue().getOperand(0), AM, Depth + 1)) {
return true;
}
AM = Backup;
// If we couldn't fold both operands into the address at the same time,
// see if we can just put each operand into a register and fold at least
// the add.
if (!AM.hasBase() && !AM.hasIndexReg()) {
N = Handle.getValue();
AM.BaseReg = N.getOperand(0);
AM.IndexReg = N.getOperand(1);
AM.Scale = 1;
return true;
}
N = Handle.getValue();
return false;
}
/// Try to match M68kISD::Wrapper and M68kISD::WrapperPC nodes into an
/// addressing mode. These wrap things that will resolve down into a symbol
/// reference. If no match is possible, this returns true, otherwise it returns
/// false.
bool M68kDAGToDAGISel::matchWrapper(SDValue N, M68kISelAddressMode &AM) {
// If the addressing mode already has a symbol as the displacement, we can
// never match another symbol.
if (AM.hasSymbolicDisplacement())
return false;
SDValue N0 = N.getOperand(0);
if (N.getOpcode() == M68kISD::WrapperPC) {
// If cannot match here just restore the old version
M68kISelAddressMode Backup = AM;
if (AM.hasBase()) {
return false;
}
if (auto *G = dyn_cast<GlobalAddressSDNode>(N0)) {
AM.GV = G->getGlobal();
AM.SymbolFlags = G->getTargetFlags();
if (!foldOffsetIntoAddress(G->getOffset(), AM)) {
AM = Backup;
return false;
}
} else if (auto *CP = dyn_cast<ConstantPoolSDNode>(N0)) {
AM.CP = CP->getConstVal();
AM.Alignment = CP->getAlign();
AM.SymbolFlags = CP->getTargetFlags();
if (!foldOffsetIntoAddress(CP->getOffset(), AM)) {
AM = Backup;
return false;
}
} else if (auto *S = dyn_cast<ExternalSymbolSDNode>(N0)) {
AM.ES = S->getSymbol();
AM.SymbolFlags = S->getTargetFlags();
} else if (auto *S = dyn_cast<MCSymbolSDNode>(N0)) {
AM.MCSym = S->getMCSymbol();
} else if (auto *J = dyn_cast<JumpTableSDNode>(N0)) {
AM.JT = J->getIndex();
AM.SymbolFlags = J->getTargetFlags();
} else if (auto *BA = dyn_cast<BlockAddressSDNode>(N0)) {
AM.BlockAddr = BA->getBlockAddress();
AM.SymbolFlags = BA->getTargetFlags();
if (!foldOffsetIntoAddress(BA->getOffset(), AM)) {
AM = Backup;
return false;
}
} else
llvm_unreachable("Unhandled symbol reference node.");
AM.setBaseReg(CurDAG->getRegister(M68k::PC, MVT::i32));
return true;
}
// This wrapper requires 32bit disp/imm field for Medium CM
if (!AM.isDisp32()) {
return false;
}
if (N.getOpcode() == M68kISD::Wrapper) {
if (auto *G = dyn_cast<GlobalAddressSDNode>(N0)) {
AM.GV = G->getGlobal();
AM.Disp += G->getOffset();
AM.SymbolFlags = G->getTargetFlags();
} else if (auto *CP = dyn_cast<ConstantPoolSDNode>(N0)) {
AM.CP = CP->getConstVal();
AM.Alignment = CP->getAlign();
AM.Disp += CP->getOffset();
AM.SymbolFlags = CP->getTargetFlags();
} else if (auto *S = dyn_cast<ExternalSymbolSDNode>(N0)) {
AM.ES = S->getSymbol();
AM.SymbolFlags = S->getTargetFlags();
} else if (auto *S = dyn_cast<MCSymbolSDNode>(N0)) {
AM.MCSym = S->getMCSymbol();
} else if (auto *J = dyn_cast<JumpTableSDNode>(N0)) {
AM.JT = J->getIndex();
AM.SymbolFlags = J->getTargetFlags();
} else if (auto *BA = dyn_cast<BlockAddressSDNode>(N0)) {
AM.BlockAddr = BA->getBlockAddress();
AM.Disp += BA->getOffset();
AM.SymbolFlags = BA->getTargetFlags();
} else
llvm_unreachable("Unhandled symbol reference node.");
return true;
}
return false;
}
//===----------------------------------------------------------------------===//
// Selectors
//===----------------------------------------------------------------------===//
void M68kDAGToDAGISel::Select(SDNode *Node) {
unsigned Opcode = Node->getOpcode();
SDLoc DL(Node);
LLVM_DEBUG(dbgs() << "Selecting: "; Node->dump(CurDAG); dbgs() << '\n');
if (Node->isMachineOpcode()) {
LLVM_DEBUG(dbgs() << "== "; Node->dump(CurDAG); dbgs() << '\n');
Node->setNodeId(-1);
return; // Already selected.
}
switch (Opcode) {
default:
break;
case M68kISD::GLOBAL_BASE_REG:
ReplaceNode(Node, getGlobalBaseReg());
return;
}
SelectCode(Node);
}
bool M68kDAGToDAGISel::SelectARIPI(SDNode *Parent, SDValue N, SDValue &Base) {
LLVM_DEBUG(dbgs() << "Selecting AddrType::ARIPI: ");
LLVM_DEBUG(dbgs() << "NOT IMPLEMENTED\n");
return false;
}
bool M68kDAGToDAGISel::SelectARIPD(SDNode *Parent, SDValue N, SDValue &Base) {
LLVM_DEBUG(dbgs() << "Selecting AddrType::ARIPD: ");
LLVM_DEBUG(dbgs() << "NOT IMPLEMENTED\n");
return false;
}
bool M68kDAGToDAGISel::SelectARID(SDNode *Parent, SDValue N, SDValue &Disp,
SDValue &Base) {
LLVM_DEBUG(dbgs() << "Selecting AddrType::ARID: ");
M68kISelAddressMode AM(M68kISelAddressMode::AddrType::ARID);
if (!matchAddress(N, AM))
return false;
if (AM.isPCRelative()) {
LLVM_DEBUG(dbgs() << "REJECT: Cannot match PC relative address\n");
return false;
}
// If this is a frame index, grab it
if (getFrameIndexAddress(AM, SDLoc(N), Disp, Base)) {
LLVM_DEBUG(dbgs() << "SUCCESS matched FI\n");
return true;
}
if (AM.hasIndexReg()) {
LLVM_DEBUG(dbgs() << "REJECT: Cannot match Index\n");
return false;
}
if (!AM.hasBaseReg()) {
LLVM_DEBUG(dbgs() << "REJECT: No Base reg\n");
return false;
}
if (getSymbolicDisplacement(AM, SDLoc(N), Disp)) {
assert(!AM.Disp && "Should not be any displacement");
LLVM_DEBUG(dbgs() << "SUCCESS, matched Symbol\n");
return true;
}
// Give a chance to AddrType::ARI
if (AM.Disp == 0) {
LLVM_DEBUG(dbgs() << "REJECT: No displacement\n");
return false;
}
Base = AM.BaseReg;
Disp = getI16Imm(AM.Disp, SDLoc(N));
LLVM_DEBUG(dbgs() << "SUCCESS\n");
return true;
}
static bool isAddressBase(const SDValue &N) {
switch (N.getOpcode()) {
case ISD::ADD:
case ISD::ADDC:
return llvm::any_of(N.getNode()->ops(),
[](const SDUse &U) { return isAddressBase(U.get()); });
case M68kISD::Wrapper:
case M68kISD::WrapperPC:
case M68kISD::GLOBAL_BASE_REG:
return true;
default:
return false;
}
}
bool M68kDAGToDAGISel::SelectARII(SDNode *Parent, SDValue N, SDValue &Disp,
SDValue &Base, SDValue &Index) {
M68kISelAddressMode AM(M68kISelAddressMode::AddrType::ARII);
LLVM_DEBUG(dbgs() << "Selecting AddrType::ARII: ");
if (!matchAddress(N, AM))
return false;
if (AM.isPCRelative()) {
LLVM_DEBUG(dbgs() << "REJECT: PC relative\n");
return false;
}
if (!AM.hasIndexReg()) {
LLVM_DEBUG(dbgs() << "REJECT: No Index\n");
return false;
}
if (!AM.hasBaseReg()) {
LLVM_DEBUG(dbgs() << "REJECT: No Base\n");
return false;
}
if (!isAddressBase(AM.BaseReg) && isAddressBase(AM.IndexReg)) {
Base = AM.IndexReg;
Index = AM.BaseReg;
} else {
Base = AM.BaseReg;
Index = AM.IndexReg;
}
if (AM.hasSymbolicDisplacement()) {
LLVM_DEBUG(dbgs() << "REJECT, Cannot match symbolic displacement\n");
return false;
}
// The idea here is that we want to use AddrType::ARII without displacement
// only if necessary like memory operations, otherwise this must be lowered
// into addition
if (AM.Disp == 0 && (!Parent || (Parent->getOpcode() != ISD::LOAD &&
Parent->getOpcode() != ISD::STORE))) {
LLVM_DEBUG(dbgs() << "REJECT: Displacement is Zero\n");
return false;
}
Disp = getI8Imm(AM.Disp, SDLoc(N));
LLVM_DEBUG(dbgs() << "SUCCESS\n");
return true;
}
bool M68kDAGToDAGISel::SelectAL(SDNode *Parent, SDValue N, SDValue &Sym) {
LLVM_DEBUG(dbgs() << "Selecting AddrType::AL: ");
M68kISelAddressMode AM(M68kISelAddressMode::AddrType::AL);
if (!matchAddress(N, AM)) {
LLVM_DEBUG(dbgs() << "REJECT: Match failed\n");
return false;
}
if (AM.isPCRelative()) {
LLVM_DEBUG(dbgs() << "REJECT: Cannot match PC relative address\n");
return false;
}
if (AM.hasBase()) {
LLVM_DEBUG(dbgs() << "REJECT: Cannot match Base\n");
return false;
}
if (AM.hasIndexReg()) {
LLVM_DEBUG(dbgs() << "REJECT: Cannot match Index\n");
return false;
}
if (getSymbolicDisplacement(AM, SDLoc(N), Sym)) {
LLVM_DEBUG(dbgs() << "SUCCESS: Matched symbol\n");
return true;
}
if (AM.Disp) {
Sym = getI32Imm(AM.Disp, SDLoc(N));
LLVM_DEBUG(dbgs() << "SUCCESS\n");
return true;
}
LLVM_DEBUG(dbgs() << "REJECT: Not Symbol or Disp\n");
return false;
;
}
bool M68kDAGToDAGISel::SelectPCD(SDNode *Parent, SDValue N, SDValue &Disp) {
LLVM_DEBUG(dbgs() << "Selecting AddrType::PCD: ");
M68kISelAddressMode AM(M68kISelAddressMode::AddrType::PCD);
if (!matchAddress(N, AM))
return false;
if (!AM.isPCRelative()) {
LLVM_DEBUG(dbgs() << "REJECT: Not PC relative\n");
return false;
}
if (AM.hasIndexReg()) {
LLVM_DEBUG(dbgs() << "REJECT: Cannot match Index\n");
return false;
}
if (getSymbolicDisplacement(AM, SDLoc(N), Disp)) {
LLVM_DEBUG(dbgs() << "SUCCESS, matched Symbol\n");
return true;
}
Disp = getI16Imm(AM.Disp, SDLoc(N));
LLVM_DEBUG(dbgs() << "SUCCESS\n");
return true;
}
bool M68kDAGToDAGISel::SelectPCI(SDNode *Parent, SDValue N, SDValue &Disp,
SDValue &Index) {
LLVM_DEBUG(dbgs() << "Selecting AddrType::PCI: ");
M68kISelAddressMode AM(M68kISelAddressMode::AddrType::PCI);
if (!matchAddress(N, AM))
return false;
if (!AM.isPCRelative()) {
LLVM_DEBUG(dbgs() << "REJECT: Not PC relative\n");
return false;
}
if (!AM.hasIndexReg()) {
LLVM_DEBUG(dbgs() << "REJECT: No Index\n");
return false;
}
Index = AM.IndexReg;
if (getSymbolicDisplacement(AM, SDLoc(N), Disp)) {
assert(!AM.Disp && "Should not be any displacement");
LLVM_DEBUG(dbgs() << "SUCCESS, matched Symbol\n");
return true;
}
Disp = getI8Imm(AM.Disp, SDLoc(N));
LLVM_DEBUG(dbgs() << "SUCCESS\n");
return true;
}
bool M68kDAGToDAGISel::SelectARI(SDNode *Parent, SDValue N, SDValue &Base) {
LLVM_DEBUG(dbgs() << "Selecting AddrType::ARI: ");
M68kISelAddressMode AM(M68kISelAddressMode::AddrType::ARI);
if (!matchAddress(N, AM)) {
LLVM_DEBUG(dbgs() << "REJECT: Match failed\n");
return false;
}
if (AM.isPCRelative()) {
LLVM_DEBUG(dbgs() << "REJECT: Cannot match PC relative address\n");
return false;
}
// AddrType::ARI does not use these
if (AM.hasIndexReg() || AM.Disp != 0) {
LLVM_DEBUG(dbgs() << "REJECT: Cannot match Index or Disp\n");
return false;
}
// Must be matched by AddrType::AL
if (AM.hasSymbolicDisplacement()) {
LLVM_DEBUG(dbgs() << "REJECT: Cannot match Symbolic Disp\n");
return false;
}
if (AM.hasBaseReg()) {
Base = AM.BaseReg;
LLVM_DEBUG(dbgs() << "SUCCESS\n");
return true;
}
return false;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,265 @@
//===-- M68kISelLowering.h - M68k DAG Lowering Interface ----*- 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 defines the interfaces that M68k uses to lower LLVM code into a
/// selection DAG.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIB_TARGET_M68K_M68KISELLOWERING_H
#define LLVM_LIB_TARGET_M68K_M68KISELLOWERING_H
#include "M68k.h"
#include "llvm/CodeGen/CallingConvLower.h"
#include "llvm/CodeGen/SelectionDAG.h"
#include "llvm/CodeGen/TargetLowering.h"
#include "llvm/IR/Function.h"
#include <deque>
namespace llvm {
namespace M68kISD {
/// M68k Specific DAG nodes
enum NodeType {
/// Start the numbering from where ISD NodeType finishes.
FIRST_NUMBER = ISD::BUILTIN_OP_END,
CALL,
RET,
TAIL_CALL,
TC_RETURN,
/// M68k compare and logical compare instructions. Subtracts the source
/// operand from the destination data register and sets the condition
/// codes according to the result. Immediate always goes first.
CMP,
/// M68k bit-test instructions.
BT,
/// M68k Select
SELECT,
/// M68k SetCC. Operand 0 is condition code, and operand 1 is the CCR
/// operand, usually produced by a CMP instruction.
SETCC,
// Same as SETCC except it's materialized with a subx and the value is all
// one's or all zero's.
SETCC_CARRY, // R = carry_bit ? ~0 : 0
/// M68k conditional moves. Operand 0 and operand 1 are the two values
/// to select from. Operand 2 is the condition code, and operand 3 is the
/// flag operand produced by a CMP or TEST instruction. It also writes a
/// flag result.
CMOV,
/// M68k conditional branches. Operand 0 is the chain operand, operand 1
/// is the block to branch if condition is true, operand 2 is the
/// condition code, and operand 3 is the flag operand produced by a CMP
/// or TEST instruction.
BRCOND,
// Arithmetic operations with CCR results.
ADD,
SUB,
ADDX,
SUBX,
SMUL,
UMUL,
OR,
XOR,
AND,
// GlobalBaseReg,
GLOBAL_BASE_REG,
/// A wrapper node for TargetConstantPool,
/// TargetExternalSymbol, and TargetGlobalAddress.
Wrapper,
/// Special wrapper used under M68k PIC mode for PC
/// relative displacements.
WrapperPC,
// For allocating variable amounts of stack space when using
// segmented stacks. Check if the current stacklet has enough space, and
// falls back to heap allocation if not.
SEG_ALLOCA,
};
} // namespace M68kISD
/// Define some predicates that are used for node matching.
namespace M68k {
/// Determines whether the callee is required to pop its
/// own arguments. Callee pop is necessary to support tail calls.
bool isCalleePop(CallingConv::ID CallingConv, bool IsVarArg, bool GuaranteeTCO);
} // end namespace M68k
//===--------------------------------------------------------------------===//
// TargetLowering Implementation
//===--------------------------------------------------------------------===//
class M68kMachineFunctionInfo;
class M68kSubtarget;
class M68kTargetLowering : public TargetLowering {
const M68kSubtarget &Subtarget;
const M68kTargetMachine &TM;
public:
explicit M68kTargetLowering(const M68kTargetMachine &TM,
const M68kSubtarget &STI);
static const M68kTargetLowering *create(const M68kTargetMachine &TM,
const M68kSubtarget &STI);
const char *getTargetNodeName(unsigned Opcode) const override;
/// Return the value type to use for ISD::SETCC.
EVT getSetCCResultType(const DataLayout &DL, LLVMContext &Context,
EVT VT) const override;
/// EVT is not used in-tree, but is used by out-of-tree target.
virtual MVT getScalarShiftAmountTy(const DataLayout &, EVT) const override;
/// Provide custom lowering hooks for some operations.
SDValue LowerOperation(SDValue Op, SelectionDAG &DAG) const override;
/// Return the entry encoding for a jump table in the current function.
/// The returned value is a member of the MachineJumpTableInfo::JTEntryKind
/// enum.
unsigned getJumpTableEncoding() const override;
const MCExpr *LowerCustomJumpTableEntry(const MachineJumpTableInfo *MJTI,
const MachineBasicBlock *MBB,
unsigned uid,
MCContext &Ctx) const override;
/// Returns relocation base for the given PIC jumptable.
SDValue getPICJumpTableRelocBase(SDValue Table,
SelectionDAG &DAG) const override;
/// This returns the relocation base for the given PIC jumptable,
/// the same as getPICJumpTableRelocBase, but as an MCExpr.
const MCExpr *getPICJumpTableRelocBaseExpr(const MachineFunction *MF,
unsigned JTI,
MCContext &Ctx) const override;
MachineBasicBlock *
EmitInstrWithCustomInserter(MachineInstr &MI,
MachineBasicBlock *MBB) const override;
private:
unsigned GetAlignedArgumentStackSize(unsigned StackSize,
SelectionDAG &DAG) const;
SDValue getReturnAddressFrameIndex(SelectionDAG &DAG) const;
/// Emit a load of return address if tail call
/// optimization is performed and it is required.
SDValue EmitTailCallLoadRetAddr(SelectionDAG &DAG, SDValue &OutRetAddr,
SDValue Chain, bool IsTailCall, int FPDiff,
const SDLoc &DL) const;
/// Emit a store of the return address if tail call
/// optimization is performed and it is required (FPDiff!=0).
SDValue EmitTailCallStoreRetAddr(SelectionDAG &DAG, MachineFunction &MF,
SDValue Chain, SDValue RetAddrFrIdx,
EVT PtrVT, unsigned SlotSize, int FPDiff,
const SDLoc &DL) const;
SDValue LowerMemArgument(SDValue Chain, CallingConv::ID CallConv,
const SmallVectorImpl<ISD::InputArg> &ArgInfo,
const SDLoc &DL, SelectionDAG &DAG,
const CCValAssign &VA, MachineFrameInfo &MFI,
unsigned ArgIdx) const;
SDValue LowerMemOpCallTo(SDValue Chain, SDValue StackPtr, SDValue Arg,
const SDLoc &DL, SelectionDAG &DAG,
const CCValAssign &VA, ISD::ArgFlagsTy Flags) const;
SDValue LowerXALUO(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerToBT(SDValue And, ISD::CondCode CC, const SDLoc &DL,
SelectionDAG &DAG) const;
SDValue LowerSETCC(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerSETCCCARRY(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerSELECT(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerBRCOND(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerADDC_ADDE_SUBC_SUBE(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerConstantPool(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerJumpTable(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerExternalSymbol(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerBlockAddress(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerGlobalAddress(const GlobalValue *GV, const SDLoc &DL,
int64_t Offset, SelectionDAG &DAG) const;
SDValue LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerVASTART(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerDYNAMIC_STACKALLOC(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerCallResult(SDValue Chain, SDValue InFlag,
CallingConv::ID CallConv, bool IsVarArg,
const SmallVectorImpl<ISD::InputArg> &Ins,
const SDLoc &DL, SelectionDAG &DAG,
SmallVectorImpl<SDValue> &InVals) const;
/// LowerFormalArguments - transform physical registers into virtual
/// registers and generate load operations for arguments places on the stack.
SDValue LowerFormalArguments(SDValue Chain, CallingConv::ID CCID,
bool IsVarArg,
const SmallVectorImpl<ISD::InputArg> &Ins,
const SDLoc &DL, SelectionDAG &DAG,
SmallVectorImpl<SDValue> &InVals) const override;
SDValue LowerCall(CallLoweringInfo &CLI,
SmallVectorImpl<SDValue> &InVals) const override;
/// Lower the result values of a call into the
/// appropriate copies out of appropriate physical registers.
SDValue LowerReturn(SDValue Chain, CallingConv::ID CCID, bool IsVarArg,
const SmallVectorImpl<ISD::OutputArg> &Outs,
const SmallVectorImpl<SDValue> &OutVals, const SDLoc &DL,
SelectionDAG &DAG) const override;
bool decomposeMulByConstant(LLVMContext &Context, EVT VT,
SDValue C) const override;
MachineBasicBlock *EmitLoweredSelect(MachineInstr &I,
MachineBasicBlock *MBB) const;
MachineBasicBlock *EmitLoweredSegAlloca(MachineInstr &MI,
MachineBasicBlock *BB) const;
/// Emit nodes that will be selected as "test Op0,Op0", or something
/// equivalent, for use with the given M68k condition code.
SDValue EmitTest(SDValue Op0, unsigned M68kCC, const SDLoc &dl,
SelectionDAG &DAG) const;
/// Emit nodes that will be selected as "cmp Op0,Op1", or something
/// equivalent, for use with the given M68k condition code.
SDValue EmitCmp(SDValue Op0, SDValue Op1, unsigned M68kCC, const SDLoc &dl,
SelectionDAG &DAG) const;
/// Check whether the call is eligible for tail call optimization. Targets
/// that want to do tail call optimization should implement this function.
bool IsEligibleForTailCallOptimization(
SDValue Callee, CallingConv::ID CalleeCC, bool IsVarArg,
bool IsCalleeStructRet, bool IsCallerStructRet, Type *RetTy,
const SmallVectorImpl<ISD::OutputArg> &Outs,
const SmallVectorImpl<SDValue> &OutVals,
const SmallVectorImpl<ISD::InputArg> &Ins, SelectionDAG &DAG) const;
SDValue PerformDAGCombine(SDNode *N, DAGCombinerInfo &DCI) const override;
};
} // namespace llvm
#endif // M68kISELLOWERING_H

View File

@ -0,0 +1,94 @@
//===-- M68kInstrBuilder.h - Functions to build M68k insts --*- 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 exposes functions that may be used with BuildMI from the
/// MachineInstrBuilder.h file to handle M68k'isms in a clean way.
///
/// TODO The BuildMem function may be used with the BuildMI function to add
/// entire memory references in a single, typed, function call. M68k memory
/// references can be very complex expressions (described in the README), so
/// wrapping them up behind an easier to use interface makes sense.
/// Descriptions of the functions are included below.
///
/// For reference, the order of operands for memory references is:
/// (Operand), Base, Scale, Index, Displacement.
///
//===----------------------------------------------------------------------===//
//
#ifndef LLVM_LIB_TARGET_M68K_M68KINSTRBUILDER_H
#define LLVM_LIB_TARGET_M68K_M68KINSTRBUILDER_H
#include "llvm/ADT/SmallVector.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineInstr.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineMemOperand.h"
#include "llvm/CodeGen/MachineOperand.h"
#include "llvm/MC/MCInstrDesc.h"
#include <cassert>
namespace llvm {
namespace M68k {
static inline const MachineInstrBuilder &
addOffset(const MachineInstrBuilder &MIB, int Offset) {
return MIB.addImm(Offset);
}
/// addRegIndirectWithDisp - This function is used to add a memory reference
/// of the form (Offset, Base), i.e., one with no scale or index, but with a
/// displacement. An example is: (4,D0).
static inline const MachineInstrBuilder &
addRegIndirectWithDisp(const MachineInstrBuilder &MIB, Register Reg,
bool IsKill, int Offset) {
return MIB.addImm(Offset).addReg(Reg, getKillRegState(IsKill));
}
/// addFrameReference - This function is used to add a reference to the base of
/// an abstract object on the stack frame of the current function. This
/// reference has base register as the FrameIndex offset until it is resolved.
/// This allows a constant offset to be specified as well...
static inline const MachineInstrBuilder &
addFrameReference(const MachineInstrBuilder &MIB, int FI, int Offset = 0) {
MachineInstr *MI = MIB;
MachineFunction &MF = *MI->getParent()->getParent();
MachineFrameInfo &MFI = MF.getFrameInfo();
const MCInstrDesc &MCID = MI->getDesc();
auto Flags = MachineMemOperand::MONone;
if (MCID.mayLoad())
Flags |= MachineMemOperand::MOLoad;
if (MCID.mayStore())
Flags |= MachineMemOperand::MOStore;
MachineMemOperand *MMO = MF.getMachineMemOperand(
MachinePointerInfo::getFixedStack(MF, FI, Offset), Flags,
MFI.getObjectSize(FI), MFI.getObjectAlign(FI));
return MIB.addImm(Offset).addFrameIndex(FI).addMemOperand(MMO);
}
static inline const MachineInstrBuilder &
addMemOperand(const MachineInstrBuilder &MIB, int FI, int Offset = 0) {
MachineInstr *MI = MIB;
MachineFunction &MF = *MI->getParent()->getParent();
MachineFrameInfo &MFI = MF.getFrameInfo();
const MCInstrDesc &MCID = MI->getDesc();
auto Flags = MachineMemOperand::MONone;
if (MCID.mayLoad())
Flags |= MachineMemOperand::MOLoad;
if (MCID.mayStore())
Flags |= MachineMemOperand::MOStore;
MachineMemOperand *MMO = MF.getMachineMemOperand(
MachinePointerInfo::getFixedStack(MF, FI, Offset), Flags,
MFI.getObjectSize(FI), MFI.getObjectAlign(FI));
return MIB.addMemOperand(MMO);
}
} // end namespace M68k
} // end namespace llvm
#endif // LLVM_LIB_TARGET_M6800_M6800INSTRBUILDER_H

View File

@ -0,0 +1,870 @@
//===-- M68kInstrInfo.cpp - M68k Instruction 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 contains the M68k declaration of the TargetInstrInfo class.
///
//===----------------------------------------------------------------------===//
#include "M68kInstrInfo.h"
#include "M68kInstrBuilder.h"
#include "M68kMachineFunction.h"
#include "M68kTargetMachine.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/CodeGen/LivePhysRegs.h"
#include "llvm/CodeGen/LiveVariables.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/TargetRegistry.h"
#include <functional>
using namespace llvm;
#define DEBUG_TYPE "M68k-instr-info"
#define GET_INSTRINFO_CTOR_DTOR
#include "M68kGenInstrInfo.inc"
// Pin the vtable to this file.
void M68kInstrInfo::anchor() {}
M68kInstrInfo::M68kInstrInfo(const M68kSubtarget &STI)
: M68kGenInstrInfo(M68k::ADJCALLSTACKDOWN, M68k::ADJCALLSTACKUP, 0,
M68k::RET),
Subtarget(STI), RI(STI) {}
static M68k::CondCode getCondFromBranchOpc(unsigned BrOpc) {
switch (BrOpc) {
default:
return M68k::COND_INVALID;
case M68k::Beq8:
return M68k::COND_EQ;
case M68k::Bne8:
return M68k::COND_NE;
case M68k::Blt8:
return M68k::COND_LT;
case M68k::Ble8:
return M68k::COND_LE;
case M68k::Bgt8:
return M68k::COND_GT;
case M68k::Bge8:
return M68k::COND_GE;
case M68k::Bcs8:
return M68k::COND_CS;
case M68k::Bls8:
return M68k::COND_LS;
case M68k::Bhi8:
return M68k::COND_HI;
case M68k::Bcc8:
return M68k::COND_CC;
case M68k::Bmi8:
return M68k::COND_MI;
case M68k::Bpl8:
return M68k::COND_PL;
case M68k::Bvs8:
return M68k::COND_VS;
case M68k::Bvc8:
return M68k::COND_VC;
}
}
bool M68kInstrInfo::AnalyzeBranchImpl(MachineBasicBlock &MBB,
MachineBasicBlock *&TBB,
MachineBasicBlock *&FBB,
SmallVectorImpl<MachineOperand> &Cond,
bool AllowModify) const {
auto UncondBranch =
std::pair<MachineBasicBlock::reverse_iterator, MachineBasicBlock *>{
MBB.rend(), nullptr};
// Erase any instructions if allowed at the end of the scope.
std::vector<std::reference_wrapper<llvm::MachineInstr>> EraseList;
auto FinalizeOnReturn = llvm::make_scope_exit([&EraseList] {
std::for_each(EraseList.begin(), EraseList.end(),
[](decltype(EraseList)::value_type &ref) {
ref.get().eraseFromParent();
});
});
// Start from the bottom of the block and work up, examining the
// terminator instructions.
for (auto iter = MBB.rbegin(); iter != MBB.rend(); iter = std::next(iter)) {
unsigned Opcode = iter->getOpcode();
if (iter->isDebugInstr())
continue;
// Working from the bottom, when we see a non-terminator instruction, we're
// done.
if (!isUnpredicatedTerminator(*iter))
break;
// A terminator that isn't a branch can't easily be handled by this
// analysis.
if (!iter->isBranch())
return true;
// Handle unconditional branches.
if (Opcode == M68k::BRA8 || Opcode == M68k::BRA16) {
if (!iter->getOperand(0).isMBB())
return true;
UncondBranch = {iter, iter->getOperand(0).getMBB()};
// TBB is used to indicate the unconditional destination.
TBB = UncondBranch.second;
if (!AllowModify)
continue;
// If the block has any instructions after a JMP, erase them.
EraseList.insert(EraseList.begin(), MBB.rbegin(), iter);
Cond.clear();
FBB = nullptr;
// Erase the JMP if it's equivalent to a fall-through.
if (MBB.isLayoutSuccessor(UncondBranch.second)) {
TBB = nullptr;
EraseList.push_back(*iter);
UncondBranch = {MBB.rend(), nullptr};
}
continue;
}
// Handle conditional branches.
auto BranchCode = M68k::GetCondFromBranchOpc(Opcode);
// Can't handle indirect branch.
if (BranchCode == M68k::COND_INVALID)
return true;
// In practice we should never have an undef CCR operand, if we do
// abort here as we are not prepared to preserve the flag.
// ??? Is this required?
// if (iter->getOperand(1).isUndef())
// return true;
// Working from the bottom, handle the first conditional branch.
if (Cond.empty()) {
if (!iter->getOperand(0).isMBB())
return true;
MachineBasicBlock *CondBranchTarget = iter->getOperand(0).getMBB();
// If we see something like this:
//
// bcc l1
// bra l2
// ...
// l1:
// ...
// l2:
if (UncondBranch.first != MBB.rend()) {
assert(std::next(UncondBranch.first) == iter && "Wrong block layout.");
// And we are allowed to modify the block and the target block of the
// conditional branch is the direct successor of this block:
//
// bcc l1
// bra l2
// l1:
// ...
// l2:
//
// we change it to this if allowed:
//
// bncc l2
// l1:
// ...
// l2:
//
// Which is a bit more efficient.
if (AllowModify && MBB.isLayoutSuccessor(CondBranchTarget)) {
BranchCode = GetOppositeBranchCondition(BranchCode);
unsigned BNCC = GetCondBranchFromCond(BranchCode);
BuildMI(MBB, *UncondBranch.first, MBB.rfindDebugLoc(iter), get(BNCC))
.addMBB(UncondBranch.second);
EraseList.push_back(*iter);
EraseList.push_back(*UncondBranch.first);
TBB = UncondBranch.second;
FBB = nullptr;
Cond.push_back(MachineOperand::CreateImm(BranchCode));
// Otherwise preserve TBB, FBB and Cond as requested
} else {
TBB = CondBranchTarget;
FBB = UncondBranch.second;
Cond.push_back(MachineOperand::CreateImm(BranchCode));
}
UncondBranch = {MBB.rend(), nullptr};
continue;
}
TBB = CondBranchTarget;
FBB = nullptr;
Cond.push_back(MachineOperand::CreateImm(BranchCode));
continue;
}
// Handle subsequent conditional branches. Only handle the case where all
// conditional branches branch to the same destination and their condition
// opcodes fit one of the special multi-branch idioms.
assert(Cond.size() == 1);
assert(TBB);
// If the conditions are the same, we can leave them alone.
auto OldBranchCode = static_cast<M68k::CondCode>(Cond[0].getImm());
if (!iter->getOperand(0).isMBB())
return true;
auto NewTBB = iter->getOperand(0).getMBB();
if (OldBranchCode == BranchCode && TBB == NewTBB)
continue;
// If they differ we cannot do much here.
return true;
}
return false;
}
bool M68kInstrInfo::analyzeBranch(MachineBasicBlock &MBB,
MachineBasicBlock *&TBB,
MachineBasicBlock *&FBB,
SmallVectorImpl<MachineOperand> &Cond,
bool AllowModify) const {
return AnalyzeBranchImpl(MBB, TBB, FBB, Cond, AllowModify);
}
unsigned M68kInstrInfo::removeBranch(MachineBasicBlock &MBB,
int *BytesRemoved) const {
assert(!BytesRemoved && "code size not handled");
MachineBasicBlock::iterator I = MBB.end();
unsigned Count = 0;
while (I != MBB.begin()) {
--I;
if (I->isDebugValue())
continue;
if (I->getOpcode() != M68k::BRA8 &&
getCondFromBranchOpc(I->getOpcode()) == M68k::COND_INVALID)
break;
// Remove the branch.
I->eraseFromParent();
I = MBB.end();
++Count;
}
return Count;
}
unsigned M68kInstrInfo::insertBranch(
MachineBasicBlock &MBB, MachineBasicBlock *TBB, MachineBasicBlock *FBB,
ArrayRef<MachineOperand> Cond, const DebugLoc &DL, int *BytesAdded) const {
// Shouldn't be a fall through.
assert(TBB && "InsertBranch must not be told to insert a fallthrough");
assert((Cond.size() == 1 || Cond.size() == 0) &&
"M68k branch conditions have one component!");
assert(!BytesAdded && "code size not handled");
if (Cond.empty()) {
// Unconditional branch?
assert(!FBB && "Unconditional branch with multiple successors!");
BuildMI(&MBB, DL, get(M68k::BRA8)).addMBB(TBB);
return 1;
}
// If FBB is null, it is implied to be a fall-through block.
bool FallThru = FBB == nullptr;
// Conditional branch.
unsigned Count = 0;
M68k::CondCode CC = (M68k::CondCode)Cond[0].getImm();
unsigned Opc = GetCondBranchFromCond(CC);
BuildMI(&MBB, DL, get(Opc)).addMBB(TBB);
++Count;
if (!FallThru) {
// Two-way Conditional branch. Insert the second branch.
BuildMI(&MBB, DL, get(M68k::BRA8)).addMBB(FBB);
++Count;
}
return Count;
}
void M68kInstrInfo::AddSExt(MachineBasicBlock &MBB,
MachineBasicBlock::iterator I, DebugLoc DL,
unsigned Reg, MVT From, MVT To) const {
if (From == MVT::i8) {
unsigned R = Reg;
// EXT16 requires i16 register
if (To == MVT::i32) {
R = RI.getSubReg(Reg, M68k::MxSubRegIndex16Lo);
assert(R && "No viable SUB register available");
}
BuildMI(MBB, I, DL, get(M68k::EXT16), R).addReg(R);
}
if (To == MVT::i32)
BuildMI(MBB, I, DL, get(M68k::EXT32), Reg).addReg(Reg);
}
void M68kInstrInfo::AddZExt(MachineBasicBlock &MBB,
MachineBasicBlock::iterator I, DebugLoc DL,
unsigned Reg, MVT From, MVT To) const {
unsigned Mask, And;
if (From == MVT::i8)
Mask = 0xFF;
else
Mask = 0xFFFF;
if (To == MVT::i16)
And = M68k::AND16di;
else // i32
And = M68k::AND32di;
// TODO use xor r,r to decrease size
BuildMI(MBB, I, DL, get(And), Reg).addReg(Reg).addImm(Mask);
}
bool M68kInstrInfo::ExpandMOVX_RR(MachineInstrBuilder &MIB, MVT MVTDst,
MVT MVTSrc) const {
unsigned Move = MVTDst == MVT::i16 ? M68k::MOV16rr : M68k::MOV32rr;
unsigned Dst = MIB->getOperand(0).getReg();
unsigned Src = MIB->getOperand(1).getReg();
assert(Dst != Src && "You cannot use the same Regs with MOVX_RR");
const auto &TRI = getRegisterInfo();
const auto *RCDst = TRI.getMaximalPhysRegClass(Dst, MVTDst);
const auto *RCSrc = TRI.getMaximalPhysRegClass(Src, MVTSrc);
assert(RCDst && RCSrc && "Wrong use of MOVX_RR");
assert(RCDst != RCSrc && "You cannot use the same Reg Classes with MOVX_RR");
// We need to find the super source register that matches the size of Dst
unsigned SSrc = RI.getMatchingMegaReg(Src, RCDst);
assert(SSrc && "No viable MEGA register available");
DebugLoc DL = MIB->getDebugLoc();
// If it happens to that super source register is the destination register
// we do nothing
if (Dst == SSrc) {
LLVM_DEBUG(dbgs() << "Remove " << *MIB.getInstr() << '\n');
MIB->eraseFromParent();
} else { // otherwise we need to MOV
LLVM_DEBUG(dbgs() << "Expand " << *MIB.getInstr() << " to MOV\n");
MIB->setDesc(get(Move));
MIB->getOperand(1).setReg(SSrc);
}
return true;
}
/// Expand SExt MOVE pseudos into a MOV and a EXT if the operands are two
/// different registers or just EXT if it is the same register
bool M68kInstrInfo::ExpandMOVSZX_RR(MachineInstrBuilder &MIB, bool IsSigned,
MVT MVTDst, MVT MVTSrc) const {
LLVM_DEBUG(dbgs() << "Expand " << *MIB.getInstr() << " to ");
unsigned Move;
if (MVTDst == MVT::i16)
Move = M68k::MOV16rr;
else // i32
Move = M68k::MOV32rr;
unsigned Dst = MIB->getOperand(0).getReg();
unsigned Src = MIB->getOperand(1).getReg();
assert(Dst != Src && "You cannot use the same Regs with MOVSX_RR");
const auto &TRI = getRegisterInfo();
const auto *RCDst = TRI.getMaximalPhysRegClass(Dst, MVTDst);
const auto *RCSrc = TRI.getMaximalPhysRegClass(Src, MVTSrc);
assert(RCDst && RCSrc && "Wrong use of MOVSX_RR");
assert(RCDst != RCSrc && "You cannot use the same Reg Classes with MOVSX_RR");
// We need to find the super source register that matches the size of Dst
unsigned SSrc = RI.getMatchingMegaReg(Src, RCDst);
assert(SSrc && "No viable MEGA register available");
MachineBasicBlock &MBB = *MIB->getParent();
DebugLoc DL = MIB->getDebugLoc();
if (Dst != SSrc) {
LLVM_DEBUG(dbgs() << "Move and " << '\n');
BuildMI(MBB, MIB.getInstr(), DL, get(Move), Dst).addReg(SSrc);
}
if (IsSigned) {
LLVM_DEBUG(dbgs() << "Sign Extend" << '\n');
AddSExt(MBB, MIB.getInstr(), DL, Dst, MVTSrc, MVTDst);
} else {
LLVM_DEBUG(dbgs() << "Zero Extend" << '\n');
AddZExt(MBB, MIB.getInstr(), DL, Dst, MVTSrc, MVTDst);
}
MIB->eraseFromParent();
return true;
}
bool M68kInstrInfo::ExpandMOVSZX_RM(MachineInstrBuilder &MIB, bool IsSigned,
const MCInstrDesc &Desc, MVT MVTDst,
MVT MVTSrc) const {
LLVM_DEBUG(dbgs() << "Expand " << *MIB.getInstr() << " to LOAD and ");
unsigned Dst = MIB->getOperand(0).getReg();
// We need the subreg of Dst to make instruction verifier happy because the
// real machine instruction consumes and produces values of the same size and
// the registers the will be used here fall into different classes and this
// makes IV cry. We could of course use bigger operation but this will put
// some pressure on cache and memory so no.
unsigned SubDst =
RI.getSubReg(Dst, MVTSrc == MVT::i8 ? M68k::MxSubRegIndex8Lo
: M68k::MxSubRegIndex16Lo);
assert(SubDst && "No viable SUB register available");
// Make this a plain move
MIB->setDesc(Desc);
MIB->getOperand(0).setReg(SubDst);
MachineBasicBlock::iterator I = MIB.getInstr();
I++;
MachineBasicBlock &MBB = *MIB->getParent();
DebugLoc DL = MIB->getDebugLoc();
if (IsSigned) {
LLVM_DEBUG(dbgs() << "Sign Extend" << '\n');
AddSExt(MBB, I, DL, Dst, MVTSrc, MVTDst);
} else {
LLVM_DEBUG(dbgs() << "Zero Extend" << '\n');
AddZExt(MBB, I, DL, Dst, MVTSrc, MVTDst);
}
return true;
}
bool M68kInstrInfo::ExpandPUSH_POP(MachineInstrBuilder &MIB,
const MCInstrDesc &Desc, bool IsPush) const {
MachineBasicBlock::iterator I = MIB.getInstr();
I++;
MachineBasicBlock &MBB = *MIB->getParent();
MachineOperand MO = MIB->getOperand(0);
DebugLoc DL = MIB->getDebugLoc();
if (IsPush)
BuildMI(MBB, I, DL, Desc).addReg(RI.getStackRegister()).add(MO);
else
BuildMI(MBB, I, DL, Desc, MO.getReg()).addReg(RI.getStackRegister());
MIB->eraseFromParent();
return true;
}
bool M68kInstrInfo::ExpandCCR(MachineInstrBuilder &MIB, bool IsToCCR) const {
// Replace the pseudo instruction with the real one
if (IsToCCR)
MIB->setDesc(get(M68k::MOV16cd));
else
// FIXME M68010 or later is required
MIB->setDesc(get(M68k::MOV16dc));
// Promote used register to the next class
auto &Opd = MIB->getOperand(1);
Opd.setReg(getRegisterInfo().getMatchingSuperReg(
Opd.getReg(), M68k::MxSubRegIndex8Lo, &M68k::DR16RegClass));
return true;
}
bool M68kInstrInfo::ExpandMOVEM(MachineInstrBuilder &MIB,
const MCInstrDesc &Desc, bool IsRM) const {
int Reg = 0, Offset = 0, Base = 0;
auto XR32 = RI.getRegClass(M68k::XR32RegClassID);
auto DL = MIB->getDebugLoc();
auto MI = MIB.getInstr();
auto &MBB = *MIB->getParent();
if (IsRM) {
Reg = MIB->getOperand(0).getReg();
Offset = MIB->getOperand(1).getImm();
Base = MIB->getOperand(2).getReg();
} else {
Offset = MIB->getOperand(0).getImm();
Base = MIB->getOperand(1).getReg();
Reg = MIB->getOperand(2).getReg();
}
// If the register is not in XR32 then it is smaller than 32 bit, we
// implicitly promote it to 32
if (!XR32->contains(Reg)) {
Reg = RI.getMatchingMegaReg(Reg, XR32);
assert(Reg && "Has not meaningful MEGA register");
}
unsigned Mask = 1 << RI.getSpillRegisterOrder(Reg);
if (IsRM) {
BuildMI(MBB, MI, DL, Desc)
.addImm(Mask)
.addImm(Offset)
.addReg(Base)
.addReg(Reg, RegState::ImplicitDefine)
.copyImplicitOps(*MIB);
} else {
BuildMI(MBB, MI, DL, Desc)
.addImm(Offset)
.addReg(Base)
.addImm(Mask)
.addReg(Reg, RegState::Implicit)
.copyImplicitOps(*MIB);
}
MIB->eraseFromParent();
return true;
}
/// Expand a single-def pseudo instruction to a two-addr
/// instruction with two undef reads of the register being defined.
/// This is used for mapping:
/// %d0 = SETCS_C32d
/// to:
/// %d0 = SUBX32dd %d0<undef>, %d0<undef>
///
static bool Expand2AddrUndef(MachineInstrBuilder &MIB,
const MCInstrDesc &Desc) {
assert(Desc.getNumOperands() == 3 && "Expected two-addr instruction.");
unsigned Reg = MIB->getOperand(0).getReg();
MIB->setDesc(Desc);
// MachineInstr::addOperand() will insert explicit operands before any
// implicit operands.
MIB.addReg(Reg, RegState::Undef).addReg(Reg, RegState::Undef);
// But we don't trust that.
assert(MIB->getOperand(1).getReg() == Reg &&
MIB->getOperand(2).getReg() == Reg && "Misplaced operand");
return true;
}
bool M68kInstrInfo::expandPostRAPseudo(MachineInstr &MI) const {
MachineInstrBuilder MIB(*MI.getParent()->getParent(), MI);
switch (MI.getOpcode()) {
case M68k::PUSH8d:
return ExpandPUSH_POP(MIB, get(M68k::MOV8ed), true);
case M68k::PUSH16d:
return ExpandPUSH_POP(MIB, get(M68k::MOV16er), true);
case M68k::PUSH32r:
return ExpandPUSH_POP(MIB, get(M68k::MOV32er), true);
case M68k::POP8d:
return ExpandPUSH_POP(MIB, get(M68k::MOV8do), false);
case M68k::POP16d:
return ExpandPUSH_POP(MIB, get(M68k::MOV16ro), false);
case M68k::POP32r:
return ExpandPUSH_POP(MIB, get(M68k::MOV32ro), false);
case M68k::SETCS_C8d:
return Expand2AddrUndef(MIB, get(M68k::SUBX8dd));
case M68k::SETCS_C16d:
return Expand2AddrUndef(MIB, get(M68k::SUBX16dd));
case M68k::SETCS_C32d:
return Expand2AddrUndef(MIB, get(M68k::SUBX32dd));
}
return false;
}
bool M68kInstrInfo::isPCRelRegisterOperandLegal(
const MachineOperand &MO) const {
assert(MO.isReg());
const auto *MI = MO.getParent();
const uint8_t *Beads = M68k::getMCInstrBeads(MI->getOpcode());
assert(*Beads);
// Only addressing mode k has (non-pc) register with PCRel
// So we're looking for EA Beads equal to
// `3Bits<011>_1Bit<1>_2Bits<11>`
// FIXME: There is an important caveat and two assumptions
// here: The caveat is that EA encoding always sit on the LSB.
// Where the assumptions are that if there are more than one
// operands, the EA encoding for the source operand always sit
// on the LSB. At the same time, k addressing mode can not be used
// on destination operand.
// The last assumption is kinda dirty so we need to find a way around
// it
const uint8_t EncEAk[3] = {0b011, 0b1, 0b11};
for (const uint8_t Pat : EncEAk) {
uint8_t Bead = *(Beads++);
if (!Bead)
return false;
switch (Bead & 0xF) {
default:
return false;
case M68kBeads::Bits1:
case M68kBeads::Bits2:
case M68kBeads::Bits3: {
uint8_t Val = (Bead & 0xF0) >> 4;
if (Val != Pat)
return false;
}
}
}
return true;
}
void M68kInstrInfo::copyPhysReg(MachineBasicBlock &MBB,
MachineBasicBlock::iterator MI,
const DebugLoc &DL, MCRegister DstReg,
MCRegister SrcReg, bool KillSrc) const {
unsigned Opc = 0;
// First deal with the normal symmetric copies.
if (M68k::XR32RegClass.contains(DstReg, SrcReg))
Opc = M68k::MOV32rr;
else if (M68k::XR16RegClass.contains(DstReg, SrcReg))
Opc = M68k::MOV16rr;
else if (M68k::DR8RegClass.contains(DstReg, SrcReg))
Opc = M68k::MOV8dd;
if (Opc) {
BuildMI(MBB, MI, DL, get(Opc), DstReg)
.addReg(SrcReg, getKillRegState(KillSrc));
return;
}
// Now deal with asymmetrically sized copies. The cases that follow are upcast
// moves.
//
// NOTE
// These moves are not aware of type nature of these values and thus
// won't do any SExt or ZExt and upper bits will basically contain garbage.
MachineInstrBuilder MIB(*MBB.getParent(), MI);
if (M68k::DR8RegClass.contains(SrcReg)) {
if (M68k::XR16RegClass.contains(DstReg))
Opc = M68k::MOVXd16d8;
else if (M68k::XR32RegClass.contains(DstReg))
Opc = M68k::MOVXd32d8;
} else if (M68k::XR16RegClass.contains(SrcReg) &&
M68k::XR32RegClass.contains(DstReg))
Opc = M68k::MOVXd32d16;
if (Opc) {
BuildMI(MBB, MI, DL, get(Opc), DstReg)
.addReg(SrcReg, getKillRegState(KillSrc));
return;
}
bool FromCCR = SrcReg == M68k::CCR;
bool FromSR = SrcReg == M68k::SR;
bool ToCCR = DstReg == M68k::CCR;
bool ToSR = DstReg == M68k::SR;
if (FromCCR) {
assert(M68k::DR8RegClass.contains(DstReg) &&
"Need DR8 register to copy CCR");
Opc = M68k::MOV8dc;
} else if (ToCCR) {
assert(M68k::DR8RegClass.contains(SrcReg) &&
"Need DR8 register to copy CCR");
Opc = M68k::MOV8cd;
} else if (FromSR || ToSR)
llvm_unreachable("Cannot emit SR copy instruction");
if (Opc) {
BuildMI(MBB, MI, DL, get(Opc), DstReg)
.addReg(SrcReg, getKillRegState(KillSrc));
return;
}
LLVM_DEBUG(dbgs() << "Cannot copy " << RI.getName(SrcReg) << " to "
<< RI.getName(DstReg) << '\n');
llvm_unreachable("Cannot emit physreg copy instruction");
}
namespace {
unsigned getLoadStoreRegOpcode(unsigned Reg, const TargetRegisterClass *RC,
const TargetRegisterInfo *TRI,
const M68kSubtarget &STI, bool load) {
switch (TRI->getRegSizeInBits(*RC)) {
default:
llvm_unreachable("Unknown spill size");
case 8:
if (M68k::DR8RegClass.hasSubClassEq(RC))
return load ? M68k::MOVM8mp_P : M68k::MOVM8pm_P;
if (M68k::CCRCRegClass.hasSubClassEq(RC))
return load ? M68k::MOV16cp : M68k::MOV16pc;
llvm_unreachable("Unknown 1-byte regclass");
case 16:
assert(M68k::XR16RegClass.hasSubClassEq(RC) && "Unknown 2-byte regclass");
return load ? M68k::MOVM16mp_P : M68k::MOVM16pm_P;
case 32:
assert(M68k::XR32RegClass.hasSubClassEq(RC) && "Unknown 4-byte regclass");
return load ? M68k::MOVM32mp_P : M68k::MOVM32pm_P;
}
}
unsigned getStoreRegOpcode(unsigned SrcReg, const TargetRegisterClass *RC,
const TargetRegisterInfo *TRI,
const M68kSubtarget &STI) {
return getLoadStoreRegOpcode(SrcReg, RC, TRI, STI, false);
}
unsigned getLoadRegOpcode(unsigned DstReg, const TargetRegisterClass *RC,
const TargetRegisterInfo *TRI,
const M68kSubtarget &STI) {
return getLoadStoreRegOpcode(DstReg, RC, TRI, STI, true);
}
} // end anonymous namespace
bool M68kInstrInfo::getStackSlotRange(const TargetRegisterClass *RC,
unsigned SubIdx, unsigned &Size,
unsigned &Offset,
const MachineFunction &MF) const {
// The slot size must be the maximum size so we can easily use MOVEM.L
Size = 4;
Offset = 0;
return true;
}
void M68kInstrInfo::storeRegToStackSlot(MachineBasicBlock &MBB,
MachineBasicBlock::iterator MI,
Register SrcReg, bool IsKill,
int FrameIndex,
const TargetRegisterClass *RC,
const TargetRegisterInfo *TRI) const {
const MachineFunction &MF = *MBB.getParent();
assert(MF.getFrameInfo().getObjectSize(FrameIndex) == 4 &&
"Stack slot too small for store");
unsigned Opc = getStoreRegOpcode(SrcReg, RC, TRI, Subtarget);
DebugLoc DL = MBB.findDebugLoc(MI);
// (0,FrameIndex) <- $reg
M68k::addFrameReference(BuildMI(MBB, MI, DL, get(Opc)), FrameIndex)
.addReg(SrcReg, getKillRegState(IsKill));
}
void M68kInstrInfo::loadRegFromStackSlot(MachineBasicBlock &MBB,
MachineBasicBlock::iterator MI,
Register DstReg, int FrameIndex,
const TargetRegisterClass *RC,
const TargetRegisterInfo *TRI) const {
const MachineFunction &MF = *MBB.getParent();
assert(MF.getFrameInfo().getObjectSize(FrameIndex) == 4 &&
"Stack slot too small for store");
unsigned Opc = getLoadRegOpcode(DstReg, RC, TRI, Subtarget);
DebugLoc DL = MBB.findDebugLoc(MI);
M68k::addFrameReference(BuildMI(MBB, MI, DL, get(Opc), DstReg), FrameIndex);
}
/// Return a virtual register initialized with the the global base register
/// value. Output instructions required to initialize the register in the
/// function entry block, if necessary.
///
/// TODO Move this function to M68kMachineFunctionInfo.
unsigned M68kInstrInfo::getGlobalBaseReg(MachineFunction *MF) const {
M68kMachineFunctionInfo *MxFI = MF->getInfo<M68kMachineFunctionInfo>();
unsigned GlobalBaseReg = MxFI->getGlobalBaseReg();
if (GlobalBaseReg != 0)
return GlobalBaseReg;
// Create the register. The code to initialize it is inserted later,
// by the CGBR pass (below).
//
// NOTE
// Normally M68k uses A5 register as global base pointer but this will
// create unnecessary spill if we use less then 4 registers in code; since A5
// is callee-save anyway we could try to allocate caller-save first and if
// lucky get one, otherwise it does not really matter which callee-save to
// use.
MachineRegisterInfo &RegInfo = MF->getRegInfo();
GlobalBaseReg = RegInfo.createVirtualRegister(&M68k::AR32_NOSPRegClass);
MxFI->setGlobalBaseReg(GlobalBaseReg);
return GlobalBaseReg;
}
std::pair<unsigned, unsigned>
M68kInstrInfo::decomposeMachineOperandsTargetFlags(unsigned TF) const {
return std::make_pair(TF, 0u);
}
ArrayRef<std::pair<unsigned, const char *>>
M68kInstrInfo::getSerializableDirectMachineOperandTargetFlags() const {
using namespace M68kII;
static const std::pair<unsigned, const char *> TargetFlags[] = {
{MO_ABSOLUTE_ADDRESS, "m68k-absolute"},
{MO_PC_RELATIVE_ADDRESS, "m68k-pcrel"},
{MO_GOT, "m68k-got"},
{MO_GOTOFF, "m68k-gotoff"},
{MO_GOTPCREL, "m68k-gotpcrel"},
{MO_PLT, "m68k-plt"}};
return makeArrayRef(TargetFlags);
}
namespace {
/// Create Global Base Reg pass. This initializes the PIC global base register
struct CGBR : public MachineFunctionPass {
static char ID;
CGBR() : MachineFunctionPass(ID) {}
bool runOnMachineFunction(MachineFunction &MF) override {
const M68kSubtarget &STI = MF.getSubtarget<M68kSubtarget>();
M68kMachineFunctionInfo *MxFI = MF.getInfo<M68kMachineFunctionInfo>();
unsigned GlobalBaseReg = MxFI->getGlobalBaseReg();
// If we didn't need a GlobalBaseReg, don't insert code.
if (GlobalBaseReg == 0)
return false;
// Insert the set of GlobalBaseReg into the first MBB of the function
MachineBasicBlock &FirstMBB = MF.front();
MachineBasicBlock::iterator MBBI = FirstMBB.begin();
DebugLoc DL = FirstMBB.findDebugLoc(MBBI);
const M68kInstrInfo *TII = STI.getInstrInfo();
// Generate lea (__GLOBAL_OFFSET_TABLE_,%PC), %A5
BuildMI(FirstMBB, MBBI, DL, TII->get(M68k::LEA32q), GlobalBaseReg)
.addExternalSymbol("_GLOBAL_OFFSET_TABLE_", M68kII::MO_GOTPCREL);
return true;
}
StringRef getPassName() const override {
return "M68k PIC Global Base Reg Initialization";
}
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.setPreservesCFG();
MachineFunctionPass::getAnalysisUsage(AU);
}
};
} // namespace
char CGBR::ID = 0;
FunctionPass *llvm::createM68kGlobalBaseRegPass() { return new CGBR(); }

View File

@ -0,0 +1,342 @@
//===-- M68kInstrInfo.h - M68k Instruction 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 contains the M68k implementation of the TargetInstrInfo class.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIB_TARGET_M68K_M68KINSTRINFO_H
#define LLVM_LIB_TARGET_M68K_M68KINSTRINFO_H
#include "M68k.h"
#include "M68kRegisterInfo.h"
#include "MCTargetDesc/M68kBaseInfo.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/TargetInstrInfo.h"
#define GET_INSTRINFO_HEADER
#include "M68kGenInstrInfo.inc"
namespace llvm {
class M68kSubtarget;
namespace M68k {
// Forward declaration
const uint8_t *getMCInstrBeads(unsigned Opcode);
// These MUST be kept in sync with codes definitions in M68kInstrInfo.td
enum CondCode {
COND_T = 0, // True
COND_F = 1, // False
COND_HI = 2, // High
COND_LS = 3, // Less or Same
COND_CC = 4, // Carry Clear
COND_CS = 5, // Carry Set
COND_NE = 6, // Not Equal
COND_EQ = 7, // Equal
COND_VC = 8, // Overflow Clear
COND_VS = 9, // Overflow Set
COND_PL = 10, // Plus
COND_MI = 11, // Minus
COND_GE = 12, // Greater or Equal
COND_LT = 13, // Less Than
COND_GT = 14, // Greater Than
COND_LE = 15, // Less or Equal
LAST_VALID_COND = COND_LE,
COND_INVALID
};
// FIXME would be nice tablegen to generate these predicates and converters
// mb tag based
static inline M68k::CondCode GetOppositeBranchCondition(M68k::CondCode CC) {
switch (CC) {
default:
llvm_unreachable("Illegal condition code!");
case M68k::COND_T:
return M68k::COND_F;
case M68k::COND_F:
return M68k::COND_T;
case M68k::COND_HI:
return M68k::COND_LS;
case M68k::COND_LS:
return M68k::COND_HI;
case M68k::COND_CC:
return M68k::COND_CS;
case M68k::COND_CS:
return M68k::COND_CC;
case M68k::COND_NE:
return M68k::COND_EQ;
case M68k::COND_EQ:
return M68k::COND_NE;
case M68k::COND_VC:
return M68k::COND_VS;
case M68k::COND_VS:
return M68k::COND_VC;
case M68k::COND_PL:
return M68k::COND_MI;
case M68k::COND_MI:
return M68k::COND_PL;
case M68k::COND_GE:
return M68k::COND_LT;
case M68k::COND_LT:
return M68k::COND_GE;
case M68k::COND_GT:
return M68k::COND_LE;
case M68k::COND_LE:
return M68k::COND_GT;
}
}
static inline unsigned GetCondBranchFromCond(M68k::CondCode CC) {
switch (CC) {
default:
llvm_unreachable("Illegal condition code!");
case M68k::COND_EQ:
return M68k::Beq8;
case M68k::COND_NE:
return M68k::Bne8;
case M68k::COND_LT:
return M68k::Blt8;
case M68k::COND_LE:
return M68k::Ble8;
case M68k::COND_GT:
return M68k::Bgt8;
case M68k::COND_GE:
return M68k::Bge8;
case M68k::COND_CS:
return M68k::Bcs8;
case M68k::COND_LS:
return M68k::Bls8;
case M68k::COND_HI:
return M68k::Bhi8;
case M68k::COND_CC:
return M68k::Bcc8;
case M68k::COND_MI:
return M68k::Bmi8;
case M68k::COND_PL:
return M68k::Bpl8;
case M68k::COND_VS:
return M68k::Bvs8;
case M68k::COND_VC:
return M68k::Bvc8;
}
}
static inline M68k::CondCode GetCondFromBranchOpc(unsigned Opcode) {
switch (Opcode) {
default:
return M68k::COND_INVALID;
case M68k::Beq8:
return M68k::COND_EQ;
case M68k::Bne8:
return M68k::COND_NE;
case M68k::Blt8:
return M68k::COND_LT;
case M68k::Ble8:
return M68k::COND_LE;
case M68k::Bgt8:
return M68k::COND_GT;
case M68k::Bge8:
return M68k::COND_GE;
case M68k::Bcs8:
return M68k::COND_CS;
case M68k::Bls8:
return M68k::COND_LS;
case M68k::Bhi8:
return M68k::COND_HI;
case M68k::Bcc8:
return M68k::COND_CC;
case M68k::Bmi8:
return M68k::COND_MI;
case M68k::Bpl8:
return M68k::COND_PL;
case M68k::Bvs8:
return M68k::COND_VS;
case M68k::Bvc8:
return M68k::COND_VC;
}
}
static inline unsigned IsCMP(unsigned Op) {
switch (Op) {
default:
return false;
case M68k::CMP8dd:
case M68k::CMP8df:
case M68k::CMP8di:
case M68k::CMP8dj:
case M68k::CMP8dp:
case M68k::CMP16dd:
case M68k::CMP16df:
case M68k::CMP16di:
case M68k::CMP16dj:
case M68k::CMP16dp:
return true;
}
}
static inline bool IsSETCC(unsigned SETCC) {
switch (SETCC) {
default:
return false;
case M68k::SETd8eq:
case M68k::SETd8ne:
case M68k::SETd8lt:
case M68k::SETd8ge:
case M68k::SETd8le:
case M68k::SETd8gt:
case M68k::SETd8cs:
case M68k::SETd8cc:
case M68k::SETd8ls:
case M68k::SETd8hi:
case M68k::SETd8pl:
case M68k::SETd8mi:
case M68k::SETd8vc:
case M68k::SETd8vs:
case M68k::SETj8eq:
case M68k::SETj8ne:
case M68k::SETj8lt:
case M68k::SETj8ge:
case M68k::SETj8le:
case M68k::SETj8gt:
case M68k::SETj8cs:
case M68k::SETj8cc:
case M68k::SETj8ls:
case M68k::SETj8hi:
case M68k::SETj8pl:
case M68k::SETj8mi:
case M68k::SETj8vc:
case M68k::SETj8vs:
case M68k::SETp8eq:
case M68k::SETp8ne:
case M68k::SETp8lt:
case M68k::SETp8ge:
case M68k::SETp8le:
case M68k::SETp8gt:
case M68k::SETp8cs:
case M68k::SETp8cc:
case M68k::SETp8ls:
case M68k::SETp8hi:
case M68k::SETp8pl:
case M68k::SETp8mi:
case M68k::SETp8vc:
case M68k::SETp8vs:
return true;
}
}
} // namespace M68k
class M68kInstrInfo : public M68kGenInstrInfo {
virtual void anchor();
protected:
const M68kSubtarget &Subtarget;
const M68kRegisterInfo RI;
public:
explicit M68kInstrInfo(const M68kSubtarget &STI);
static const M68kInstrInfo *create(M68kSubtarget &STI);
/// TargetInstrInfo is a superset of MRegister info. As such, whenever a
/// client has an instance of instruction info, it should always be able to
/// get register info as well (through this method).
const M68kRegisterInfo &getRegisterInfo() const { return RI; };
bool analyzeBranch(MachineBasicBlock &MBB, MachineBasicBlock *&TBB,
MachineBasicBlock *&FBB,
SmallVectorImpl<MachineOperand> &Cond,
bool AllowModify) const override;
bool AnalyzeBranchImpl(MachineBasicBlock &MBB, MachineBasicBlock *&TBB,
MachineBasicBlock *&FBB,
SmallVectorImpl<MachineOperand> &Cond,
bool AllowModify) const;
unsigned removeBranch(MachineBasicBlock &MBB,
int *BytesRemoved = nullptr) const override;
unsigned insertBranch(MachineBasicBlock &MBB, MachineBasicBlock *TBB,
MachineBasicBlock *FBB, ArrayRef<MachineOperand> Cond,
const DebugLoc &DL,
int *BytesAdded = nullptr) const override;
void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI,
const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg,
bool KillSrc) const override;
bool getStackSlotRange(const TargetRegisterClass *RC, unsigned SubIdx,
unsigned &Size, unsigned &Offset,
const MachineFunction &MF) const override;
void storeRegToStackSlot(MachineBasicBlock &MBB,
MachineBasicBlock::iterator MI, Register SrcReg,
bool IsKill, int FrameIndex,
const TargetRegisterClass *RC,
const TargetRegisterInfo *TRI) const override;
void loadRegFromStackSlot(MachineBasicBlock &MBB,
MachineBasicBlock::iterator MI, Register DestReg,
int FrameIndex, const TargetRegisterClass *RC,
const TargetRegisterInfo *TRI) const override;
bool expandPostRAPseudo(MachineInstr &MI) const override;
bool isPCRelRegisterOperandLegal(const MachineOperand &MO) const override;
/// Add appropriate SExt nodes
void AddSExt(MachineBasicBlock &MBB, MachineBasicBlock::iterator I,
DebugLoc DL, unsigned Reg, MVT From, MVT To) const;
/// Add appropriate ZExt nodes
void AddZExt(MachineBasicBlock &MBB, MachineBasicBlock::iterator I,
DebugLoc DL, unsigned Reg, MVT From, MVT To) const;
/// Move across register classes without extension
bool ExpandMOVX_RR(MachineInstrBuilder &MIB, MVT MVTDst, MVT MVTSrc) const;
/// Move from register and extend
bool ExpandMOVSZX_RR(MachineInstrBuilder &MIB, bool IsSigned, MVT MVTDst,
MVT MVTSrc) const;
/// Move from memory and extend
bool ExpandMOVSZX_RM(MachineInstrBuilder &MIB, bool IsSigned,
const MCInstrDesc &Desc, MVT MVTDst, MVT MVTSrc) const;
/// Push/Pop to/from stack
bool ExpandPUSH_POP(MachineInstrBuilder &MIB, const MCInstrDesc &Desc,
bool IsPush) const;
/// Moves to/from CCR
bool ExpandCCR(MachineInstrBuilder &MIB, bool IsToCCR) const;
/// Expand all MOVEM pseudos into real MOVEMs
bool ExpandMOVEM(MachineInstrBuilder &MIB, const MCInstrDesc &Desc,
bool IsRM) const;
/// Return a virtual register initialized with the the global base register
/// value. Output instructions required to initialize the register in the
/// function entry block, if necessary.
unsigned getGlobalBaseReg(MachineFunction *MF) const;
std::pair<unsigned, unsigned>
decomposeMachineOperandsTargetFlags(unsigned TF) const override;
ArrayRef<std::pair<unsigned, const char *>>
getSerializableDirectMachineOperandTargetFlags() const override;
};
} // namespace llvm
#endif

View File

@ -0,0 +1,170 @@
//===-- M68kMCInstLower.cpp - M68k MachineInstr to MCInst ---*- 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 contains code to lower M68k MachineInstrs to their
/// corresponding MCInst records.
///
//===----------------------------------------------------------------------===//
#include "M68kMCInstLower.h"
#include "M68kAsmPrinter.h"
#include "M68kInstrInfo.h"
#include "MCTargetDesc/M68kBaseInfo.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineInstr.h"
#include "llvm/CodeGen/MachineOperand.h"
#include "llvm/IR/Mangler.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCInst.h"
using namespace llvm;
#define DEBUG_TYPE "m68k-mc-inst-lower"
M68kMCInstLower::M68kMCInstLower(MachineFunction &MF, M68kAsmPrinter &AP)
: Ctx(MF.getContext()), MF(MF), TM(MF.getTarget()), MAI(*TM.getMCAsmInfo()),
AsmPrinter(AP) {}
MCSymbol *
M68kMCInstLower::GetSymbolFromOperand(const MachineOperand &MO) const {
assert((MO.isGlobal() || MO.isSymbol() || MO.isMBB()) &&
"Isn't a symbol reference");
const auto &TT = TM.getTargetTriple();
if (MO.isGlobal() && TT.isOSBinFormatELF())
return AsmPrinter.getSymbolPreferLocal(*MO.getGlobal());
const DataLayout &DL = MF.getDataLayout();
MCSymbol *Sym = nullptr;
SmallString<128> Name;
StringRef Suffix;
if (!Suffix.empty())
Name += DL.getPrivateGlobalPrefix();
if (MO.isGlobal()) {
const GlobalValue *GV = MO.getGlobal();
AsmPrinter.getNameWithPrefix(Name, GV);
} else if (MO.isSymbol()) {
Mangler::getNameWithPrefix(Name, MO.getSymbolName(), DL);
} else if (MO.isMBB()) {
assert(Suffix.empty());
Sym = MO.getMBB()->getSymbol();
}
Name += Suffix;
if (!Sym)
Sym = Ctx.getOrCreateSymbol(Name);
return Sym;
}
MCOperand M68kMCInstLower::LowerSymbolOperand(const MachineOperand &MO,
MCSymbol *Sym) const {
// FIXME We would like an efficient form for this, so we don't have to do a
// lot of extra uniquing. This fixme is originally from X86
const MCExpr *Expr = nullptr;
MCSymbolRefExpr::VariantKind RefKind = MCSymbolRefExpr::VK_None;
switch (MO.getTargetFlags()) {
default:
llvm_unreachable("Unknown target flag on GV operand");
case M68kII::MO_NO_FLAG:
case M68kII::MO_ABSOLUTE_ADDRESS:
case M68kII::MO_PC_RELATIVE_ADDRESS:
break;
case M68kII::MO_GOTPCREL:
RefKind = MCSymbolRefExpr::VK_GOTPCREL;
break;
case M68kII::MO_GOT:
RefKind = MCSymbolRefExpr::VK_GOT;
break;
case M68kII::MO_GOTOFF:
RefKind = MCSymbolRefExpr::VK_GOTOFF;
break;
case M68kII::MO_PLT:
RefKind = MCSymbolRefExpr::VK_PLT;
break;
}
if (!Expr) {
Expr = MCSymbolRefExpr::create(Sym, RefKind, Ctx);
}
if (!MO.isJTI() && !MO.isMBB() && MO.getOffset()) {
Expr = MCBinaryExpr::createAdd(
Expr, MCConstantExpr::create(MO.getOffset(), Ctx), Ctx);
}
return MCOperand::createExpr(Expr);
}
Optional<MCOperand>
M68kMCInstLower::LowerOperand(const MachineInstr *MI,
const MachineOperand &MO) const {
switch (MO.getType()) {
default:
llvm_unreachable("unknown operand type");
case MachineOperand::MO_Register:
// Ignore all implicit register operands.
if (MO.isImplicit())
return None;
return MCOperand::createReg(MO.getReg());
case MachineOperand::MO_Immediate:
return MCOperand::createImm(MO.getImm());
case MachineOperand::MO_MachineBasicBlock:
case MachineOperand::MO_GlobalAddress:
case MachineOperand::MO_ExternalSymbol:
return LowerSymbolOperand(MO, GetSymbolFromOperand(MO));
case MachineOperand::MO_MCSymbol:
return LowerSymbolOperand(MO, MO.getMCSymbol());
case MachineOperand::MO_JumpTableIndex:
return LowerSymbolOperand(MO, AsmPrinter.GetJTISymbol(MO.getIndex()));
case MachineOperand::MO_ConstantPoolIndex:
return LowerSymbolOperand(MO, AsmPrinter.GetCPISymbol(MO.getIndex()));
case MachineOperand::MO_BlockAddress:
return LowerSymbolOperand(
MO, AsmPrinter.GetBlockAddressSymbol(MO.getBlockAddress()));
case MachineOperand::MO_RegisterMask:
// Ignore call clobbers.
return None;
}
}
void M68kMCInstLower::Lower(const MachineInstr *MI, MCInst &OutMI) const {
unsigned Opcode = MI->getOpcode();
OutMI.setOpcode(Opcode);
for (unsigned i = 0, e = MI->getNumOperands(); i != e; ++i) {
const MachineOperand &MO = MI->getOperand(i);
Optional<MCOperand> MCOp = LowerOperand(MI, MO);
if (MCOp.hasValue() && MCOp.getValue().isValid())
OutMI.addOperand(MCOp.getValue());
}
// TAILJMPj, TAILJMPq - Lower to the correct jump instructions.
if (Opcode == M68k::TAILJMPj || Opcode == M68k::TAILJMPq) {
assert(OutMI.getNumOperands() == 1 && "Unexpected number of operands");
switch (Opcode) {
case M68k::TAILJMPj:
Opcode = M68k::JMP32j;
break;
case M68k::TAILJMPq:
Opcode = M68k::BRA8;
break;
}
OutMI.setOpcode(Opcode);
}
}

View File

@ -0,0 +1,54 @@
//===-- M68kMCInstLower.h - Lower MachineInstr to MCInst -----*- 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 contains code to lower M68k MachineInstrs to their
/// corresponding MCInst records.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIB_TARGET_M68K_M68KMCINSTLOWER_H
#define LLVM_LIB_TARGET_M68K_M68KMCINSTLOWER_H
#include "llvm/CodeGen/MachineOperand.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/Target/TargetMachine.h"
namespace llvm {
class MCContext;
class MCInst;
class MCOperand;
class MachineInstr;
class MachineFunction;
class M68kAsmPrinter;
/// This class is used to lower an MachineInstr into an MCInst.
class M68kMCInstLower {
typedef MachineOperand::MachineOperandType MachineOperandType;
MCContext &Ctx;
MachineFunction &MF;
const TargetMachine &TM;
const MCAsmInfo &MAI;
M68kAsmPrinter &AsmPrinter;
public:
M68kMCInstLower(MachineFunction &MF, M68kAsmPrinter &AP);
/// Lower an MO_GlobalAddress or MO_ExternalSymbol operand to an MCSymbol.
MCSymbol *GetSymbolFromOperand(const MachineOperand &MO) const;
MCOperand LowerSymbolOperand(const MachineOperand &MO, MCSymbol *Sym) const;
Optional<MCOperand> LowerOperand(const MachineInstr *MI,
const MachineOperand &MO) const;
void Lower(const MachineInstr *MI, MCInst &OutMI) const;
};
} // namespace llvm
#endif

View File

@ -0,0 +1,20 @@
//===-- M68kMachineFunctionInfo.cpp - M68k private data ----*- 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
//
//===----------------------------------------------------------------------===//
#include "M68kMachineFunction.h"
#include "M68kInstrInfo.h"
#include "M68kSubtarget.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/IR/Function.h"
using namespace llvm;
void M68kMachineFunctionInfo::anchor() {}

View File

@ -0,0 +1,115 @@
//===-- M68kMachineFunctionInfo.h - M68k private data ---------*- 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 declares the M68k specific subclass of MachineFunctionInfo.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIB_TARGET_M68K_M68KMACHINEFUNCTION_H
#define LLVM_LIB_TARGET_M68K_M68KMACHINEFUNCTION_H
#include "llvm/CodeGen/CallingConvLower.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/Support/MachineValueType.h"
namespace llvm {
class M68kMachineFunctionInfo : public MachineFunctionInfo {
MachineFunction &MF;
/// Non-zero if the function has base pointer and makes call to
/// llvm.eh.sjlj.setjmp. When non-zero, the value is a displacement from the
/// frame pointer to a slot where the base pointer is stashed.
signed char RestoreBasePointerOffset = 0;
/// Size of the callee-saved register portion of the stack frame in bytes.
unsigned CalleeSavedFrameSize = 0;
/// Number of bytes function pops on return (in addition to the space used by
/// the return address). Used on windows platform for stdcall & fastcall
/// name decoration
unsigned BytesToPopOnReturn = 0;
/// FrameIndex for return slot.
int ReturnAddrIndex = 0;
/// The number of bytes by which return address stack slot is moved as the
/// result of tail call optimization.
int TailCallReturnAddrDelta = 0;
/// keeps track of the virtual register initialized for use as the global
/// base register. This is used for PIC in some PIC relocation models.
unsigned GlobalBaseReg = 0;
/// FrameIndex for start of varargs area.
int VarArgsFrameIndex = 0;
/// Keeps track of whether this function uses sequences of pushes to pass
/// function parameters.
bool HasPushSequences = false;
/// Some subtargets require that sret lowering includes
/// returning the value of the returned struct in a register. This field
/// holds the virtual register into which the sret argument is passed.
unsigned SRetReturnReg = 0;
/// A list of virtual and physical registers that must be forwarded to every
/// musttail call.
SmallVector<ForwardedRegister, 1> ForwardedMustTailRegParms;
/// The number of bytes on stack consumed by the arguments being passed on
/// the stack.
unsigned ArgumentStackSize = 0;
public:
M68kMachineFunctionInfo() = default;
explicit M68kMachineFunctionInfo(MachineFunction &MF) : MF(MF) {}
bool getRestoreBasePointer() const { return RestoreBasePointerOffset != 0; }
void setRestoreBasePointer(const MachineFunction *MF);
int getRestoreBasePointerOffset() const { return RestoreBasePointerOffset; }
unsigned getCalleeSavedFrameSize() const { return CalleeSavedFrameSize; }
void setCalleeSavedFrameSize(unsigned bytes) { CalleeSavedFrameSize = bytes; }
unsigned getBytesToPopOnReturn() const { return BytesToPopOnReturn; }
void setBytesToPopOnReturn(unsigned bytes) { BytesToPopOnReturn = bytes; }
int getRAIndex() const { return ReturnAddrIndex; }
void setRAIndex(int Index) { ReturnAddrIndex = Index; }
int getTCReturnAddrDelta() const { return TailCallReturnAddrDelta; }
void setTCReturnAddrDelta(int delta) { TailCallReturnAddrDelta = delta; }
unsigned getGlobalBaseReg() const { return GlobalBaseReg; }
void setGlobalBaseReg(unsigned Reg) { GlobalBaseReg = Reg; }
int getVarArgsFrameIndex() const { return VarArgsFrameIndex; }
void setVarArgsFrameIndex(int Index) { VarArgsFrameIndex = Index; }
bool getHasPushSequences() const { return HasPushSequences; }
void setHasPushSequences(bool HasPush) { HasPushSequences = HasPush; }
unsigned getSRetReturnReg() const { return SRetReturnReg; }
void setSRetReturnReg(unsigned Reg) { SRetReturnReg = Reg; }
unsigned getArgumentStackSize() const { return ArgumentStackSize; }
void setArgumentStackSize(unsigned size) { ArgumentStackSize = size; }
SmallVectorImpl<ForwardedRegister> &getForwardedMustTailRegParms() {
return ForwardedMustTailRegParms;
}
private:
virtual void anchor();
};
} // end of namespace llvm
#endif // M68K_MACHINE_FUNCTION_INFO_H

View File

@ -0,0 +1,261 @@
//===-- M68kRegisterInfo.cpp - CPU0 Register 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 contains the CPU0 implementation of the TargetRegisterInfo class.
///
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "m68k-reg-info"
#include "M68kRegisterInfo.h"
#include "M68k.h"
#include "M68kMachineFunction.h"
#include "M68kSubtarget.h"
#include "MCTargetDesc/M68kMCTargetDesc.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Type.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
#define GET_REGINFO_TARGET_DESC
#include "M68kGenRegisterInfo.inc"
using namespace llvm;
static cl::opt<bool> EnableBasePointer(
"m68k-use-base-pointer", cl::Hidden, cl::init(true),
cl::desc("Enable use of a base pointer for complex stack frames"));
// Pin the vtable to this file.
void M68kRegisterInfo::anchor() {}
M68kRegisterInfo::M68kRegisterInfo(const M68kSubtarget &ST)
// FIXME x26 not sure it this the correct value, it expects RA, but M68k
// passes IP anyway, how this works?
: M68kGenRegisterInfo(M68k::A0, 0, 0, M68k::PC), Subtarget(ST) {
StackPtr = M68k::SP;
FramePtr = M68k::A6;
GlobalBasePtr = M68k::A5;
BasePtr = M68k::A4;
}
//===----------------------------------------------------------------------===//
// Callee Saved Registers methods
//===----------------------------------------------------------------------===//
const MCPhysReg *
M68kRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const {
return CSR_STD_SaveList;
}
const uint32_t *
M68kRegisterInfo::getCallPreservedMask(const MachineFunction &MF,
CallingConv::ID) const {
return CSR_STD_RegMask;
}
const TargetRegisterClass *
M68kRegisterInfo::getRegsForTailCall(const MachineFunction &MF) const {
return &M68k::XR32_TCRegClass;
}
unsigned
M68kRegisterInfo::getMatchingMegaReg(unsigned Reg,
const TargetRegisterClass *RC) const {
for (MCSuperRegIterator Super(Reg, this); Super.isValid(); ++Super)
if (RC->contains(*Super))
return *Super;
return 0;
}
const TargetRegisterClass *
M68kRegisterInfo::getMaximalPhysRegClass(unsigned reg, MVT VT) const {
assert(Register::isPhysicalRegister(reg) &&
"reg must be a physical register");
// Pick the most sub register class of the right type that contains
// this physreg.
const TargetRegisterClass *BestRC = nullptr;
for (regclass_iterator I = regclass_begin(), E = regclass_end(); I != E;
++I) {
const TargetRegisterClass *RC = *I;
if ((VT == MVT::Other || isTypeLegalForClass(*RC, VT)) &&
RC->contains(reg) &&
(!BestRC ||
(BestRC->hasSubClass(RC) && RC->getNumRegs() > BestRC->getNumRegs())))
BestRC = RC;
}
assert(BestRC && "Couldn't find the register class");
return BestRC;
}
int M68kRegisterInfo::getRegisterOrder(unsigned Reg,
const TargetRegisterClass &TRC) const {
for (unsigned i = 0; i < TRC.getNumRegs(); ++i) {
if (regsOverlap(Reg, TRC.getRegister(i))) {
return i;
}
}
return -1;
}
int M68kRegisterInfo::getSpillRegisterOrder(unsigned Reg) const {
int Result = getRegisterOrder(Reg, *getRegClass(M68k::SPILLRegClassID));
assert(Result >= 0 && "Can not determine spill order");
return Result;
}
BitVector M68kRegisterInfo::getReservedRegs(const MachineFunction &MF) const {
const M68kFrameLowering *TFI = getFrameLowering(MF);
BitVector Reserved(getNumRegs());
// Set a register's and its sub-registers and aliases as reserved.
auto setBitVector = [&Reserved, this](unsigned Reg) {
for (MCRegAliasIterator I(Reg, this, /* self */ true); I.isValid(); ++I) {
Reserved.set(*I);
}
for (MCSubRegIterator I(Reg, this, /* self */ true); I.isValid(); ++I) {
Reserved.set(*I);
}
};
setBitVector(M68k::PC);
setBitVector(M68k::SP);
if (TFI->hasFP(MF)) {
setBitVector(FramePtr);
}
// Set the base-pointer register and its aliases as reserved if needed.
if (hasBasePointer(MF)) {
CallingConv::ID CC = MF.getFunction().getCallingConv();
const uint32_t *RegMask = getCallPreservedMask(MF, CC);
if (MachineOperand::clobbersPhysReg(RegMask, getBaseRegister()))
report_fatal_error("Stack realignment in presence of dynamic allocas is "
"not supported with"
"this calling convention.");
setBitVector(getBaseRegister());
}
return Reserved;
}
void M68kRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II,
int SPAdj, unsigned FIOperandNum,
RegScavenger *RS) const {
MachineInstr &MI = *II;
MachineFunction &MF = *MI.getParent()->getParent();
const M68kFrameLowering *TFI = getFrameLowering(MF);
// We have either (i,An,Rn) or (i,An) EA form
// NOTE Base contains the FI and we need to backtrace a bit to get Disp
MachineOperand &Disp = MI.getOperand(FIOperandNum - 1);
MachineOperand &Base = MI.getOperand(FIOperandNum);
int Imm = (int)(Disp.getImm());
int FIndex = (int)(Base.getIndex());
// FIXME tail call: implement jmp from mem
bool AfterFPPop = false;
unsigned BasePtr;
if (hasBasePointer(MF))
BasePtr = (FIndex < 0 ? FramePtr : getBaseRegister());
else if (needsStackRealignment(MF))
BasePtr = (FIndex < 0 ? FramePtr : StackPtr);
else if (AfterFPPop)
BasePtr = StackPtr;
else
BasePtr = (TFI->hasFP(MF) ? FramePtr : StackPtr);
Base.ChangeToRegister(BasePtr, false);
// Now add the frame object offset to the offset from FP.
int64_t FIOffset;
Register IgnoredFrameReg;
if (AfterFPPop) {
// Tail call jmp happens after FP is popped.
const MachineFrameInfo &MFI = MF.getFrameInfo();
FIOffset = MFI.getObjectOffset(FIndex) - TFI->getOffsetOfLocalArea();
} else {
FIOffset =
TFI->getFrameIndexReference(MF, FIndex, IgnoredFrameReg).getFixed();
}
if (BasePtr == StackPtr)
FIOffset += SPAdj;
Disp.ChangeToImmediate(FIOffset + Imm);
}
bool M68kRegisterInfo::requiresRegisterScavenging(
const MachineFunction &MF) const {
return true;
}
bool M68kRegisterInfo::trackLivenessAfterRegAlloc(
const MachineFunction &MF) const {
return true;
}
static bool CantUseSP(const MachineFrameInfo &MFI) {
return MFI.hasVarSizedObjects() || MFI.hasOpaqueSPAdjustment();
}
bool M68kRegisterInfo::hasBasePointer(const MachineFunction &MF) const {
const MachineFrameInfo &MFI = MF.getFrameInfo();
if (!EnableBasePointer)
return false;
// When we need stack realignment, we can't address the stack from the frame
// pointer. When we have dynamic allocas or stack-adjusting inline asm, we
// can't address variables from the stack pointer. MS inline asm can
// reference locals while also adjusting the stack pointer. When we can't
// use both the SP and the FP, we need a separate base pointer register.
bool CantUseFP = needsStackRealignment(MF);
return CantUseFP && CantUseSP(MFI);
}
bool M68kRegisterInfo::canRealignStack(const MachineFunction &MF) const {
if (!TargetRegisterInfo::canRealignStack(MF))
return false;
const MachineFrameInfo &MFI = MF.getFrameInfo();
const MachineRegisterInfo *MRI = &MF.getRegInfo();
// Stack realignment requires a frame pointer. If we already started
// register allocation with frame pointer elimination, it is too late now.
if (!MRI->canReserveReg(FramePtr))
return false;
// If a base pointer is necessary. Check that it isn't too late to reserve it.
if (CantUseSP(MFI))
return MRI->canReserveReg(BasePtr);
return true;
}
Register M68kRegisterInfo::getFrameRegister(const MachineFunction &MF) const {
const TargetFrameLowering *TFI = MF.getSubtarget().getFrameLowering();
return TFI->hasFP(MF) ? FramePtr : StackPtr;
}
const TargetRegisterClass *M68kRegisterInfo::intRegClass(unsigned size) const {
return &M68k::DR32RegClass;
}

View File

@ -0,0 +1,109 @@
//===-- M68kRegisterInfo.h - M68k Register Information Impl --*- 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 contains the M68k implementation of the TargetRegisterInfo
/// class.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIB_TARGET_M68K_M68KREGISTERINFO_H
#define LLVM_LIB_TARGET_M68K_M68KREGISTERINFO_H
#include "M68k.h"
#include "llvm/CodeGen/TargetRegisterInfo.h"
#define GET_REGINFO_HEADER
#include "M68kGenRegisterInfo.inc"
namespace llvm {
class M68kSubtarget;
class TargetInstrInfo;
class Type;
class M68kRegisterInfo : public M68kGenRegisterInfo {
virtual void anchor();
/// Physical register used as stack ptr.
unsigned StackPtr;
/// Physical register used as frame ptr.
unsigned FramePtr;
/// Physical register used as a base ptr in complex stack frames. I.e., when
/// we need a 3rd base, not just SP and FP, due to variable size stack
/// objects.
unsigned BasePtr;
/// Physical register used to store GOT address if needed.
unsigned GlobalBasePtr;
protected:
const M68kSubtarget &Subtarget;
public:
M68kRegisterInfo(const M68kSubtarget &Subtarget);
const MCPhysReg *getCalleeSavedRegs(const MachineFunction *MF) const override;
const uint32_t *getCallPreservedMask(const MachineFunction &MF,
CallingConv::ID) const override;
/// Returns a register class with registers that can be used in forming tail
/// calls.
const TargetRegisterClass *
getRegsForTailCall(const MachineFunction &MF) const;
/// Return a mega-register of the specified register Reg so its sub-register
/// of index SubIdx is Reg, its super(or mega) Reg. In other words it will
/// return a register that is not direct super register but still shares
/// physical register with Reg.
/// NOTE not sure about the term though.
unsigned getMatchingMegaReg(unsigned Reg,
const TargetRegisterClass *RC) const;
/// Returns the Register Class of a physical register of the given type,
/// picking the biggest register class of the right type that contains this
/// physreg.
const TargetRegisterClass *getMaximalPhysRegClass(unsigned reg, MVT VT) const;
/// Return index of a register within a register class, otherwise return -1
int getRegisterOrder(unsigned Reg, const TargetRegisterClass &TRC) const;
/// Return spill order index of a register, if there is none then trap
int getSpillRegisterOrder(unsigned Reg) const;
BitVector getReservedRegs(const MachineFunction &MF) const override;
bool requiresRegisterScavenging(const MachineFunction &MF) const override;
bool trackLivenessAfterRegAlloc(const MachineFunction &MF) const override;
/// FrameIndex represent objects inside a abstract stack. We must replace
/// FrameIndex with an stack/frame pointer direct reference.
void eliminateFrameIndex(MachineBasicBlock::iterator II, int SPAdj,
unsigned FIOperandNum,
RegScavenger *RS = nullptr) const override;
bool hasBasePointer(const MachineFunction &MF) const;
/// True if the stack can be realigned for the target.
bool canRealignStack(const MachineFunction &MF) const override;
Register getFrameRegister(const MachineFunction &MF) const override;
unsigned getStackRegister() const { return StackPtr; }
unsigned getBaseRegister() const { return BasePtr; }
unsigned getGlobalBaseRegister() const { return GlobalBasePtr; }
const TargetRegisterClass *intRegClass(unsigned Size) const;
};
} // end namespace llvm
#endif

View File

@ -0,0 +1,232 @@
//===-- 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 "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/Support/CommandLine.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/TargetRegistry.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) {}
bool M68kSubtarget::isPositionIndependent() const {
return TM.isPositionIndependent();
}
bool M68kSubtarget::isLegalToCallImmediateAddr() const { return true; }
bool M68kSubtarget::abiUsesSoftFloat() 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;
}

View File

@ -0,0 +1,157 @@
//===-- M68kSubtarget.h - Define Subtarget for the M68k -----*- 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 declares the M68k specific subclass of TargetSubtargetInfo.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIB_TARGET_CPU0_M68KSUBTARGET_H
#define LLVM_LIB_TARGET_CPU0_M68KSUBTARGET_H
#include "M68kFrameLowering.h"
#include "M68kISelLowering.h"
#include "M68kInstrInfo.h"
#include "llvm/CodeGen/SelectionDAGTargetInfo.h"
#include "llvm/CodeGen/TargetSubtargetInfo.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/MC/MCInstrItineraries.h"
#include "llvm/Support/Alignment.h"
#include <string>
#define GET_SUBTARGETINFO_HEADER
#include "M68kGenSubtargetInfo.inc"
extern bool M68kReserveGP;
extern bool M68kNoCpload;
namespace llvm {
class StringRef;
class M68kTargetMachine;
class M68kSubtarget : public M68kGenSubtargetInfo {
virtual void anchor();
protected:
// These define which ISA is supported. Since each Motorola M68k ISA is
// built on top of the previous one whenever an ISA is selected the previous
// selected as well.
enum SubtargetEnum { M00, M10, M20, M30, M40, M60 };
SubtargetEnum SubtargetKind = M00;
InstrItineraryData InstrItins;
/// Small section is used.
bool UseSmallSection = true;
const M68kTargetMachine &TM;
SelectionDAGTargetInfo TSInfo;
M68kInstrInfo InstrInfo;
M68kFrameLowering FrameLowering;
M68kTargetLowering TLInfo;
/// The minimum alignment known to hold of the stack frame on
/// entry to the function and which must be maintained by every function.
unsigned stackAlignment = 8;
Triple TargetTriple;
public:
/// This constructor initializes the data members to match that
/// of the specified triple.
M68kSubtarget(const Triple &TT, StringRef CPU, StringRef FS,
const M68kTargetMachine &_TM);
/// Parses features string setting specified subtarget options. Definition
/// of function is auto generated by tblgen.
void ParseSubtargetFeatures(StringRef CPU, StringRef TuneCPU, StringRef FS);
bool atLeastM68000() const { return SubtargetKind >= M00; }
bool atLeastM68010() const { return SubtargetKind >= M10; }
bool atLeastM68020() const { return SubtargetKind >= M20; }
bool atLeastM68030() const { return SubtargetKind >= M30; }
bool atLeastM68040() const { return SubtargetKind >= M40; }
bool atLeastM68060() const { return SubtargetKind >= M60; }
bool useSmallSection() const { return UseSmallSection; }
bool abiUsesSoftFloat() const;
const Triple &getTargetTriple() const { return TargetTriple; }
bool isTargetELF() const { return TargetTriple.isOSBinFormatELF(); }
/// Return true if the subtarget allows calls to immediate address.
bool isLegalToCallImmediateAddr() const;
bool isPositionIndependent() const;
/// Classify a global variable reference for the current subtarget according
/// to how we should reference it in a non-pcrel context.
unsigned char classifyLocalReference(const GlobalValue *GV) const;
/// Classify a global variable reference for the current subtarget according
/// to how we should reference it in a non-pcrel context.
unsigned char classifyGlobalReference(const GlobalValue *GV,
const Module &M) const;
unsigned char classifyGlobalReference(const GlobalValue *GV) const;
/// Classify a external variable reference for the current subtarget according
/// to how we should reference it in a non-pcrel context.
unsigned char classifyExternalReference(const Module &M) const;
/// Classify a global function reference for the current subtarget.
unsigned char classifyGlobalFunctionReference(const GlobalValue *GV,
const Module &M) const;
unsigned char classifyGlobalFunctionReference(const GlobalValue *GV) const;
/// Classify a blockaddress reference for the current subtarget according to
/// how we should reference it in a non-pcrel context.
unsigned char classifyBlockAddressReference() const;
unsigned getJumpTableEncoding() const;
/// TODO this must be controlled by options like -malign-int and -mshort
Align getStackAlignment() const { return Align(stackAlignment); }
/// getSlotSize - Stack slot size in bytes.
unsigned getSlotSize() const { return 4; }
M68kSubtarget &initializeSubtargetDependencies(StringRef CPU, Triple TT,
StringRef FS,
const M68kTargetMachine &TM);
const SelectionDAGTargetInfo *getSelectionDAGInfo() const override {
return &TSInfo;
}
const M68kInstrInfo *getInstrInfo() const override { return &InstrInfo; }
const M68kFrameLowering *getFrameLowering() const override {
return &FrameLowering;
}
const M68kRegisterInfo *getRegisterInfo() const override {
return &InstrInfo.getRegisterInfo();
}
const M68kTargetLowering *getTargetLowering() const override {
return &TLInfo;
}
const InstrItineraryData *getInstrItineraryData() const override {
return &InstrItins;
}
};
} // namespace llvm
#endif

View File

@ -11,7 +11,148 @@
///
//===----------------------------------------------------------------------===//
/// This is just a placeholder to make current
/// commit buildable. Body of this function will
/// be filled in later commits
extern "C" void LLVMInitializeM68kTarget() {}
#include "M68kTargetMachine.h"
#include "M68k.h"
#include "M68kSubtarget.h"
#include "M68kTargetObjectFile.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/CodeGen/TargetPassConfig.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/Support/TargetRegistry.h"
#include <memory>
using namespace llvm;
#define DEBUG_TYPE "m68k"
extern "C" void LLVMInitializeM68kTarget() {
RegisterTargetMachine<M68kTargetMachine> X(TheM68kTarget);
}
namespace {
std::string computeDataLayout(const Triple &TT, StringRef CPU,
const TargetOptions &Options) {
std::string Ret = "";
// M68k is Big Endian
Ret += "E";
// FIXME how to wire it with the used object format?
Ret += "-m:e";
// M68k pointers are always 32 bit wide even for 16 bit cpus
Ret += "-p:32:32";
// M68k requires i8 to align on 2 byte boundry
Ret += "-i8:8:8-i16:16:16-i32:16:32";
// FIXME no floats at the moment
// The registers can hold 8, 16, 32 bits
Ret += "-n8:16:32";
Ret += "-a:0:16-S16";
return Ret;
}
Reloc::Model getEffectiveRelocModel(const Triple &TT,
Optional<Reloc::Model> RM) {
// If not defined we default to static
if (!RM.hasValue()) {
return Reloc::Static;
}
return *RM;
}
CodeModel::Model getEffectiveCodeModel(Optional<CodeModel::Model> CM,
bool JIT) {
if (!CM) {
return CodeModel::Small;
} else if (CM == CodeModel::Large) {
llvm_unreachable("Large code model is not supported");
} else if (CM == CodeModel::Kernel) {
llvm_unreachable("Kernel code model is not implemented yet");
}
return CM.getValue();
}
} // end anonymous namespace
M68kTargetMachine::M68kTargetMachine(const Target &T, const Triple &TT,
StringRef CPU, StringRef FS,
const TargetOptions &Options,
Optional<Reloc::Model> RM,
Optional<CodeModel::Model> CM,
CodeGenOpt::Level OL, bool JIT)
: LLVMTargetMachine(T, computeDataLayout(TT, CPU, Options), TT, CPU, FS,
Options, getEffectiveRelocModel(TT, RM),
::getEffectiveCodeModel(CM, JIT), OL),
TLOF(std::make_unique<M68kELFTargetObjectFile>()),
Subtarget(TT, CPU, FS, *this) {
initAsmInfo();
}
M68kTargetMachine::~M68kTargetMachine() {}
const M68kSubtarget *
M68kTargetMachine::getSubtargetImpl(const Function &F) const {
Attribute CPUAttr = F.getFnAttribute("target-cpu");
Attribute FSAttr = F.getFnAttribute("target-features");
auto CPU = CPUAttr.isValid() ? CPUAttr.getValueAsString().str() : TargetCPU;
auto FS = FSAttr.isValid() ? FSAttr.getValueAsString().str() : TargetFS;
auto &I = SubtargetMap[CPU + FS];
if (!I) {
// This needs to be done before we create a new subtarget since any
// creation will depend on the TM and the code generation flags on the
// function that reside in TargetOptions.
resetTargetOptions(F);
I = std::make_unique<M68kSubtarget>(TargetTriple, CPU, FS, *this);
}
return I.get();
}
//===----------------------------------------------------------------------===//
// Pass Pipeline Configuration
//===----------------------------------------------------------------------===//
namespace {
class M68kPassConfig : public TargetPassConfig {
public:
M68kPassConfig(M68kTargetMachine &TM, PassManagerBase &PM)
: TargetPassConfig(TM, PM) {}
M68kTargetMachine &getM68kTargetMachine() const {
return getTM<M68kTargetMachine>();
}
const M68kSubtarget &getM68kSubtarget() const {
return *getM68kTargetMachine().getSubtargetImpl();
}
bool addInstSelector() override;
void addPreSched2() override;
void addPreEmitPass() override;
};
} // namespace
TargetPassConfig *M68kTargetMachine::createPassConfig(PassManagerBase &PM) {
return new M68kPassConfig(*this, PM);
}
bool M68kPassConfig::addInstSelector() {
// Install an instruction selector.
addPass(createM68kISelDag(getM68kTargetMachine()));
addPass(createM68kGlobalBaseRegPass());
return false;
}
void M68kPassConfig::addPreSched2() { addPass(createM68kExpandPseudoPass()); }
void M68kPassConfig::addPreEmitPass() {
addPass(createM68kCollapseMOVEMPass());
}

View File

@ -0,0 +1,56 @@
//===-- M68kTargetMachine.h - Define TargetMachine for M68k ----- 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 declares the M68k specific subclass of TargetMachine.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIB_TARGET_M68K_M68KTARGETMACHINE_H
#define LLVM_LIB_TARGET_M68K_M68KTARGETMACHINE_H
#include "M68kSubtarget.h"
#include "MCTargetDesc/M68kMCTargetDesc.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/CodeGen/SelectionDAGISel.h"
#include "llvm/CodeGen/TargetFrameLowering.h"
#include "llvm/Target/TargetMachine.h"
namespace llvm {
class formatted_raw_ostream;
class M68kRegisterInfo;
class M68kTargetMachine : public LLVMTargetMachine {
std::unique_ptr<TargetLoweringObjectFile> TLOF;
M68kSubtarget Subtarget;
mutable StringMap<std::unique_ptr<M68kSubtarget>> SubtargetMap;
public:
M68kTargetMachine(const Target &T, const Triple &TT, StringRef CPU,
StringRef FS, const TargetOptions &Options,
Optional<Reloc::Model> RM, Optional<CodeModel::Model> CM,
CodeGenOpt::Level OL, bool JIT);
~M68kTargetMachine() override;
const M68kSubtarget *getSubtargetImpl() const { return &Subtarget; }
const M68kSubtarget *getSubtargetImpl(const Function &F) const override;
// Pass Pipeline Configuration
TargetPassConfig *createPassConfig(PassManagerBase &PM) override;
TargetLoweringObjectFile *getObjFileLowering() const override {
return TLOF.get();
}
};
} // namespace llvm
#endif

View File

@ -0,0 +1,48 @@
//===-- M68kELFTargetObjectFile.cpp - M68k Object Files -----*- 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 contains definitions for M68k ELF object file lowering.
///
//===----------------------------------------------------------------------===//
#include "M68kTargetObjectFile.h"
#include "M68kSubtarget.h"
#include "M68kTargetMachine.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCSectionELF.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Target/TargetMachine.h"
using namespace llvm;
static cl::opt<unsigned> SSThreshold(
"m68k-ssection-threshold", cl::Hidden,
cl::desc("Small data and bss section threshold size (default=8)"),
cl::init(8));
void M68kELFTargetObjectFile::Initialize(MCContext &Ctx,
const TargetMachine &TM) {
TargetLoweringObjectFileELF::Initialize(Ctx, TM);
InitializeELF(TM.Options.UseInitArray);
this->TM = &static_cast<const M68kTargetMachine &>(TM);
// FIXME do we need `.sdata` and `.sbss` explicitly?
SmallDataSection = getContext().getELFSection(
".sdata", ELF::SHT_PROGBITS, ELF::SHF_WRITE | ELF::SHF_ALLOC);
SmallBSSSection = getContext().getELFSection(".sbss", ELF::SHT_NOBITS,
ELF::SHF_WRITE | ELF::SHF_ALLOC);
}

View File

@ -0,0 +1,31 @@
//===-- M68kELFTargetObjectFile.h - M68k Object Info ---------*- 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 contains declarations for M68k ELF object file lowering.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIB_TARGET_M68K_M68KTARGETOBJECTFILE_H
#define LLVM_LIB_TARGET_M68K_M68KTARGETOBJECTFILE_H
#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h"
namespace llvm {
class M68kTargetMachine;
class M68kELFTargetObjectFile : public TargetLoweringObjectFileELF {
const M68kTargetMachine *TM;
MCSection *SmallDataSection;
MCSection *SmallBSSSection;
public:
void Initialize(MCContext &Ctx, const TargetMachine &TM) override;
};
} // end namespace llvm
#endif