Backend structure¶
From Fig. 11, llvm compiler transfer from llvm-ir to assembly or binary object with the following data structure as Fig. 18.
Cpu0 backend supports the following backend compiler, assembler and disassembler as Fig. 19 and Fig. 20. However only the green part for printing assembly is implemented in this chapter. Others are implemented in later chapters Fig. 32, Fig. 56 and Fig. 51.
This chapter introduces the backend class inheritance tree and class members first. Next, following the backend structure, adding individual classes implementation in each section. At the end of this chapter, we will have a backend to compile llvm intermediate code into Cpu0 assembly code.
Many lines of code are added in this chapter. They almost are common in every backend except the backend name (Cpu0 or Mips …). Actually, we copy almost all the code from Mips and replace the name with Cpu0. In addition to knowing the DAGs pattern match in theoretic compiler and realistic llvm code generation phase, please focus on the classes relationship in this backend structure. Once knowing the structure, you can create your backend structure as quickly as we did, even though there are 5000 lines of code around added in this chapter.
TargetMachine structure¶
lbdex/chapters/Chapter3_1/Cpu0TargetObjectFile.h
//===-- llvm/Target/Cpu0TargetObjectFile.h - Cpu0 Object Info ---*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIB_TARGET_CPU0_CPU0TARGETOBJECTFILE_H
#define LLVM_LIB_TARGET_CPU0_CPU0TARGETOBJECTFILE_H
#include "Cpu0Config.h"
#include "Cpu0TargetMachine.h"
#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h"
namespace llvm {
class Cpu0TargetMachine;
class Cpu0TargetObjectFile : public TargetLoweringObjectFileELF {
MCSection *SmallDataSection;
MCSection *SmallBSSSection;
const Cpu0TargetMachine *TM;
public:
void Initialize(MCContext &Ctx, const TargetMachine &TM) override;
};
} // end namespace llvm
#endif
lbdex/chapters/Chapter3_1/Cpu0TargetObjectFile.cpp
//===-- Cpu0TargetObjectFile.cpp - Cpu0 Object Files ----------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "Cpu0TargetObjectFile.h"
#include "Cpu0Subtarget.h"
#include "Cpu0TargetMachine.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("cpu0-ssection-threshold", cl::Hidden,
cl::desc("Small data and bss section threshold size (default=8)"),
cl::init(8));
void Cpu0TargetObjectFile::Initialize(MCContext &Ctx, const TargetMachine &TM){
TargetLoweringObjectFileELF::Initialize(Ctx, TM);
InitializeELF(TM.Options.UseInitArray);
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);
this->TM = &static_cast<const Cpu0TargetMachine &>(TM);
}
lbdex/chapters/Chapter3_1/Cpu0TargetMachine.h
//===-- Cpu0TargetMachine.h - Define TargetMachine for Cpu0 -----*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file declares the Cpu0 specific subclass of TargetMachine.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIB_TARGET_CPU0_CPU0TARGETMACHINE_H
#define LLVM_LIB_TARGET_CPU0_CPU0TARGETMACHINE_H
#include "Cpu0Config.h"
#include "MCTargetDesc/Cpu0ABIInfo.h"
#include "Cpu0Subtarget.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/CodeGen/SelectionDAGISel.h"
#include "llvm/CodeGen/TargetFrameLowering.h"
#include "llvm/Support/CodeGen.h"
#include "llvm/Target/TargetMachine.h"
namespace llvm {
class formatted_raw_ostream;
class Cpu0RegisterInfo;
class Cpu0TargetMachine : public LLVMTargetMachine {
bool isLittle;
std::unique_ptr<TargetLoweringObjectFile> TLOF;
// Selected ABI
Cpu0ABIInfo ABI;
Cpu0Subtarget DefaultSubtarget;
mutable StringMap<std::unique_ptr<Cpu0Subtarget>> SubtargetMap;
public:
Cpu0TargetMachine(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, bool isLittle);
~Cpu0TargetMachine() override;
const Cpu0Subtarget *getSubtargetImpl() const {
return &DefaultSubtarget;
}
const Cpu0Subtarget *getSubtargetImpl(const Function &F) const override;
// Pass Pipeline Configuration
TargetPassConfig *createPassConfig(PassManagerBase &PM) override;
TargetLoweringObjectFile *getObjFileLowering() const override {
return TLOF.get();
}
bool isLittleEndian() const { return isLittle; }
const Cpu0ABIInfo &getABI() const { return ABI; }
};
/// Cpu0ebTargetMachine - Cpu032 big endian target machine.
///
class Cpu0ebTargetMachine : public Cpu0TargetMachine {
virtual void anchor();
public:
Cpu0ebTargetMachine(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);
};
/// Cpu0elTargetMachine - Cpu032 little endian target machine.
///
class Cpu0elTargetMachine : public Cpu0TargetMachine {
virtual void anchor();
public:
Cpu0elTargetMachine(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);
};
} // End llvm namespace
#endif
lbdex/chapters/Chapter3_1/Cpu0TargetMachine.cpp
//===-- Cpu0TargetMachine.cpp - Define TargetMachine for Cpu0 -------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Implements the info about Cpu0 target spec.
//
//===----------------------------------------------------------------------===//
#include "Cpu0TargetMachine.h"
#include "Cpu0.h"
#include "Cpu0Subtarget.h"
#include "Cpu0TargetObjectFile.h"
#include "llvm/IR/Attributes.h"
#include "llvm/IR/Function.h"
#include "llvm/Support/CodeGen.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/CodeGen/TargetPassConfig.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Target/TargetOptions.h"
using namespace llvm;
#define DEBUG_TYPE "cpu0"
extern "C" void LLVMInitializeCpu0Target() {
// Register the target.
//- Big endian Target Machine
RegisterTargetMachine<Cpu0ebTargetMachine> X(TheCpu0Target);
//- Little endian Target Machine
RegisterTargetMachine<Cpu0elTargetMachine> Y(TheCpu0elTarget);
}
static std::string computeDataLayout(const Triple &TT, StringRef CPU,
const TargetOptions &Options,
bool isLittle) {
std::string Ret = "";
// There are both little and big endian cpu0.
if (isLittle)
Ret += "e";
else
Ret += "E";
Ret += "-m:m";
// Pointers are 32 bit on some ABIs.
Ret += "-p:32:32";
// 8 and 16 bit integers only need to have natural alignment, but try to
// align them to 32 bits. 64 bit integers have natural alignment.
Ret += "-i8:8:32-i16:16:32-i64:64";
// 32 bit registers are always available and the stack is at least 64 bit
// aligned.
Ret += "-n32-S64";
return Ret;
}
static Reloc::Model getEffectiveRelocModel(bool JIT,
Optional<Reloc::Model> RM) {
if (!RM.hasValue() || JIT)
return Reloc::Static;
return *RM;
}
// DataLayout --> Big-endian, 32-bit pointer/ABI/alignment
// The stack is always 8 byte aligned
// On function prologue, the stack is created by decrementing
// its pointer. Once decremented, all references are done with positive
// offset from the stack/frame pointer, using StackGrowsUp enables
// an easier handling.
// Using CodeModel::Large enables different CALL behavior.
Cpu0TargetMachine::Cpu0TargetMachine(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,
bool isLittle)
//- Default is big endian
: LLVMTargetMachine(T, computeDataLayout(TT, CPU, Options, isLittle), TT,
CPU, FS, Options, getEffectiveRelocModel(JIT, RM),
getEffectiveCodeModel(CM, CodeModel::Small), OL),
isLittle(isLittle), TLOF(std::make_unique<Cpu0TargetObjectFile>()),
ABI(Cpu0ABIInfo::computeTargetABI()),
DefaultSubtarget(TT, CPU, FS, isLittle, *this) {
// initAsmInfo will display features by llc -march=cpu0 -mcpu=help on 3.7 but
// not on 3.6
initAsmInfo();
}
Cpu0TargetMachine::~Cpu0TargetMachine() {}
void Cpu0ebTargetMachine::anchor() { }
Cpu0ebTargetMachine::Cpu0ebTargetMachine(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)
: Cpu0TargetMachine(T, TT, CPU, FS, Options, RM, CM, OL, JIT, false) {}
void Cpu0elTargetMachine::anchor() { }
Cpu0elTargetMachine::Cpu0elTargetMachine(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)
: Cpu0TargetMachine(T, TT, CPU, FS, Options, RM, CM, OL, JIT, true) {}
const Cpu0Subtarget *
Cpu0TargetMachine::getSubtargetImpl(const Function &F) const {
std::string CPU = TargetCPU;
std::string FS = 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<Cpu0Subtarget>(TargetTriple, CPU, FS, isLittle,
*this);
}
return I.get();
}
namespace {
//@Cpu0PassConfig {
/// Cpu0 Code Generator Pass Configuration Options.
class Cpu0PassConfig : public TargetPassConfig {
public:
Cpu0PassConfig(Cpu0TargetMachine &TM, PassManagerBase &PM)
: TargetPassConfig(TM, PM) {}
Cpu0TargetMachine &getCpu0TargetMachine() const {
return getTM<Cpu0TargetMachine>();
}
const Cpu0Subtarget &getCpu0Subtarget() const {
return *getCpu0TargetMachine().getSubtargetImpl();
}
};
} // namespace
TargetPassConfig *Cpu0TargetMachine::createPassConfig(PassManagerBase &PM) {
return new Cpu0PassConfig(*this, PM);
}
include/llvm/Target/TargetInstInfo.h
class TargetInstrInfo : public MCInstrInfo {
TargetInstrInfo(const TargetInstrInfo &) = delete;
void operator=(const TargetInstrInfo &) = delete;
public:
...
}
...
class TargetInstrInfoImpl : public TargetInstrInfo {
protected:
TargetInstrInfoImpl(int CallFrameSetupOpcode = -1,
int CallFrameDestroyOpcode = -1)
: TargetInstrInfo(CallFrameSetupOpcode, CallFrameDestroyOpcode) {}
public:
...
}
lbdex/chapters/Chapter3_1/Cpu0.td
include "Cpu0CallingConv.td"
lbdex/chapters/Chapter3_1/Cpu0CallingConv.td
//===-- Cpu0CallingConv.td - Calling Conventions for Cpu0 --*- tablegen -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// This describes the calling conventions for Cpu0 architecture.
//===----------------------------------------------------------------------===//
/// CCIfSubtarget - Match if the current subtarget has a feature F.
class CCIfSubtarget<string F, CCAction A>:
CCIf<!strconcat("State.getTarget().getSubtarget<Cpu0Subtarget>().", F), A>;
def CSR_O32 : CalleeSavedRegs<(add LR, FP,
(sequence "S%u", 1, 0))>;
lbdex/chapters/Chapter3_1/Cpu0FrameLowering.h
//===-- Cpu0FrameLowering.h - Define frame lowering for Cpu0 ----*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIB_TARGET_CPU0_CPU0FRAMELOWERING_H
#define LLVM_LIB_TARGET_CPU0_CPU0FRAMELOWERING_H
#include "Cpu0Config.h"
#include "Cpu0.h"
#include "llvm/CodeGen/TargetFrameLowering.h"
namespace llvm {
class Cpu0Subtarget;
class Cpu0FrameLowering : public TargetFrameLowering {
protected:
const Cpu0Subtarget &STI;
public:
explicit Cpu0FrameLowering(const Cpu0Subtarget &sti, unsigned Alignment)
: TargetFrameLowering(StackGrowsDown, Align(Alignment), 0, Align(Alignment)),
STI(sti) {
}
static const Cpu0FrameLowering *create(const Cpu0Subtarget &ST);
bool hasFP(const MachineFunction &MF) const override;
};
/// Create Cpu0FrameLowering objects.
const Cpu0FrameLowering *createCpu0SEFrameLowering(const Cpu0Subtarget &ST);
} // End llvm namespace
#endif
lbdex/chapters/Chapter3_1/Cpu0FrameLowering.cpp
//===-- Cpu0FrameLowering.cpp - Cpu0 Frame Information --------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains the Cpu0 implementation of TargetFrameLowering class.
//
//===----------------------------------------------------------------------===//
#include "Cpu0FrameLowering.h"
#include "Cpu0InstrInfo.h"
#include "Cpu0MachineFunction.h"
#include "Cpu0Subtarget.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/CommandLine.h"
#include "llvm/Target/TargetOptions.h"
using namespace llvm;
//- emitPrologue() and emitEpilogue must exist for main().
//===----------------------------------------------------------------------===//
//
// Stack Frame Processing methods
// +----------------------------+
//
// The stack is allocated decrementing the stack pointer on
// the first instruction of a function prologue. Once decremented,
// all stack references are done thought a positive offset
// from the stack/frame pointer, so the stack is considering
// to grow up! Otherwise terrible hacks would have to be made
// to get this stack ABI compliant :)
//
// The stack frame required by the ABI (after call):
// Offset
//
// 0 ----------
// 4 Args to pass
// . saved $GP (used in PIC)
// . Alloca allocations
// . Local Area
// . CPU "Callee Saved" Registers
// . saved FP
// . saved RA
// . FPU "Callee Saved" Registers
// StackSize -----------
//
// Offset - offset from sp after stack allocation on function prologue
//
// The sp is the stack pointer subtracted/added from the stack size
// at the Prologue/Epilogue
//
// References to the previous stack (to obtain arguments) are done
// with offsets that exceeds the stack size: (stacksize+(4*(num_arg-1))
//
// Examples:
// - reference to the actual stack frame
// for any local area var there is smt like : FI >= 0, StackOffset: 4
// st REGX, 4(SP)
//
// - reference to previous stack frame
// suppose there's a load to the 5th arguments : FI < 0, StackOffset: 16.
// The emitted instruction will be something like:
// ld REGX, 16+StackSize(SP)
//
// Since the total stack size is unknown on LowerFormalArguments, all
// stack references (ObjectOffset) created to reference the function
// arguments, are negative numbers. This way, on eliminateFrameIndex it's
// possible to detect those references and the offsets are adjusted to
// their real location.
//
//===----------------------------------------------------------------------===//
const Cpu0FrameLowering *Cpu0FrameLowering::create(const Cpu0Subtarget &ST) {
return llvm::createCpu0SEFrameLowering(ST);
}
// hasFP - 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 Cpu0FrameLowering::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);
}
lbdex/chapters/Chapter3_1/Cpu0SEFrameLowering.h
//===-- Cpu0SEFrameLowering.h - Cpu032/64 frame lowering --------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIB_TARGET_CPU0_CPU0SEFRAMELOWERING_H
#define LLVM_LIB_TARGET_CPU0_CPU0SEFRAMELOWERING_H
#include "Cpu0Config.h"
#include "Cpu0FrameLowering.h"
namespace llvm {
class Cpu0SEFrameLowering : public Cpu0FrameLowering {
public:
explicit Cpu0SEFrameLowering(const Cpu0Subtarget &STI);
/// emitProlog/emitEpilog - These methods insert prolog and epilog code into
/// the function.
void emitPrologue(MachineFunction &MF, MachineBasicBlock &MBB) const override;
void emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const override;
};
} // End llvm namespace
#endif
lbdex/chapters/Chapter3_1/Cpu0SEFrameLowering.cpp
//===-- Cpu0SEFrameLowering.cpp - Cpu0 Frame Information ------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains the Cpu0 implementation of TargetFrameLowering class.
//
//===----------------------------------------------------------------------===//
#include "Cpu0SEFrameLowering.h"
#include "Cpu0MachineFunction.h"
#include "Cpu0SEInstrInfo.h"
#include "Cpu0Subtarget.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/CodeGen/RegisterScavenging.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/Function.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Target/TargetOptions.h"
using namespace llvm;
Cpu0SEFrameLowering::Cpu0SEFrameLowering(const Cpu0Subtarget &STI)
: Cpu0FrameLowering(STI, STI.stackAlignment()) {}
//@emitPrologue {
void Cpu0SEFrameLowering::emitPrologue(MachineFunction &MF,
MachineBasicBlock &MBB) const {
}
//}
//@emitEpilogue {
void Cpu0SEFrameLowering::emitEpilogue(MachineFunction &MF,
MachineBasicBlock &MBB) const {
}
//}
const Cpu0FrameLowering *
llvm::createCpu0SEFrameLowering(const Cpu0Subtarget &ST) {
return new Cpu0SEFrameLowering(ST);
}
lbdex/chapters/Chapter3_1/Cpu0InstrInfo.h
//===-- Cpu0InstrInfo.h - Cpu0 Instruction Information ----------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains the Cpu0 implementation of the TargetInstrInfo class.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIB_TARGET_CPU0_CPU0INSTRINFO_H
#define LLVM_LIB_TARGET_CPU0_CPU0INSTRINFO_H
#include "Cpu0Config.h"
#include "Cpu0.h"
#include "Cpu0RegisterInfo.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/TargetInstrInfo.h"
#define GET_INSTRINFO_HEADER
#include "Cpu0GenInstrInfo.inc"
namespace llvm {
class Cpu0InstrInfo : public Cpu0GenInstrInfo {
virtual void anchor();
protected:
const Cpu0Subtarget &Subtarget;
public:
explicit Cpu0InstrInfo(const Cpu0Subtarget &STI);
static const Cpu0InstrInfo *create(Cpu0Subtarget &STI);
/// getRegisterInfo - 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).
///
virtual const Cpu0RegisterInfo &getRegisterInfo() const = 0;
/// Return the number of bytes of code the specified instruction may be.
unsigned GetInstSizeInBytes(const MachineInstr &MI) const;
protected:
};
const Cpu0InstrInfo *createCpu0SEInstrInfo(const Cpu0Subtarget &STI);
}
#endif
lbdex/chapters/Chapter3_1/Cpu0InstrInfo.cpp
//===-- Cpu0InstrInfo.cpp - Cpu0 Instruction Information ------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains the Cpu0 implementation of the TargetInstrInfo class.
//
//===----------------------------------------------------------------------===//
#include "Cpu0InstrInfo.h"
#include "Cpu0TargetMachine.h"
#include "Cpu0MachineFunction.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/TargetRegistry.h"
using namespace llvm;
#define GET_INSTRINFO_CTOR_DTOR
#include "Cpu0GenInstrInfo.inc"
// Pin the vtable to this file.
void Cpu0InstrInfo::anchor() {}
//@Cpu0InstrInfo {
Cpu0InstrInfo::Cpu0InstrInfo(const Cpu0Subtarget &STI)
:
Subtarget(STI) {}
const Cpu0InstrInfo *Cpu0InstrInfo::create(Cpu0Subtarget &STI) {
return llvm::createCpu0SEInstrInfo(STI);
}
//@GetInstSizeInBytes {
/// Return the number of bytes of code the specified instruction may be.
unsigned Cpu0InstrInfo::GetInstSizeInBytes(const MachineInstr &MI) const {
//@GetInstSizeInBytes - body
switch (MI.getOpcode()) {
default:
return MI.getDesc().getSize();
}
}
lbdex/chapters/Chapter3_1/Cpu0InstrInfo.td
//===----------------------------------------------------------------------===//
// Cpu0 Instruction Predicate Definitions.
//===----------------------------------------------------------------------===//
def Ch3_1 : Predicate<"Subtarget->hasChapter3_1()">,
AssemblerPredicate<(all_of FeatureChapter3_1)>;
def Ch3_2 : Predicate<"Subtarget->hasChapter3_2()">,
AssemblerPredicate<(all_of FeatureChapter3_2)>;
def Ch3_3 : Predicate<"Subtarget->hasChapter3_3()">,
AssemblerPredicate<(all_of FeatureChapter3_3)>;
def Ch3_4 : Predicate<"Subtarget->hasChapter3_4()">,
AssemblerPredicate<(all_of FeatureChapter3_4)>;
def Ch3_5 : Predicate<"Subtarget->hasChapter3_5()">,
AssemblerPredicate<(all_of FeatureChapter3_5)>;
def Ch4_1 : Predicate<"Subtarget->hasChapter4_1()">,
AssemblerPredicate<(all_of FeatureChapter4_1)>;
def Ch4_2 : Predicate<"Subtarget->hasChapter4_2()">,
AssemblerPredicate<(all_of FeatureChapter4_2)>;
def Ch5_1 : Predicate<"Subtarget->hasChapter5_1()">,
AssemblerPredicate<(all_of FeatureChapter5_1)>;
def Ch6_1 : Predicate<"Subtarget->hasChapter6_1()">,
AssemblerPredicate<(all_of FeatureChapter6_1)>;
def Ch7_1 : Predicate<"Subtarget->hasChapter7_1()">,
AssemblerPredicate<(all_of FeatureChapter7_1)>;
def Ch8_1 : Predicate<"Subtarget->hasChapter8_1()">,
AssemblerPredicate<(all_of FeatureChapter8_1)>;
def Ch8_2 : Predicate<"Subtarget->hasChapter8_2()">,
AssemblerPredicate<(all_of FeatureChapter8_2)>;
def Ch9_1 : Predicate<"Subtarget->hasChapter9_1()">,
AssemblerPredicate<(all_of FeatureChapter9_1)>;
def Ch9_2 : Predicate<"Subtarget->hasChapter9_2()">,
AssemblerPredicate<(all_of FeatureChapter9_2)>;
def Ch9_3 : Predicate<"Subtarget->hasChapter9_3()">,
AssemblerPredicate<(all_of FeatureChapter9_3)>;
def Ch10_1 : Predicate<"Subtarget->hasChapter10_1()">,
AssemblerPredicate<(all_of FeatureChapter10_1)>;
def Ch11_1 : Predicate<"Subtarget->hasChapter11_1()">,
AssemblerPredicate<(all_of FeatureChapter11_1)>;
def Ch11_2 : Predicate<"Subtarget->hasChapter11_2()">,
AssemblerPredicate<(all_of FeatureChapter11_2)>;
def Ch12_1 : Predicate<"Subtarget->hasChapter12_1()">,
AssemblerPredicate<(all_of FeatureChapter12_1)>;
def Ch_all : Predicate<"Subtarget->hasChapterAll()">,
AssemblerPredicate<(all_of FeatureChapterAll)>;
def EnableOverflow : Predicate<"Subtarget->enableOverflow()">;
def DisableOverflow : Predicate<"Subtarget->disableOverflow()">;
def HasCmp : Predicate<"Subtarget->hasCmp()">;
def HasSlt : Predicate<"Subtarget->hasSlt()">;
lbdex/chapters/Chapter3_1/Cpu0ISelLowering.h
//===-- Cpu0ISelLowering.h - Cpu0 DAG Lowering Interface --------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines the interfaces that Cpu0 uses to lower LLVM code into a
// selection DAG.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIB_TARGET_CPU0_CPU0ISELLOWERING_H
#define LLVM_LIB_TARGET_CPU0_CPU0ISELLOWERING_H
#include "Cpu0Config.h"
#include "MCTargetDesc/Cpu0ABIInfo.h"
#include "Cpu0.h"
#include "llvm/CodeGen/CallingConvLower.h"
#include "llvm/CodeGen/SelectionDAG.h"
#include "llvm/IR/Function.h"
#include "llvm/CodeGen/TargetLowering.h"
#include <deque>
namespace llvm {
namespace Cpu0ISD {
enum NodeType {
// Start the numbering from where ISD NodeType finishes.
FIRST_NUMBER = ISD::BUILTIN_OP_END,
// Jump and link (call)
JmpLink,
// Tail call
TailCall,
// Get the Higher 16 bits from a 32-bit immediate
// No relation with Cpu0 Hi register
Hi,
// Get the Lower 16 bits from a 32-bit immediate
// No relation with Cpu0 Lo register
Lo,
// Handle gp_rel (small data/bss sections) relocation.
GPRel,
// Thread Pointer
ThreadPointer,
// Return
Ret,
EH_RETURN,
// DivRem(u)
DivRem,
DivRemU,
Wrapper,
DynAlloc,
Sync
};
}
//===--------------------------------------------------------------------===//
// TargetLowering Implementation
//===--------------------------------------------------------------------===//
class Cpu0FunctionInfo;
class Cpu0Subtarget;
//@class Cpu0TargetLowering
class Cpu0TargetLowering : public TargetLowering {
public:
explicit Cpu0TargetLowering(const Cpu0TargetMachine &TM,
const Cpu0Subtarget &STI);
static const Cpu0TargetLowering *create(const Cpu0TargetMachine &TM,
const Cpu0Subtarget &STI);
/// getTargetNodeName - This method returns the name of a target specific
// DAG node.
const char *getTargetNodeName(unsigned Opcode) const override;
protected:
/// ByValArgInfo - Byval argument information.
struct ByValArgInfo {
unsigned FirstIdx; // Index of the first register used.
unsigned NumRegs; // Number of registers used for this argument.
unsigned Address; // Offset of the stack area used to pass this argument.
ByValArgInfo() : FirstIdx(0), NumRegs(0), Address(0) {}
};
protected:
// Subtarget Info
const Cpu0Subtarget &Subtarget;
// Cache the ABI from the TargetMachine, we use it everywhere.
const Cpu0ABIInfo &ABI;
private:
// Lower Operand specifics
SDValue lowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const;
//- must be exist even without function all
SDValue
LowerFormalArguments(SDValue Chain,
CallingConv::ID CallConv, bool IsVarArg,
const SmallVectorImpl<ISD::InputArg> &Ins,
const SDLoc &dl, SelectionDAG &DAG,
SmallVectorImpl<SDValue> &InVals) const override;
SDValue LowerReturn(SDValue Chain,
CallingConv::ID CallConv, bool IsVarArg,
const SmallVectorImpl<ISD::OutputArg> &Outs,
const SmallVectorImpl<SDValue> &OutVals,
const SDLoc &dl, SelectionDAG &DAG) const override;
};
const Cpu0TargetLowering *
createCpu0SETargetLowering(const Cpu0TargetMachine &TM, const Cpu0Subtarget &STI);
}
#endif // Cpu0ISELLOWERING_H
lbdex/chapters/Chapter3_1/Cpu0ISelLowering.cpp
//===-- Cpu0ISelLowering.cpp - Cpu0 DAG Lowering Implementation -----------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines the interfaces that Cpu0 uses to lower LLVM code into a
// selection DAG.
//
//===----------------------------------------------------------------------===//
#include "Cpu0ISelLowering.h"
#include "Cpu0MachineFunction.h"
#include "Cpu0TargetMachine.h"
#include "Cpu0TargetObjectFile.h"
#include "Cpu0Subtarget.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/CodeGen/CallingConvLower.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/SelectionDAG.h"
#include "llvm/CodeGen/ValueTypes.h"
#include "llvm/IR/CallingConv.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
#define DEBUG_TYPE "cpu0-lower"
//@3_1 1 {
const char *Cpu0TargetLowering::getTargetNodeName(unsigned Opcode) const {
switch (Opcode) {
case Cpu0ISD::JmpLink: return "Cpu0ISD::JmpLink";
case Cpu0ISD::TailCall: return "Cpu0ISD::TailCall";
case Cpu0ISD::Hi: return "Cpu0ISD::Hi";
case Cpu0ISD::Lo: return "Cpu0ISD::Lo";
case Cpu0ISD::GPRel: return "Cpu0ISD::GPRel";
case Cpu0ISD::Ret: return "Cpu0ISD::Ret";
case Cpu0ISD::EH_RETURN: return "Cpu0ISD::EH_RETURN";
case Cpu0ISD::DivRem: return "Cpu0ISD::DivRem";
case Cpu0ISD::DivRemU: return "Cpu0ISD::DivRemU";
case Cpu0ISD::Wrapper: return "Cpu0ISD::Wrapper";
default: return NULL;
}
}
//@3_1 1 }
//@Cpu0TargetLowering {
Cpu0TargetLowering::Cpu0TargetLowering(const Cpu0TargetMachine &TM,
const Cpu0Subtarget &STI)
: TargetLowering(TM), Subtarget(STI), ABI(TM.getABI()) {
}
const Cpu0TargetLowering *Cpu0TargetLowering::create(const Cpu0TargetMachine &TM,
const Cpu0Subtarget &STI) {
return llvm::createCpu0SETargetLowering(TM, STI);
}
//===----------------------------------------------------------------------===//
// Lower helper functions
//===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
// Misc Lower Operation implementation
//===----------------------------------------------------------------------===//
#include "Cpu0GenCallingConv.inc"
//===----------------------------------------------------------------------===//
//@ Formal Arguments Calling Convention Implementation
//===----------------------------------------------------------------------===//
//@LowerFormalArguments {
/// LowerFormalArguments - transform physical registers into virtual registers
/// and generate load operations for arguments places on the stack.
SDValue
Cpu0TargetLowering::LowerFormalArguments(SDValue Chain,
CallingConv::ID CallConv,
bool IsVarArg,
const SmallVectorImpl<ISD::InputArg> &Ins,
const SDLoc &DL, SelectionDAG &DAG,
SmallVectorImpl<SDValue> &InVals)
const {
return Chain;
}
// @LowerFormalArguments }
//===----------------------------------------------------------------------===//
//@ Return Value Calling Convention Implementation
//===----------------------------------------------------------------------===//
SDValue
Cpu0TargetLowering::LowerReturn(SDValue Chain,
CallingConv::ID CallConv, bool IsVarArg,
const SmallVectorImpl<ISD::OutputArg> &Outs,
const SmallVectorImpl<SDValue> &OutVals,
const SDLoc &DL, SelectionDAG &DAG) const {
return DAG.getNode(Cpu0ISD::Ret, DL, MVT::Other,
Chain, DAG.getRegister(Cpu0::LR, MVT::i32));
}
lbdex/chapters/Chapter3_1/Cpu0SEISelLowering.h
//===-- Cpu0ISEISelLowering.h - Cpu0ISE DAG Lowering Interface ----*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Subclass of Cpu0ITargetLowering specialized for cpu032/64.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIB_TARGET_CPU0_CPU0SEISELLOWERING_H
#define LLVM_LIB_TARGET_CPU0_CPU0SEISELLOWERING_H
#include "Cpu0Config.h"
#include "Cpu0ISelLowering.h"
#include "Cpu0RegisterInfo.h"
namespace llvm {
class Cpu0SETargetLowering : public Cpu0TargetLowering {
public:
explicit Cpu0SETargetLowering(const Cpu0TargetMachine &TM,
const Cpu0Subtarget &STI);
SDValue LowerOperation(SDValue Op, SelectionDAG &DAG) const override;
private:
};
}
#endif // Cpu0ISEISELLOWERING_H
lbdex/chapters/Chapter3_1/Cpu0SEISelLowering.cpp
//===-- Cpu0SEISelLowering.cpp - Cpu0SE DAG Lowering Interface --*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Subclass of Cpu0TargetLowering specialized for cpu032.
//
//===----------------------------------------------------------------------===//
#include "Cpu0MachineFunction.h"
#include "Cpu0SEISelLowering.h"
#include "Cpu0RegisterInfo.h"
#include "Cpu0TargetMachine.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/TargetInstrInfo.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
#define DEBUG_TYPE "cpu0-isel"
static cl::opt<bool>
EnableCpu0TailCalls("enable-cpu0-tail-calls", cl::Hidden,
cl::desc("CPU0: Enable tail calls."), cl::init(false));
//@Cpu0SETargetLowering {
Cpu0SETargetLowering::Cpu0SETargetLowering(const Cpu0TargetMachine &TM,
const Cpu0Subtarget &STI)
: Cpu0TargetLowering(TM, STI) {
//@Cpu0SETargetLowering body {
// Set up the register classes
addRegisterClass(MVT::i32, &Cpu0::CPURegsRegClass);
// must, computeRegisterProperties - Once all of the register classes are
// added, this allows us to compute derived properties we expose.
computeRegisterProperties(Subtarget.getRegisterInfo());
}
SDValue Cpu0SETargetLowering::LowerOperation(SDValue Op,
SelectionDAG &DAG) const {
return Cpu0TargetLowering::LowerOperation(Op, DAG);
}
const Cpu0TargetLowering *
llvm::createCpu0SETargetLowering(const Cpu0TargetMachine &TM,
const Cpu0Subtarget &STI) {
return new Cpu0SETargetLowering(TM, STI);
}
lbdex/chapters/Chapter3_1/Cpu0MachineFunction.h
//===-- Cpu0MachineFunctionInfo.h - Private data used for Cpu0 ----*- C++ -*-=//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file declares the Cpu0 specific subclass of MachineFunctionInfo.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIB_TARGET_CPU0_CPU0MACHINEFUNCTION_H
#define LLVM_LIB_TARGET_CPU0_CPU0MACHINEFUNCTION_H
#include "Cpu0Config.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineMemOperand.h"
#include "llvm/CodeGen/PseudoSourceValue.h"
#include "llvm/Target/TargetMachine.h"
#include <map>
namespace llvm {
//@1 {
/// Cpu0FunctionInfo - This class is derived from MachineFunction private
/// Cpu0 target-specific information for each MachineFunction.
class Cpu0FunctionInfo : public MachineFunctionInfo {
public:
Cpu0FunctionInfo(MachineFunction& MF)
: MF(MF),
VarArgsFrameIndex(0),
MaxCallFrameSize(0)
{}
~Cpu0FunctionInfo();
int getVarArgsFrameIndex() const { return VarArgsFrameIndex; }
void setVarArgsFrameIndex(int Index) { VarArgsFrameIndex = Index; }
private:
virtual void anchor();
MachineFunction& MF;
/// VarArgsFrameIndex - FrameIndex for start of varargs area.
int VarArgsFrameIndex;
unsigned MaxCallFrameSize;
};
//@1 }
} // end of namespace llvm
#endif // CPU0_MACHINE_FUNCTION_INFO_H
lbdex/chapters/Chapter3_1/Cpu0MachineFunction.cpp
//===-- Cpu0MachineFunctionInfo.cpp - Private data used for Cpu0 ----------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "Cpu0MachineFunction.h"
#include "Cpu0InstrInfo.h"
#include "Cpu0Subtarget.h"
#include "llvm/IR/Function.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
using namespace llvm;
bool FixGlobalBaseReg;
Cpu0FunctionInfo::~Cpu0FunctionInfo() {}
void Cpu0FunctionInfo::anchor() { }
lbdex/chapters/Chapter3_1/MCTargetDesc/Cpu0ABIInfo.h
//===---- Cpu0ABIInfo.h - Information about CPU0 ABI's --------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIB_TARGET_CPU0_MCTARGETDESC_CPU0ABIINFO_H
#define LLVM_LIB_TARGET_CPU0_MCTARGETDESC_CPU0ABIINFO_H
#include "Cpu0Config.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/Triple.h"
#include "llvm/IR/CallingConv.h"
#include "llvm/MC/MCRegisterInfo.h"
namespace llvm {
class MCTargetOptions;
class StringRef;
class TargetRegisterClass;
class Cpu0ABIInfo {
public:
enum class ABI { Unknown, O32, S32 };
protected:
ABI ThisABI;
public:
Cpu0ABIInfo(ABI ThisABI) : ThisABI(ThisABI) {}
static Cpu0ABIInfo Unknown() { return Cpu0ABIInfo(ABI::Unknown); }
static Cpu0ABIInfo O32() { return Cpu0ABIInfo(ABI::O32); }
static Cpu0ABIInfo S32() { return Cpu0ABIInfo(ABI::S32); }
static Cpu0ABIInfo computeTargetABI();
bool IsKnown() const { return ThisABI != ABI::Unknown; }
bool IsO32() const { return ThisABI == ABI::O32; }
bool IsS32() const { return ThisABI == ABI::S32; }
ABI GetEnumValue() const { return ThisABI; }
/// The registers to use for byval arguments.
const ArrayRef<MCPhysReg> GetByValArgRegs() const;
/// The registers to use for the variable argument list.
const ArrayRef<MCPhysReg> GetVarArgRegs() const;
/// Obtain the size of the area allocated by the callee for arguments.
/// CallingConv::FastCall affects the value for O32.
unsigned GetCalleeAllocdArgSizeInBytes(CallingConv::ID CC) const;
/// Ordering of ABI's
/// Cpu0GenSubtargetInfo.inc will use this to resolve conflicts when given
/// multiple ABI options.
bool operator<(const Cpu0ABIInfo Other) const {
return ThisABI < Other.GetEnumValue();
}
unsigned GetStackPtr() const;
unsigned GetFramePtr() const;
unsigned GetNullPtr() const;
unsigned GetEhDataReg(unsigned I) const;
int EhDataRegSize() const;
};
}
#endif
lbdex/chapters/Chapter3_1/MCTargetDesc/Cpu0ABIInfo.cpp
//===---- Cpu0ABIInfo.cpp - Information about CPU0 ABI's ------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "Cpu0Config.h"
#include "Cpu0ABIInfo.h"
#include "Cpu0RegisterInfo.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/MC/MCTargetOptions.h"
#include "llvm/Support/CommandLine.h"
using namespace llvm;
static cl::opt<bool>
EnableCpu0S32Calls("cpu0-s32-calls", cl::Hidden,
cl::desc("CPU0 S32 call: use stack only to pass arguments.\
"), cl::init(false));
namespace {
static const MCPhysReg O32IntRegs[4] = {Cpu0::A0, Cpu0::A1};
static const MCPhysReg S32IntRegs = {};
}
const ArrayRef<MCPhysReg> Cpu0ABIInfo::GetByValArgRegs() const {
if (IsO32())
return makeArrayRef(O32IntRegs);
if (IsS32())
return makeArrayRef(S32IntRegs);
llvm_unreachable("Unhandled ABI");
}
const ArrayRef<MCPhysReg> Cpu0ABIInfo::GetVarArgRegs() const {
if (IsO32())
return makeArrayRef(O32IntRegs);
if (IsS32())
return makeArrayRef(S32IntRegs);
llvm_unreachable("Unhandled ABI");
}
unsigned Cpu0ABIInfo::GetCalleeAllocdArgSizeInBytes(CallingConv::ID CC) const {
if (IsO32())
return CC != 0;
if (IsS32())
return 0;
llvm_unreachable("Unhandled ABI");
}
Cpu0ABIInfo Cpu0ABIInfo::computeTargetABI() {
Cpu0ABIInfo abi(ABI::Unknown);
if (EnableCpu0S32Calls)
abi = ABI::S32;
else
abi = ABI::O32;
// Assert exactly one ABI was chosen.
assert(abi.ThisABI != ABI::Unknown);
return abi;
}
unsigned Cpu0ABIInfo::GetStackPtr() const {
return Cpu0::SP;
}
unsigned Cpu0ABIInfo::GetFramePtr() const {
return Cpu0::FP;
}
unsigned Cpu0ABIInfo::GetNullPtr() const {
return Cpu0::ZERO;
}
unsigned Cpu0ABIInfo::GetEhDataReg(unsigned I) const {
static const unsigned EhDataReg[] = {
Cpu0::A0, Cpu0::A1
};
return EhDataReg[I];
}
int Cpu0ABIInfo::EhDataRegSize() const {
if (ThisABI == ABI::S32)
return 0;
else
return 2;
}
lbdex/chapters/Chapter3_1/Cpu0Subtarget.h
#include "Cpu0FrameLowering.h"
#include "Cpu0ISelLowering.h"
#include "Cpu0InstrInfo.h"
#include "llvm/CodeGen/SelectionDAGTargetInfo.h"
#include "llvm/CodeGen/TargetSubtargetInfo.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/MC/MCInstrItineraries.h"
#include <string>
#define GET_SUBTARGETINFO_HEADER
#include "Cpu0GenSubtargetInfo.inc"
namespace llvm {
class StringRef;
class Cpu0TargetMachine;
class Cpu0Subtarget : public Cpu0GenSubtargetInfo {
virtual void anchor();
public:
bool HasChapterDummy;
bool HasChapterAll;
bool hasChapter3_1() const {
#if CH >= CH3_1
return true;
#else
return false;
#endif
}
bool hasChapter3_2() const {
#if CH >= CH3_2
return true;
#else
return false;
#endif
}
bool hasChapter3_3() const {
#if CH >= CH3_3
return true;
#else
return false;
#endif
}
bool hasChapter3_4() const {
#if CH >= CH3_4
return true;
#else
return false;
#endif
}
bool hasChapter3_5() const {
#if CH >= CH3_5
return true;
#else
return false;
#endif
}
bool hasChapter4_1() const {
#if CH >= CH4_1
return true;
#else
return false;
#endif
}
bool hasChapter4_2() const {
#if CH >= CH4_2
return true;
#else
return false;
#endif
}
bool hasChapter5_1() const {
#if CH >= CH5_1
return true;
#else
return false;
#endif
}
bool hasChapter6_1() const {
#if CH >= CH6_1
return true;
#else
return false;
#endif
}
bool hasChapter7_1() const {
#if CH >= CH7_1
return true;
#else
return false;
#endif
}
bool hasChapter8_1() const {
#if CH >= CH8_1
return true;
#else
return false;
#endif
}
bool hasChapter8_2() const {
#if CH >= CH8_2
return true;
#else
return false;
#endif
}
bool hasChapter9_1() const {
#if CH >= CH9_1
return true;
#else
return false;
#endif
}
bool hasChapter9_2() const {
#if CH >= CH9_2
return true;
#else
return false;
#endif
}
bool hasChapter9_3() const {
#if CH >= CH9_3
return true;
#else
return false;
#endif
}
bool hasChapter10_1() const {
#if CH >= CH10_1
return true;
#else
return false;
#endif
}
bool hasChapter11_1() const {
#if CH >= CH11_1
return true;
#else
return false;
#endif
}
bool hasChapter11_2() const {
#if CH >= CH11_2
return true;
#else
return false;
#endif
}
bool hasChapter12_1() const {
#if CH >= CH12_1
return true;
#else
return false;
#endif
}
protected:
enum Cpu0ArchEnum {
Cpu032I,
Cpu032II
};
// Cpu0 architecture version
Cpu0ArchEnum Cpu0ArchVersion;
// IsLittle - The target is Little Endian
bool IsLittle;
bool EnableOverflow;
// HasCmp - cmp instructions.
bool HasCmp;
// HasSlt - slt instructions.
bool HasSlt;
InstrItineraryData InstrItins;
const Cpu0TargetMachine &TM;
Triple TargetTriple;
const SelectionDAGTargetInfo TSInfo;
std::unique_ptr<const Cpu0InstrInfo> InstrInfo;
std::unique_ptr<const Cpu0FrameLowering> FrameLowering;
std::unique_ptr<const Cpu0TargetLowering> TLInfo;
public:
bool isPositionIndependent() const;
const Cpu0ABIInfo &getABI() const;
/// This constructor initializes the data members to match that
/// of the specified triple.
Cpu0Subtarget(const Triple &TT, StringRef CPU, StringRef FS,
bool little, const Cpu0TargetMachine &_TM);
//- Vitual function, must have
/// ParseSubtargetFeatures - Parses features string setting specified
/// subtarget options. Definition of function is auto generated by tblgen.
void ParseSubtargetFeatures(StringRef CPU, StringRef TuneCPU, StringRef FS);
bool isLittle() const { return IsLittle; }
bool hasCpu032I() const { return Cpu0ArchVersion >= Cpu032I; }
bool isCpu032I() const { return Cpu0ArchVersion == Cpu032I; }
bool hasCpu032II() const { return Cpu0ArchVersion >= Cpu032II; }
bool isCpu032II() const { return Cpu0ArchVersion == Cpu032II; }
/// Features related to the presence of specific instructions.
bool enableOverflow() const { return EnableOverflow; }
bool disableOverflow() const { return !EnableOverflow; }
bool hasCmp() const { return HasCmp; }
bool hasSlt() const { return HasSlt; }
bool abiUsesSoftFloat() const;
bool enableLongBranchPass() const {
return hasCpu032II();
}
unsigned stackAlignment() const { return 8; }
Cpu0Subtarget &initializeSubtargetDependencies(StringRef CPU, StringRef FS,
const TargetMachine &TM);
const SelectionDAGTargetInfo *getSelectionDAGInfo() const override {
return &TSInfo;
}
const Cpu0InstrInfo *getInstrInfo() const override { return InstrInfo.get(); }
const TargetFrameLowering *getFrameLowering() const override {
return FrameLowering.get();
}
const Cpu0RegisterInfo *getRegisterInfo() const override {
return &InstrInfo->getRegisterInfo();
}
const Cpu0TargetLowering *getTargetLowering() const override {
return TLInfo.get();
}
const InstrItineraryData *getInstrItineraryData() const override {
return &InstrItins;
}
};
} // End llvm namespace
#endif
lbdex/chapters/Chapter3_1/Cpu0Subtarget.cpp
//===-- Cpu0Subtarget.cpp - Cpu0 Subtarget Information --------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the Cpu0 specific subclass of TargetSubtargetInfo.
//
//===----------------------------------------------------------------------===//
#include "Cpu0Subtarget.h"
#include "Cpu0MachineFunction.h"
#include "Cpu0.h"
#include "Cpu0RegisterInfo.h"
#include "Cpu0TargetMachine.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 "cpu0-subtarget"
#define GET_SUBTARGETINFO_TARGET_DESC
#define GET_SUBTARGETINFO_CTOR
#include "Cpu0GenSubtargetInfo.inc"
extern bool FixGlobalBaseReg;
void Cpu0Subtarget::anchor() { }
//@1 {
Cpu0Subtarget::Cpu0Subtarget(const Triple &TT, StringRef CPU,
StringRef FS, bool little,
const Cpu0TargetMachine &_TM) :
//@1 }
// Cpu0GenSubtargetInfo will display features by llc -march=cpu0 -mcpu=help
Cpu0GenSubtargetInfo(TT, CPU, /*TuneCPU*/ CPU, FS),
IsLittle(little), TM(_TM), TargetTriple(TT), TSInfo(),
InstrInfo(
Cpu0InstrInfo::create(initializeSubtargetDependencies(CPU, FS, TM))),
FrameLowering(Cpu0FrameLowering::create(*this)),
TLInfo(Cpu0TargetLowering::create(TM, *this)) {
}
bool Cpu0Subtarget::isPositionIndependent() const {
return TM.isPositionIndependent();
}
Cpu0Subtarget &
Cpu0Subtarget::initializeSubtargetDependencies(StringRef CPU, StringRef FS,
const TargetMachine &TM) {
if (TargetTriple.getArch() == Triple::cpu0 || TargetTriple.getArch() == Triple::cpu0el) {
if (CPU.empty() || CPU == "generic") {
CPU = "cpu032II";
}
else if (CPU == "help") {
CPU = "";
return *this;
}
else if (CPU != "cpu032I" && CPU != "cpu032II") {
CPU = "cpu032II";
}
}
else {
errs() << "!!!Error, TargetTriple.getArch() = " << TargetTriple.getArch()
<< "CPU = " << CPU << "\n";
exit(0);
}
if (CPU == "cpu032I")
Cpu0ArchVersion = Cpu032I;
else if (CPU == "cpu032II")
Cpu0ArchVersion = Cpu032II;
if (isCpu032I()) {
HasCmp = true;
HasSlt = false;
}
else if (isCpu032II()) {
HasCmp = false;
HasSlt = true;
}
else {
errs() << "-mcpu must be empty(default:cpu032II), cpu032I or cpu032II" << "\n";
}
// Parse features string.
ParseSubtargetFeatures(CPU, /*TuneCPU*/ CPU, FS);
// Initialize scheduling itinerary for the specified CPU.
InstrItins = getInstrItineraryForCPU(CPU);
return *this;
}
bool Cpu0Subtarget::abiUsesSoftFloat() const {
// return TM->Options.UseSoftFloat;
return true;
}
const Cpu0ABIInfo &Cpu0Subtarget::getABI() const { return TM.getABI(); }
lbdex/chapters/Chapter3_1/Cpu0RegisterInfo.h
//===-- Cpu0RegisterInfo.h - Cpu0 Register Information Impl -----*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains the Cpu0 implementation of the TargetRegisterInfo class.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIB_TARGET_CPU0_CPU0REGISTERINFO_H
#define LLVM_LIB_TARGET_CPU0_CPU0REGISTERINFO_H
#include "Cpu0Config.h"
#include "Cpu0.h"
#include "llvm/CodeGen/TargetRegisterInfo.h"
#define GET_REGINFO_HEADER
#include "Cpu0GenRegisterInfo.inc"
namespace llvm {
class Cpu0Subtarget;
class TargetInstrInfo;
class Type;
class Cpu0RegisterInfo : public Cpu0GenRegisterInfo {
protected:
const Cpu0Subtarget &Subtarget;
public:
Cpu0RegisterInfo(const Cpu0Subtarget &Subtarget);
const MCPhysReg *getCalleeSavedRegs(const MachineFunction *MF) const override;
const uint32_t *getCallPreservedMask(const MachineFunction &MF,
CallingConv::ID) const override;
BitVector getReservedRegs(const MachineFunction &MF) const override;
bool requiresRegisterScavenging(const MachineFunction &MF) const override;
bool trackLivenessAfterRegAlloc(const MachineFunction &MF) const override;
/// Stack Frame Processing Methods
void eliminateFrameIndex(MachineBasicBlock::iterator II,
int SPAdj, unsigned FIOperandNum,
RegScavenger *RS = nullptr) const override;
/// Debug information queries.
Register getFrameRegister(const MachineFunction &MF) const override;
/// \brief Return GPR register class.
virtual const TargetRegisterClass *intRegClass(unsigned Size) const = 0;
};
} // end namespace llvm
#endif
lbdex/chapters/Chapter3_1/Cpu0RegisterInfo.cpp
//===-- Cpu0RegisterInfo.cpp - CPU0 Register Information -== --------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains the CPU0 implementation of the TargetRegisterInfo class.
//
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "cpu0-reg-info"
#include "Cpu0RegisterInfo.h"
#include "Cpu0.h"
#include "Cpu0Subtarget.h"
#include "Cpu0MachineFunction.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 "Cpu0GenRegisterInfo.inc"
using namespace llvm;
Cpu0RegisterInfo::Cpu0RegisterInfo(const Cpu0Subtarget &ST)
: Cpu0GenRegisterInfo(Cpu0::LR), Subtarget(ST) {}
//===----------------------------------------------------------------------===//
// Callee Saved Registers methods
//===----------------------------------------------------------------------===//
/// Cpu0 Callee Saved Registers
// In Cpu0CallConv.td,
// def CSR_O32 : CalleeSavedRegs<(add LR, FP,
// (sequence "S%u", 2, 0))>;
// llc create CSR_O32_SaveList and CSR_O32_RegMask from above defined.
const MCPhysReg *
Cpu0RegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const {
return CSR_O32_SaveList;
}
const uint32_t *
Cpu0RegisterInfo::getCallPreservedMask(const MachineFunction &MF,
CallingConv::ID) const {
return CSR_O32_RegMask;
}
// pure virtual method
//@getReservedRegs {
BitVector Cpu0RegisterInfo::
getReservedRegs(const MachineFunction &MF) const {
//@getReservedRegs body {
static const uint16_t ReservedCPURegs[] = {
Cpu0::ZERO, Cpu0::AT, Cpu0::SP, Cpu0::LR, /*Cpu0::SW, */Cpu0::PC
};
BitVector Reserved(getNumRegs());
for (unsigned I = 0; I < array_lengthof(ReservedCPURegs); ++I)
Reserved.set(ReservedCPURegs[I]);
return Reserved;
}
//@eliminateFrameIndex {
//- If no eliminateFrameIndex(), it will hang on run.
// pure virtual method
// FrameIndex represent objects inside a abstract stack.
// We must replace FrameIndex with an stack/frame pointer
// direct reference.
void Cpu0RegisterInfo::
eliminateFrameIndex(MachineBasicBlock::iterator II, int SPAdj,
unsigned FIOperandNum, RegScavenger *RS) const {
}
//}
bool
Cpu0RegisterInfo::requiresRegisterScavenging(const MachineFunction &MF) const {
return true;
}
bool
Cpu0RegisterInfo::trackLivenessAfterRegAlloc(const MachineFunction &MF) const {
return true;
}
// pure virtual method
Register Cpu0RegisterInfo::
getFrameRegister(const MachineFunction &MF) const {
const TargetFrameLowering *TFI = MF.getSubtarget().getFrameLowering();
return TFI->hasFP(MF) ? (Cpu0::FP) :
(Cpu0::SP);
}
lbdex/chapters/Chapter3_1/Cpu0SERegisterInfo.h
//===-- Cpu0SERegisterInfo.h - Cpu032 Register Information ------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains the Cpu032/64 implementation of the TargetRegisterInfo
// class.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIB_TARGET_CPU0_CPU0SEREGISTERINFO_H
#define LLVM_LIB_TARGET_CPU0_CPU0SEREGISTERINFO_H
#include "Cpu0Config.h"
#include "Cpu0RegisterInfo.h"
namespace llvm {
class Cpu0SEInstrInfo;
class Cpu0SERegisterInfo : public Cpu0RegisterInfo {
public:
Cpu0SERegisterInfo(const Cpu0Subtarget &Subtarget);
const TargetRegisterClass *intRegClass(unsigned Size) const override;
};
} // end namespace llvm
#endif
lbdex/chapters/Chapter3_1/Cpu0SERegisterInfo.cpp
//===-- Cpu0SERegisterInfo.cpp - CPU0 Register Information ------== -------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains the CPU0 implementation of the TargetRegisterInfo
// class.
//
//===----------------------------------------------------------------------===//
#include "Cpu0SERegisterInfo.h"
using namespace llvm;
#define DEBUG_TYPE "cpu0-reg-info"
Cpu0SERegisterInfo::Cpu0SERegisterInfo(const Cpu0Subtarget &ST)
: Cpu0RegisterInfo(ST) {}
const TargetRegisterClass *
Cpu0SERegisterInfo::intRegClass(unsigned Size) const {
return &Cpu0::CPURegsRegClass;
}
build/lib/Target/Cpu0/Cpu0GenInstInfo.inc
//- Cpu0GenInstInfo.inc which generate from Cpu0InstrInfo.td
#ifdef GET_INSTRINFO_HEADER
#undef GET_INSTRINFO_HEADER
namespace llvm {
struct Cpu0GenInstrInfo : public TargetInstrInfoImpl {
explicit Cpu0GenInstrInfo(int SO = -1, int DO = -1);
};
} // End llvm namespace
#endif // GET_INSTRINFO_HEADER
#define GET_INSTRINFO_HEADER
#include "Cpu0GenInstrInfo.inc"
//- Cpu0InstInfo.h
class Cpu0InstrInfo : public Cpu0GenInstrInfo {
Cpu0TargetMachine &TM;
public:
explicit Cpu0InstrInfo(Cpu0TargetMachine &TM);
};
Chapter3_1 add most Cpu0 backend classes. The code of Chapter3_1 can be summaried as Fig. 21. Class Cpu0Subtarget provides the interfaces getInstrInfo(), getFrameLowering(), …, to get other Cpu0 classes. Most classes (like Cpu0InstrInfo, Cpu0RegisterInfo, …) have Subtarget reference member to allowing them access other classes through the Cpu0Subtarget interface. If the backend module hasn’t the Subtarget reference, these classes still can access Subtarget class through Cpu0TargetMachine (usually use TM as symbol) by static_cast<Cpu0TargetMachine &>(TM).getSubtargetImpl(). Once getting Subtarget class, the backend code can access other classes through it. For those name of Cpu0SExx classes, they mean the standard 32 bits class. This arrangement follows llvm 3.5 Mips backend style. Mips backend uses Mips16, MipsSE and Mips64 files/classes name to define classes for 16, 32 and 64 bits architecture, respectively. Since Cpu0Subtarget creates Cpu0InstrInfo, Cpu0RegisterInfo, …, at constuctor function, it can provide the class reference through the interfaces shown in Fig. 21.
Below Fig. 22 shows Cpu0 TableGen inheritance relationship. Last chapter mentioned that backend class can include the TableGen generated classes and inherited from it. All the TableGen generated classes of Cpu0 backend are in build/lib/Target/Cpu0/*.inc. Through C++ inheritance mechanism, TableGen provides backend programmers a fexible way to use its generated code. Programmers have chance to override this function if they need to.
Since llvm has deep inheritance tree, they are not digged here. Benefit from the inheritance tree structure, not much code needed to be implemented in classes of instruction, frame/stack and select DAG, since much code are implemented by their parent classes. The llvm-tblgen generate Cpu0GenInstrInfo.inc based on information from Cpu0InstrInfo.td. Cpu0InstrInfo.h extract those code it needs from Cpu0GenInstrInfo.inc by define “#define GET_INSTRINFO_HEADER”. With TabelGen, the code size in backend is reduced again through the pattern match theory of compiler developemnt. This is explained in both sections of “DAG” and “Instruction Selection” in last chapter. Following is the code fragment from Cpu0GenInstrInfo.inc. Code between “#if def GET_INSTRINFO_HEADER” and “#endif // GET_INSTRINFO_HEADER”” will be extracted to Cpu0InstrInfo.h.
build/lib/Target/Cpu0/Cpu0GenInstInfo.inc
//- Cpu0GenInstInfo.inc which generate from Cpu0InstrInfo.td
#ifdef GET_INSTRINFO_HEADER
#undef GET_INSTRINFO_HEADER
namespace llvm {
struct Cpu0GenInstrInfo : public TargetInstrInfoImpl {
explicit Cpu0GenInstrInfo(int SO = -1, int DO = -1);
};
} // End llvm namespace
#endif // GET_INSTRINFO_HEADER
Reference web sites are here [1] [2].
Chapter3_1/CMakeLists.txt is modified with these new added *.cpp as follows,
lbdex/chapters/Chapter3_1/CMakeLists.txt
tablegen(LLVM Cpu0GenDAGISel.inc -gen-dag-isel)
tablegen(LLVM Cpu0GenCallingConv.inc -gen-callingconv)
Cpu0FrameLowering.cpp
Please take a look for Chapter3_1 code. After that, building Chapter3_1 by “#define CH CH3_1” in Cpu0Config.h as follows, and do building with cmake and make again.
~/llvm/test/llvm/lib/Target/Cpu0SetChapter.h
#define CH CH3_1
118-165-78-230:input Jonathan$ /Users/Jonathan/llvm/test/build/
bin/llc -march=cpu0 -relocation-model=pic -filetype=asm ch3.bc -o
ch3.cpu0.s
... Assertion failed: (MRI && "Unable to create reg info"), function initAsmInfo
...
With Chapter3_1 implementation, the Chapter2 error message “Could not allocate target machine!” has gone. The new error say that we have not Target AsmPrinter and asm register info. We will add it in next section.
Chapter3_1 create FeatureCpu032I and FeatureCpu032II for CPU cpu032I and
cpu032II, repectively.
Beyond that, it defines two more features, FeatureCmp and FeatureSlt.
In order to demostrate the “instruction set designing choice” to readers, this
book creates two CPU.
Readers will realize why Mips CPU uses instruction SLT instead of
CMP after they have read later Chapter “Control flow statement”.
With the added code of supporting cpu032I and cpu32II in Cpu0.td and
Cpu0InstrInfo.td of Chapter3_1, the command llc -march=cpu0 -mcpu=help
can
display messages as follows,
JonathantekiiMac:input Jonathan$ /Users/Jonathan/llvm/test/
build/bin/llc -march=cpu0 -mcpu=help
Available CPUs for this target:
cpu032I - Select the cpu032I processor.
cpu032II - Select the cpu032II processor.
Available features for this target:
ch10_1 - Enable Chapter instructions..
ch11_1 - Enable Chapter instructions..
ch11_2 - Enable Chapter instructions..
ch14_1 - Enable Chapter instructions..
ch3_1 - Enable Chapter instructions..
ch3_2 - Enable Chapter instructions..
ch3_3 - Enable Chapter instructions..
ch3_4 - Enable Chapter instructions..
ch3_5 - Enable Chapter instructions..
ch4_1 - Enable Chapter instructions..
ch4_2 - Enable Chapter instructions..
ch5_1 - Enable Chapter instructions..
ch6_1 - Enable Chapter instructions..
ch7_1 - Enable Chapter instructions..
ch8_1 - Enable Chapter instructions..
ch8_2 - Enable Chapter instructions..
ch9_1 - Enable Chapter instructions..
ch9_2 - Enable Chapter instructions..
ch9_3 - Enable Chapter instructions..
chall - Enable Chapter instructions..
cmp - Enable 'cmp' instructions..
cpu032I - Cpu032I ISA Support.
cpu032II - Cpu032II ISA Support (slt).
o32 - Enable o32 ABI.
s32 - Enable s32 ABI.
slt - Enable 'slt' instructions..
Use +feature to enable a feature, or -feature to disable it.
For example, llc -mcpu=mycpu -mattr=+feature1,-feature2
...
When user input -mcpu=cpu032I
, the variable IsCpu032I from Cpu0InstrInfo.td
will be true since the function isCpu032I() defined in Cpu0Subtarget.h is
true (set Cpu0ArchVersion to cpu032I in initializeSubtargetDependencies() called
in constructor function, the variable CPU in constructor function is “cpu032I”
when user input -mcpu=cpu032I).
Please notice variable Cpu0ArchVersion must be initialized in
Cpu0Subtarget.cpp, otherwise variable Cpu0ArchVersion can be any value and
functions isCpu032I() and isCpu032II() which support llc -mcpu=cpu032I
and
llc -mcpu=cpu032II
, repectively, will have trouble.
The value of variables HasCmp and HasSlt are set depend on Cpu0ArchVersion.
Instructions slt and beq, … are supported only in case of HasSlt is true, and
furthermore, HasSlt is true only when Cpu0ArchVersion is Cpu032II.
Similiarly, Ch4_1, Ch4_2, …, are used in controlling the enable or disable of
instruction definition. Through Subtarget->hasChapter4_1() which exists
both in Cpu0.td and Cpu0Subtarget.h, the Predicate, such as Ch4_1, defined in
Cpu0InstrInfo.td can be enabled or disabled. For example, the shift-rotate
instructions can be enabled by define CH to greater than or equal to CH4_1 as
follows,
lbdex/Cpu0/Cpu0InstrInfo.td
let Predicates = [Ch4_1] in {
class shift_rotate_reg<bits<8> op, bits<4> isRotate, string instr_asm,
SDNode OpNode, RegisterClass RC>:
FA<op, (outs GPROut:$ra), (ins RC:$rb, RC:$rc),
!strconcat(instr_asm, "\t$ra, $rb, $rc"),
[(set GPROut:$ra, (OpNode RC:$rb, RC:$rc))], IIAlu> {
let shamt = 0;
}
}
~/llvm/test/llvm/lib/Target/Cpu0SetChapter.h
#define CH CH4_1
On the contrary, it can be disabled by define it to less than CH4_1, for instance CH3_5, as follows,
~/llvm/test/llvm/lib/Target/Cpu0SetChapter.h
#define CH CH3_5
Add AsmPrinter¶
As Fig. 23, because MachineInstr is a big class for opitmization and convertion in many passes. LLVM creates MCInst for encoding purpose in assembly and binary object.
Chapter3_2/ contains the Cpu0AsmPrinter definition.
lbdex/chapters/Chapter2/Cpu0.td
def Cpu0InstrInfo : InstrInfo;
// Will generate Cpu0GenAsmWrite.inc included by Cpu0InstPrinter.cpp, contents
// as follows,
// void Cpu0InstPrinter::printInstruction(const MCInst *MI, raw_ostream &O) {...}
// const char *Cpu0InstPrinter::getRegisterName(unsigned RegNo) {...}
def Cpu0 : Target {
// def Cpu0InstrInfo : InstrInfo as before.
let InstructionSet = Cpu0InstrInfo;
}
As above comments of Chapter2/Cpu0.td indicate, it will generate Cpu0GenAsmWrite.inc which is included by Cpu0InstPrinter.cpp as follows,
lbdex/chapters/Chapter3_2/InstPrinter/Cpu0InstPrinter.h
//=== Cpu0InstPrinter.h - Convert Cpu0 MCInst to assembly syntax -*- C++ -*-==//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This class prints a Cpu0 MCInst to a .s file.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIB_TARGET_CPU0_INSTPRINTER_CPU0INSTPRINTER_H
#define LLVM_LIB_TARGET_CPU0_INSTPRINTER_CPU0INSTPRINTER_H
#include "Cpu0Config.h"
#include "llvm/MC/MCInstPrinter.h"
namespace llvm {
// These enumeration declarations were orignally in Cpu0InstrInfo.h but
// had to be moved here to avoid circular dependencies between
// LLVMCpu0CodeGen and LLVMCpu0AsmPrinter.
class TargetMachine;
class Cpu0InstPrinter : public MCInstPrinter {
public:
Cpu0InstPrinter(const MCAsmInfo &MAI, const MCInstrInfo &MII,
const MCRegisterInfo &MRI)
: MCInstPrinter(MAI, MII, MRI) {}
// Autogenerated by tblgen.
std::pair<const char *, uint64_t> getMnemonic(const MCInst *MI) override;
void printInstruction(const MCInst *MI, uint64_t Address, raw_ostream &O);
static const char *getRegisterName(unsigned RegNo);
void printRegName(raw_ostream &OS, unsigned RegNo) const override;
void printInst(const MCInst *MI, uint64_t Address, StringRef Annot,
const MCSubtargetInfo &STI, raw_ostream &O) override;
bool printAliasInstr(const MCInst *MI, uint64_t Address, raw_ostream &OS);
void printCustomAliasOperand(const MCInst *MI, uint64_t Address,
unsigned OpIdx, unsigned PrintMethodIdx,
raw_ostream &O);
private:
void printOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O);
void printOperand(const MCInst *MI, uint64_t /*Address*/, unsigned OpNum,
raw_ostream &O) {
printOperand(MI, OpNum, O);
}
void printUnsignedImm(const MCInst *MI, int opNum, raw_ostream &O);
void printMemOperand(const MCInst *MI, int opNum, raw_ostream &O);
//#if CH >= CH7_1
void printMemOperandEA(const MCInst *MI, int opNum, raw_ostream &O);
//#endif
};
} // end namespace llvm
#endif
lbdex/chapters/Chapter3_2/InstPrinter/Cpu0InstPrinter.cpp
//===-- Cpu0InstPrinter.cpp - Convert Cpu0 MCInst to assembly syntax ------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This class prints an Cpu0 MCInst to a .s file.
//
//===----------------------------------------------------------------------===//
#include "Cpu0InstPrinter.h"
#include "Cpu0InstrInfo.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCSymbol.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
#define DEBUG_TYPE "asm-printer"
#define PRINT_ALIAS_INSTR
#include "Cpu0GenAsmWriter.inc"
void Cpu0InstPrinter::printRegName(raw_ostream &OS, unsigned RegNo) const {
//- getRegisterName(RegNo) defined in Cpu0GenAsmWriter.inc which indicate in
// Cpu0.td.
OS << '$' << StringRef(getRegisterName(RegNo)).lower();
}
//@1 {
void Cpu0InstPrinter::printInst(const MCInst *MI, uint64_t Address,
StringRef Annot, const MCSubtargetInfo &STI,
raw_ostream &O) {
// Try to print any aliases first.
if (!printAliasInstr(MI, Address, O))
//@1 }
//- printInstruction(MI, O) defined in Cpu0GenAsmWriter.inc which came from
// Cpu0.td indicate.
printInstruction(MI, Address, O);
printAnnotation(O, Annot);
}
void Cpu0InstPrinter::printOperand(const MCInst *MI, unsigned OpNo,
raw_ostream &O) {
const MCOperand &Op = MI->getOperand(OpNo);
if (Op.isReg()) {
printRegName(O, Op.getReg());
return;
}
if (Op.isImm()) {
O << Op.getImm();
return;
}
assert(Op.isExpr() && "unknown operand kind in printOperand");
Op.getExpr()->print(O, &MAI, true);
}
void Cpu0InstPrinter::printUnsignedImm(const MCInst *MI, int opNum,
raw_ostream &O) {
const MCOperand &MO = MI->getOperand(opNum);
if (MO.isImm())
O << (unsigned short int)MO.getImm();
else
printOperand(MI, opNum, O);
}
void Cpu0InstPrinter::
printMemOperand(const MCInst *MI, int opNum, raw_ostream &O) {
// Load/Store memory operands -- imm($reg)
// If PIC target the target is loaded as the
// pattern ld $t9,%call16($gp)
printOperand(MI, opNum+1, O);
O << "(";
printOperand(MI, opNum, O);
O << ")";
}
//#if CH >= CH7_1
// The DAG data node, mem_ea of Cpu0InstrInfo.td, cannot be disabled by
// ch7_1, only opcode node can be disabled.
void Cpu0InstPrinter::
printMemOperandEA(const MCInst *MI, int opNum, raw_ostream &O) {
// when using stack locations for not load/store instructions
// print the same way as all normal 3 operand instructions.
printOperand(MI, opNum, O);
O << ", ";
printOperand(MI, opNum+1, O);
return;
}
//#endif
lbdex/chapters/Chapter3_2/InstPrinter/CMakeLists.txt
add_llvm_component_library(LLVMCpu0AsmPrinter
Cpu0InstPrinter.cpp
LINK_COMPONENTS
Support
ADD_TO_COMPONENT
Cpu0
)
Cpu0GenAsmWrite.inc has the implementations of Cpu0InstPrinter::printInstruction() and Cpu0InstPrinter::getRegisterName(). Both of these functions can be auto-generated from the information we defined in Cpu0InstrInfo.td and Cpu0RegisterInfo.td. To let these two functions work in our code, the only thing needed is adding a class Cpu0InstPrinter and include them as did in Chapter3_2.
File Chapter3_2/Cpu0/InstPrinter/Cpu0InstPrinter.cpp includes Cpu0GenAsmWrite.inc and call the auto-generated functions from TableGen. The flow of printing assembly and calling between Cpu0InstPrinter.cpp and Cpu0GenAsmWrite.inc as Fig. 24. Cpu0AsmPrinter::emitInstruction() calls Cpu0MCInstLower::Lower(const MachineInstr *MI, MCInst &OutMI) to extracts MCInst from MachineInstr.
AsmPrinter::OutStreamer is nMCAsmStreamer if llc -filetype=asm; AsmPrinter::OutStreamer is nMCObjectStreamer if llc -filetype=obj as Fig. 32.
Bits is the format of instruction for Opcode, used to print “,” between operands.
Function Cpu0InstPrinter::printMemOperand() defined in Chapter3_2/InstPrinter/Cpu0InstPrinter.cpp as above. It will be triggered since Cpu0InstrInfo.td defined ‘let PrintMethod = "printMemOperand";’ as follows,
lbdex/chapters/Chapter2/Cpu0InstrInfo.td
// Address operand
def mem : Operand<i32> {
let PrintMethod = "printMemOperand";
let MIOperandInfo = (ops CPURegs, simm16);
let EncoderMethod = "getMemEncoding";
}
...
// 32-bit load.
multiclass LoadM32<bits<8> op, string instr_asm, PatFrag OpNode,
bit Pseudo = 0> {
def #NAME# : LoadM<op, instr_asm, OpNode, GPROut, mem, Pseudo>;
}
// 32-bit store.
multiclass StoreM32<bits<8> op, string instr_asm, PatFrag OpNode,
bit Pseudo = 0> {
def #NAME# : StoreM<op, instr_asm, OpNode, CPURegs, mem, Pseudo>;
}
defm LD : LoadM32<0x01, "ld", load_a>;
defm ST : StoreM32<0x02, "st", store_a>;
Cpu0InstPrinter::printMemOperand() will print backend operands for “local variable access”, which is like the following,
ld $2, 16($fp)
st $2, 8($fp)
Next, add Cpu0MCInstLower (Cpu0MCInstLower.h, Cpu0MCInstLower.cpp) as well as Cpu0BaseInfo.h, Cpu0FixupKinds.h and Cpu0MCAsmInfo (Cpu0MCAsmInfo.h, Cpu0MCAsmInfo.cpp) in sub-directory MCTargetDesc as follows,
lbdex/chapters/Chapter3_2/Cpu0MCInstLower.h
//===-- Cpu0MCInstLower.h - Lower MachineInstr to MCInst -------*- C++ -*--===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIB_TARGET_CPU0_CPU0MCINSTLOWER_H
#define LLVM_LIB_TARGET_CPU0_CPU0MCINSTLOWER_H
#include "Cpu0Config.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/CodeGen/MachineOperand.h"
#include "llvm/Support/Compiler.h"
namespace llvm {
class MCContext;
class MCInst;
class MCOperand;
class MachineInstr;
class MachineFunction;
class Cpu0AsmPrinter;
//@1 {
/// This class is used to lower an MachineInstr into an MCInst.
class LLVM_LIBRARY_VISIBILITY Cpu0MCInstLower {
//@2
typedef MachineOperand::MachineOperandType MachineOperandType;
MCContext *Ctx;
Cpu0AsmPrinter &AsmPrinter;
public:
Cpu0MCInstLower(Cpu0AsmPrinter &asmprinter);
void Initialize(MCContext* C);
void Lower(const MachineInstr *MI, MCInst &OutMI) const;
MCOperand LowerOperand(const MachineOperand& MO, unsigned offset = 0) const;
};
}
#endif
lbdex/chapters/Chapter3_2/Cpu0MCInstLower.cpp
//===-- Cpu0MCInstLower.cpp - Convert Cpu0 MachineInstr to MCInst ---------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains code to lower Cpu0 MachineInstrs to their corresponding
// MCInst records.
//
//===----------------------------------------------------------------------===//
#include "Cpu0MCInstLower.h"
#include "Cpu0AsmPrinter.h"
#include "Cpu0InstrInfo.h"
#include "MCTargetDesc/Cpu0BaseInfo.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;
Cpu0MCInstLower::Cpu0MCInstLower(Cpu0AsmPrinter &asmprinter)
: AsmPrinter(asmprinter) {}
void Cpu0MCInstLower::Initialize(MCContext* C) {
Ctx = C;
}
static void CreateMCInst(MCInst& Inst, unsigned Opc, const MCOperand& Opnd0,
const MCOperand& Opnd1,
const MCOperand& Opnd2 = MCOperand()) {
Inst.setOpcode(Opc);
Inst.addOperand(Opnd0);
Inst.addOperand(Opnd1);
if (Opnd2.isValid())
Inst.addOperand(Opnd2);
}
//@LowerOperand {
MCOperand Cpu0MCInstLower::LowerOperand(const MachineOperand& MO,
unsigned offset) const {
MachineOperandType MOTy = MO.getType();
switch (MOTy) {
//@2
default: llvm_unreachable("unknown operand type");
case MachineOperand::MO_Register:
// Ignore all implicit register operands.
if (MO.isImplicit()) break;
return MCOperand::createReg(MO.getReg());
case MachineOperand::MO_Immediate:
return MCOperand::createImm(MO.getImm() + offset);
case MachineOperand::MO_RegisterMask:
break;
}
return MCOperand();
}
void Cpu0MCInstLower::Lower(const MachineInstr *MI, MCInst &OutMI) const {
OutMI.setOpcode(MI->getOpcode());
for (unsigned i = 0, e = MI->getNumOperands(); i != e; ++i) {
const MachineOperand &MO = MI->getOperand(i);
MCOperand MCOp = LowerOperand(MO);
if (MCOp.isValid())
OutMI.addOperand(MCOp);
}
}
lbdex/chapters/Chapter3_2/MCTargetDesc/Cpu0BaseInfo.h
//===-- Cpu0BaseInfo.h - Top level definitions for CPU0 MC ------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains small standalone helper functions and enum definitions for
// the Cpu0 target useful for the compiler back-end and the MC libraries.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIB_TARGET_CPU0_MCTARGETDESC_CPU0BASEINFO_H
#define LLVM_LIB_TARGET_CPU0_MCTARGETDESC_CPU0BASEINFO_H
#include "Cpu0Config.h"
#include "Cpu0MCTargetDesc.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/Support/DataTypes.h"
#include "llvm/Support/ErrorHandling.h"
namespace llvm {
/// Cpu0II - This namespace holds all of the target specific flags that
/// instruction info tracks.
//@Cpu0II
namespace Cpu0II {
/// Target Operand Flag enum.
enum TOF {
//===------------------------------------------------------------------===//
// Cpu0 Specific MachineOperand flags.
MO_NO_FLAG,
/// MO_GOT_CALL - Represents the offset into the global offset table at
/// which the address of a call site relocation entry symbol resides
/// during execution. This is different from the above since this flag
/// can only be present in call instructions.
MO_GOT_CALL,
/// MO_GPREL - Represents the offset from the current gp value to be used
/// for the relocatable object file being produced.
MO_GPREL,
/// MO_ABS_HI/LO - Represents the hi or low part of an absolute symbol
/// address.
MO_ABS_HI,
MO_ABS_LO,
/// MO_GOT_HI16/LO16 - Relocations used for large GOTs.
MO_GOT_HI16,
MO_GOT_LO16
}; // enum TOF {
enum {
//===------------------------------------------------------------------===//
// Instruction encodings. These are the standard/most common forms for
// Cpu0 instructions.
//
// Pseudo - This represents an instruction that is a pseudo instruction
// or one that has not been implemented yet. It is illegal to code generate
// it, but tolerated for intermediate implementation stages.
Pseudo = 0,
/// FrmR - This form is for instructions of the format R.
FrmR = 1,
/// FrmI - This form is for instructions of the format I.
FrmI = 2,
/// FrmJ - This form is for instructions of the format J.
FrmJ = 3,
/// FrmOther - This form is for instructions that have no specific format.
FrmOther = 4,
FormMask = 15
};
}
}
#endif
lbdex/chapters/Chapter3_2/Cpu0MCAsmInfo.h
//===-- Cpu0MCAsmInfo.h - Cpu0 Asm Info ------------------------*- C++ -*--===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains the declaration of the Cpu0MCAsmInfo class.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIB_TARGET_CPU0_MCTARGETDESC_CPU0MCASMINFO_H
#define LLVM_LIB_TARGET_CPU0_MCTARGETDESC_CPU0MCASMINFO_H
#include "Cpu0Config.h"
#if CH >= CH3_2
#include "llvm/MC/MCAsmInfoELF.h"
namespace llvm {
class Triple;
class Cpu0MCAsmInfo : public MCAsmInfoELF {
void anchor() override;
public:
explicit Cpu0MCAsmInfo(const Triple &TheTriple);
};
} // namespace llvm
#endif // #if CH >= CH3_2
#endif
lbdex/chapters/Chapter3_2/Cpu0MCAsmInfo.cpp
//===-- Cpu0MCAsmInfo.cpp - Cpu0 Asm Properties ---------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains the declarations of the Cpu0MCAsmInfo properties.
//
//===----------------------------------------------------------------------===//
#include "Cpu0MCAsmInfo.h"
#if CH >= CH3_2
#include "llvm/ADT/Triple.h"
using namespace llvm;
void Cpu0MCAsmInfo::anchor() { }
Cpu0MCAsmInfo::Cpu0MCAsmInfo(const Triple &TheTriple) {
if ((TheTriple.getArch() == Triple::cpu0))
IsLittleEndian = false; // the default of IsLittleEndian is true
AlignmentIsInBytes = false;
Data16bitsDirective = "\t.2byte\t";
Data32bitsDirective = "\t.4byte\t";
Data64bitsDirective = "\t.8byte\t";
PrivateGlobalPrefix = "$";
// PrivateLabelPrefix: display $BB for the labels of basic block
PrivateLabelPrefix = "$";
CommentString = "#";
ZeroDirective = "\t.space\t";
GPRel32Directive = "\t.gpword\t";
GPRel64Directive = "\t.gpdword\t";
WeakRefDirective = "\t.weak\t";
UseAssignmentForEHBegin = true;
SupportsDebugInformation = true;
ExceptionsType = ExceptionHandling::DwarfCFI;
DwarfRegNumForCFI = true;
}
#endif // #if CH >= CH3_2
Finally, add code in Cpu0MCTargetDesc.cpp to register Cpu0InstPrinter as below. It also registers other classes (register, instruction and subtarget) which defined in Chapter3_1 at this point.
lbdex/chapters/Chapter3_2/MCTargetDesc/Cpu0MCTargetDesc.h
namespace llvm {
class MCAsmBackend;
class MCCodeEmitter;
class MCContext;
class MCInstrInfo;
class MCObjectWriter;
class MCRegisterInfo;
class MCSubtargetInfo;
class StringRef;
...
class raw_ostream;
...
}
lbdex/chapters/Chapter3_2/MCTargetDesc/Cpu0MCTargetDesc.cpp
#include "InstPrinter/Cpu0InstPrinter.h"
#include "Cpu0MCAsmInfo.h"
/// Select the Cpu0 Architecture Feature for the given triple and cpu name.
/// The function will be called at command 'llvm-objdump -d' for Cpu0 elf input.
static std::string selectCpu0ArchFeature(const Triple &TT, StringRef CPU) {
std::string Cpu0ArchFeature;
if (CPU.empty() || CPU == "generic") {
if (TT.getArch() == Triple::cpu0 || TT.getArch() == Triple::cpu0el) {
if (CPU.empty() || CPU == "cpu032II") {
Cpu0ArchFeature = "+cpu032II";
}
else {
if (CPU == "cpu032I") {
Cpu0ArchFeature = "+cpu032I";
}
}
}
}
return Cpu0ArchFeature;
}
//@1 }
static MCInstrInfo *createCpu0MCInstrInfo() {
MCInstrInfo *X = new MCInstrInfo();
InitCpu0MCInstrInfo(X); // defined in Cpu0GenInstrInfo.inc
return X;
}
static MCRegisterInfo *createCpu0MCRegisterInfo(const Triple &TT) {
MCRegisterInfo *X = new MCRegisterInfo();
InitCpu0MCRegisterInfo(X, Cpu0::SW); // defined in Cpu0GenRegisterInfo.inc
return X;
}
static MCSubtargetInfo *createCpu0MCSubtargetInfo(const Triple &TT,
StringRef CPU, StringRef FS) {
std::string ArchFS = selectCpu0ArchFeature(TT,CPU);
if (!FS.empty()) {
if (!ArchFS.empty())
ArchFS = ArchFS + "," + FS.str();
else
ArchFS = FS.str();
}
return createCpu0MCSubtargetInfoImpl(TT, CPU, /*TuneCPU*/ CPU, ArchFS);
// createCpu0MCSubtargetInfoImpl defined in Cpu0GenSubtargetInfo.inc
}
static MCAsmInfo *createCpu0MCAsmInfo(const MCRegisterInfo &MRI,
const Triple &TT,
const MCTargetOptions &Options) {
MCAsmInfo *MAI = new Cpu0MCAsmInfo(TT);
unsigned SP = MRI.getDwarfRegNum(Cpu0::SP, true);
MCCFIInstruction Inst = MCCFIInstruction::createDefCfaRegister(nullptr, SP);
MAI->addInitialFrameState(Inst);
return MAI;
}
static MCInstPrinter *createCpu0MCInstPrinter(const Triple &T,
unsigned SyntaxVariant,
const MCAsmInfo &MAI,
const MCInstrInfo &MII,
const MCRegisterInfo &MRI) {
return new Cpu0InstPrinter(MAI, MII, MRI);
}
namespace {
class Cpu0MCInstrAnalysis : public MCInstrAnalysis {
public:
Cpu0MCInstrAnalysis(const MCInstrInfo *Info) : MCInstrAnalysis(Info) {}
};
}
static MCInstrAnalysis *createCpu0MCInstrAnalysis(const MCInstrInfo *Info) {
return new Cpu0MCInstrAnalysis(Info);
}
//@2 {
extern "C" void LLVMInitializeCpu0TargetMC() {
for (Target *T : {&TheCpu0Target, &TheCpu0elTarget}) {
// Register the MC asm info.
RegisterMCAsmInfoFn X(*T, createCpu0MCAsmInfo);
// Register the MC instruction info.
TargetRegistry::RegisterMCInstrInfo(*T, createCpu0MCInstrInfo);
// Register the MC register info.
TargetRegistry::RegisterMCRegInfo(*T, createCpu0MCRegisterInfo);
// Register the MC subtarget info.
TargetRegistry::RegisterMCSubtargetInfo(*T,
createCpu0MCSubtargetInfo);
// Register the MC instruction analyzer.
TargetRegistry::RegisterMCInstrAnalysis(*T, createCpu0MCInstrAnalysis);
// Register the MCInstPrinter.
TargetRegistry::RegisterMCInstPrinter(*T,
createCpu0MCInstPrinter);
}
}
//@2 }
lbdex/chapters/Chapter3_2/MCTargetDesc/CMakeLists.txt
Cpu0MCAsmInfo.cpp
To make the registration clearly, summary as the following diagram, Fig. 25.
Above createCpu0MCAsmInfo() registering the object of class Cpu0MCAsmInfo for target TheCpu0Target and TheCpu0elTarget. TheCpu0Target is for big endian and TheCpu0elTarget is for little endian. Cpu0MCAsmInfo is derived from MCAsmInfo which is an llvm built-in class. Most code is implemented in it’s parent, backend reuses those code by inheritance.
Above createCpu0MCInstrInfo() instancing MCInstrInfo object X, and initialize it by InitCpu0MCInstrInfo(X). Since InitCpu0MCInstrInfo(X) is defined in Cpu0GenInstrInfo.inc, this function will add the information from Cpu0InstrInfo.td we specified.
Above createCpu0MCInstPrinter() instancing Cpu0InstPrinter to take care printing function for instructions.
Above createCpu0MCRegisterInfo() is similar to “Register function of MC instruction info”, but it initializes the register information specified in Cpu0RegisterInfo.td. They share some values from instruction/register td description, so no need to specify them again in Initialize routine if they are consistant with td description files.
Above createCpu0MCSubtargetInfo() instancing MCSubtargetInfo object and initialize with Cpu0.td information.
According “section Target Registration” [3], we can register Cpu0 backend classes at LLVMInitializeCpu0TargetMC() on demand by the dynamic register mechanism as the above function, LLVMInitializeCpu0TargetMC().
Now, it’s time to work with AsmPrinter as follows,
lbdex/chapters/Chapter3_2/Cpu0AsmPrinter.h
//===-- Cpu0AsmPrinter.h - Cpu0 LLVM Assembly Printer ----------*- C++ -*--===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Cpu0 Assembly printer class.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIB_TARGET_CPU0_CPU0ASMPRINTER_H
#define LLVM_LIB_TARGET_CPU0_CPU0ASMPRINTER_H
#include "Cpu0Config.h"
#include "Cpu0MachineFunction.h"
#include "Cpu0MCInstLower.h"
#include "Cpu0Subtarget.h"
#include "Cpu0TargetMachine.h"
#include "llvm/CodeGen/AsmPrinter.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Target/TargetMachine.h"
namespace llvm {
class MCStreamer;
class MachineInstr;
class MachineBasicBlock;
class Module;
class raw_ostream;
class LLVM_LIBRARY_VISIBILITY Cpu0AsmPrinter : public AsmPrinter {
void EmitInstrWithMacroNoAT(const MachineInstr *MI);
private:
// lowerOperand - Convert a MachineOperand into the equivalent MCOperand.
bool lowerOperand(const MachineOperand &MO, MCOperand &MCOp);
public:
const Cpu0Subtarget *Subtarget;
const Cpu0FunctionInfo *Cpu0FI;
Cpu0MCInstLower MCInstLowering;
explicit Cpu0AsmPrinter(TargetMachine &TM,
std::unique_ptr<MCStreamer> Streamer)
: AsmPrinter(TM, std::move(Streamer)),
MCInstLowering(*this) {
Subtarget = static_cast<Cpu0TargetMachine &>(TM).getSubtargetImpl();
}
StringRef getPassName() const override {
return "Cpu0 Assembly Printer";
}
virtual bool runOnMachineFunction(MachineFunction &MF) override;
//- emitInstruction() must exists or will have run time error.
void emitInstruction(const MachineInstr *MI) override;
void printSavedRegsBitmask(raw_ostream &O);
void printHex32(unsigned int Value, raw_ostream &O);
void emitFrameDirective();
const char *getCurrentABIString() const;
void emitFunctionEntryLabel() override;
void emitFunctionBodyStart() override;
void emitFunctionBodyEnd() override;
void emitStartOfAsmFile(Module &M) override;
void PrintDebugValueComment(const MachineInstr *MI, raw_ostream &OS);
};
}
#endif
lbdex/chapters/Chapter3_2/Cpu0AsmPrinter.cpp
//===-- Cpu0AsmPrinter.cpp - Cpu0 LLVM Assembly Printer -------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains a printer that converts from our internal representation
// of machine-dependent LLVM code to GAS-format CPU0 assembly language.
//
//===----------------------------------------------------------------------===//
#include "Cpu0AsmPrinter.h"
#include "InstPrinter/Cpu0InstPrinter.h"
#include "MCTargetDesc/Cpu0BaseInfo.h"
#include "Cpu0.h"
#include "Cpu0InstrInfo.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/Twine.h"
#include "llvm/CodeGen/MachineConstantPool.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineInstr.h"
#include "llvm/CodeGen/MachineMemOperand.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Mangler.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSymbol.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Target/TargetLoweringObjectFile.h"
#include "llvm/Target/TargetOptions.h"
using namespace llvm;
#define DEBUG_TYPE "cpu0-asm-printer"
bool Cpu0AsmPrinter::runOnMachineFunction(MachineFunction &MF) {
Cpu0FI = MF.getInfo<Cpu0FunctionInfo>();
AsmPrinter::runOnMachineFunction(MF);
return true;
}
//@EmitInstruction {
//- emitInstruction() must exists or will have run time error.
void Cpu0AsmPrinter::emitInstruction(const MachineInstr *MI) {
//@EmitInstruction body {
if (MI->isDebugValue()) {
SmallString<128> Str;
raw_svector_ostream OS(Str);
PrintDebugValueComment(MI, OS);
return;
}
//@print out instruction:
// Print out both ordinary instruction and boudle instruction
MachineBasicBlock::const_instr_iterator I = MI->getIterator();
MachineBasicBlock::const_instr_iterator E = MI->getParent()->instr_end();
do {
if (I->isPseudo())
llvm_unreachable("Pseudo opcode found in emitInstruction()");
MCInst TmpInst0;
// Call Cpu0MCInstLower::Lower(const MachineInstr *MI, MCInst &OutMI) to
// extracts MCInst from MachineInstr.
MCInstLowering.Lower(&*I, TmpInst0);
OutStreamer->emitInstruction(TmpInst0, getSubtargetInfo());
} while ((++I != E) && I->isInsideBundle()); // Delay slot check
}
//@EmitInstruction }
//===----------------------------------------------------------------------===//
//
// Cpu0 Asm Directives
//
// -- Frame directive "frame Stackpointer, Stacksize, RARegister"
// Describe the stack frame.
//
// -- Mask directives "(f)mask bitmask, offset"
// Tells the assembler which registers are saved and where.
// bitmask - contain a little endian bitset indicating which registers are
// saved on function prologue (e.g. with a 0x80000000 mask, the
// assembler knows the register 31 (RA) is saved at prologue.
// offset - the position before stack pointer subtraction indicating where
// the first saved register on prologue is located. (e.g. with a
//
// Consider the following function prologue:
//
// .frame $fp,48,$ra
// .mask 0xc0000000,-8
// addiu $sp, $sp, -48
// st $ra, 40($sp)
// st $fp, 36($sp)
//
// With a 0xc0000000 mask, the assembler knows the register 31 (RA) and
// 30 (FP) are saved at prologue. As the save order on prologue is from
// left to right, RA is saved first. A -8 offset means that after the
// stack pointer subtration, the first register in the mask (RA) will be
// saved at address 48-8=40.
//
//===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
// Mask directives
//===----------------------------------------------------------------------===//
// .frame $sp,8,$lr
//-> .mask 0x00000000,0
// .set noreorder
// .set nomacro
// Create a bitmask with all callee saved registers for CPU or Floating Point
// registers. For CPU registers consider LR, GP and FP for saving if necessary.
void Cpu0AsmPrinter::printSavedRegsBitmask(raw_ostream &O) {
// CPU and FPU Saved Registers Bitmasks
unsigned CPUBitmask = 0;
int CPUTopSavedRegOff;
// Set the CPU and FPU Bitmasks
const MachineFrameInfo &MFI = MF->getFrameInfo();
const TargetRegisterInfo *TRI = MF->getSubtarget().getRegisterInfo();
const std::vector<CalleeSavedInfo> &CSI = MFI.getCalleeSavedInfo();
// size of stack area to which FP callee-saved regs are saved.
unsigned CPURegSize = TRI->getRegSizeInBits(Cpu0::CPURegsRegClass) / 8;
unsigned i = 0, e = CSI.size();
// Set CPU Bitmask.
for (; i != e; ++i) {
unsigned Reg = CSI[i].getReg();
unsigned RegNum = TRI->getEncodingValue(Reg);
CPUBitmask |= (1 << RegNum);
}
CPUTopSavedRegOff = CPUBitmask ? -CPURegSize : 0;
// Print CPUBitmask
O << "\t.mask \t"; printHex32(CPUBitmask, O);
O << ',' << CPUTopSavedRegOff << '\n';
}
// Print a 32 bit hex number with all numbers.
void Cpu0AsmPrinter::printHex32(unsigned Value, raw_ostream &O) {
O << "0x";
for (int i = 7; i >= 0; i--)
O.write_hex((Value & (0xF << (i*4))) >> (i*4));
}
//===----------------------------------------------------------------------===//
// Frame and Set directives
//===----------------------------------------------------------------------===//
//-> .frame $sp,8,$lr
// .mask 0x00000000,0
// .set noreorder
// .set nomacro
/// Frame Directive
void Cpu0AsmPrinter::emitFrameDirective() {
const TargetRegisterInfo &RI = *MF->getSubtarget().getRegisterInfo();
unsigned stackReg = RI.getFrameRegister(*MF);
unsigned returnReg = RI.getRARegister();
unsigned stackSize = MF->getFrameInfo().getStackSize();
if (OutStreamer->hasRawTextSupport())
OutStreamer->emitRawText("\t.frame\t$" +
StringRef(Cpu0InstPrinter::getRegisterName(stackReg)).lower() +
"," + Twine(stackSize) + ",$" +
StringRef(Cpu0InstPrinter::getRegisterName(returnReg)).lower());
}
/// Emit Set directives.
const char *Cpu0AsmPrinter::getCurrentABIString() const {
switch (static_cast<Cpu0TargetMachine &>(TM).getABI().GetEnumValue()) {
case Cpu0ABIInfo::ABI::O32: return "abiO32";
case Cpu0ABIInfo::ABI::S32: return "abiS32";
default: llvm_unreachable("Unknown Cpu0 ABI");
}
}
// .type main,@function
//-> .ent main # @main
// main:
void Cpu0AsmPrinter::emitFunctionEntryLabel() {
if (OutStreamer->hasRawTextSupport())
OutStreamer->emitRawText("\t.ent\t" + Twine(CurrentFnSym->getName()));
OutStreamer->emitLabel(CurrentFnSym);
}
// .frame $sp,8,$pc
// .mask 0x00000000,0
//-> .set noreorder
//@-> .set nomacro
/// EmitFunctionBodyStart - Targets can override this to emit stuff before
/// the first basic block in the function.
void Cpu0AsmPrinter::emitFunctionBodyStart() {
MCInstLowering.Initialize(&MF->getContext());
emitFrameDirective();
if (OutStreamer->hasRawTextSupport()) {
SmallString<128> Str;
raw_svector_ostream OS(Str);
printSavedRegsBitmask(OS);
OutStreamer->emitRawText(OS.str());
OutStreamer->emitRawText(StringRef("\t.set\tnoreorder"));
OutStreamer->emitRawText(StringRef("\t.set\tnomacro"));
if (Cpu0FI->getEmitNOAT())
OutStreamer->emitRawText(StringRef("\t.set\tnoat"));
}
}
//-> .set macro
//-> .set reorder
//-> .end main
/// EmitFunctionBodyEnd - Targets can override this to emit stuff after
/// the last basic block in the function.
void Cpu0AsmPrinter::emitFunctionBodyEnd() {
// There are instruction for this macros, but they must
// always be at the function end, and we can't emit and
// break with BB logic.
if (OutStreamer->hasRawTextSupport()) {
if (Cpu0FI->getEmitNOAT())
OutStreamer->emitRawText(StringRef("\t.set\tat"));
OutStreamer->emitRawText(StringRef("\t.set\tmacro"));
OutStreamer->emitRawText(StringRef("\t.set\treorder"));
OutStreamer->emitRawText("\t.end\t" + Twine(CurrentFnSym->getName()));
}
}
// .section .mdebug.abi32
// .previous
void Cpu0AsmPrinter::emitStartOfAsmFile(Module &M) {
// FIXME: Use SwitchSection.
// Tell the assembler which ABI we are using
if (OutStreamer->hasRawTextSupport())
OutStreamer->emitRawText("\t.section .mdebug." +
Twine(getCurrentABIString()));
// return to previous section
if (OutStreamer->hasRawTextSupport())
OutStreamer->emitRawText(StringRef("\t.previous"));
}
void Cpu0AsmPrinter::PrintDebugValueComment(const MachineInstr *MI,
raw_ostream &OS) {
// TODO: implement
OS << "PrintDebugValueComment()";
}
// Force static initialization.
extern "C" void LLVMInitializeCpu0AsmPrinter() {
RegisterAsmPrinter<Cpu0AsmPrinter> X(TheCpu0Target);
RegisterAsmPrinter<Cpu0AsmPrinter> Y(TheCpu0elTarget);
}
When instruction is ready to print, function Cpu0AsmPrinter::EmitInstruction() will be triggered first. And then it will call OutStreamer.EmitInstruction() to print OP code and register name according the information from Cpu0GenInstrInfo.inc and Cpu0GenRegisterInfo.inc both registered at dynamic register function, LLVMInitializeCpu0TargetMC(). Notice, file Cpu0InstPrinter.cpp only print operand while the OP code information come from Cpu0InstrInfo.td.
Add the following code to Cpu0ISelLowering.cpp.
lbdex/chapters/Chapter3_2/Cpu0ISelLowering.cpp
Cpu0TargetLowering::
Cpu0TargetLowering(Cpu0TargetMachine &TM)
: TargetLowering(TM, new Cpu0TargetObjectFile()),
Subtarget(&TM.getSubtarget<Cpu0Subtarget>()) {
//- Set .align 2
// It will emit .align 2 later
setMinFunctionAlignment(2);
// must, computeRegisterProperties - Once all of the register classes are
// added, this allows us to compute derived properties we expose.
computeRegisterProperties();
}
Add the following code to Cpu0MachineFunction.h since the Cpu0AsmPrinter.cpp will call getEmitNOAT().
lbdex/chapters/Chapter3_2/Cpu0MachineFunction.h
class Cpu0FunctionInfo : public MachineFunctionInfo {
public:
Cpu0FunctionInfo(MachineFunction& MF)
: ...
, EmitNOAT(false)
{}
...
bool getEmitNOAT() const { return EmitNOAT; }
void setEmitNOAT() { EmitNOAT = true; }
private:
...
bool EmitNOAT;
};
lbdex/chapters/Chapter3_2/CMakeLists.txt
tablegen(LLVM Cpu0GenCodeEmitter.inc -gen-emitter)
tablegen(LLVM Cpu0GenMCCodeEmitter.inc -gen-emitter)
tablegen(LLVM Cpu0GenAsmWriter.inc -gen-asm-writer)
...
add_llvm_target(Cpu0CodeGen
Cpu0AsmPrinter.cpp
Cpu0MCInstLower.cpp
LINK_COMPONENTS
...
Cpu0AsmPrinter
...
)
...
add_subdirectory(InstPrinter)
Now, run Chapter3_2/Cpu0 for AsmPrinter support, will get new error message as follows,
118-165-78-230:input Jonathan$ /Users/Jonathan/llvm/test/build/
bin/llc -march=cpu0 -relocation-model=pic -filetype=asm ch3.bc -o
ch3.cpu0.s
/Users/Jonathan/llvm/test/build/bin/llc: target does not
support generation of this file type!
The llc
fails to compile IR code into machine code since we don’t implement
class Cpu0DAGToDAGISel.
Add Cpu0DAGToDAGISel class¶
The IR DAG to machine instruction DAG transformation is introduced in the previous chapter. Now, let’s check what IR DAG nodes the file ch3.bc has. List ch3.ll as follows,
// ch3.ll
define i32 @main() nounwind uwtable {
%1 = alloca i32, align 4
store i32 0, i32* %1
ret i32 0
}
As above, ch3.ll uses the IR DAG node store, ret. So, the definitions in Cpu0InstrInfo.td as below is enough. The ADDiu used for stack adjustment which will be needed in later section “Add Prologue/Epilogue functions” of this chapter. IR DAG is defined in file include/llvm/Target/TargetSelectionDAG.td.
lbdex/chapters/Chapter2/Cpu0InstrInfo.td
//===----------------------------------------------------------------------===//
/// Load and Store Instructions
/// aligned
defm LD : LoadM32<0x01, "ld", load_a>;
defm ST : StoreM32<0x02, "st", store_a>;
/// Arithmetic Instructions (ALU Immediate)
// IR "add" defined in include/llvm/Target/TargetSelectionDAG.td, line 315 (def add).
def ADDiu : ArithLogicI<0x09, "addiu", add, simm16, immSExt16, CPURegs>;
let isReturn=1, isTerminator=1, hasDelaySlot=1, isBarrier=1, hasCtrlDep=1 in
def RetLR : Cpu0Pseudo<(outs), (ins), "", [(Cpu0Ret)]>;
def RET : RetBase<GPROut>;
Add class Cpu0DAGToDAGISel (Cpu0ISelDAGToDAG.cpp) to CMakeLists.txt, and add the following fragment to Cpu0TargetMachine.cpp,
lbdex/chapters/Chapter3_3/CMakeLists.txt
add_llvm_target(
...
Cpu0ISelDAGToDAG.cpp
Cpu0SEISelDAGToDAG.cpp
...
)
The following code in Cpu0TargetMachine.cpp will create a pass in instruction selection stage.
lbdex/chapters/Chapter3_3/Cpu0TargetMachine.cpp
#include "Cpu0SEISelDAGToDAG.h"
...
class Cpu0PassConfig : public TargetPassConfig {
public:
...
bool addInstSelector() override;
};
...
// Install an instruction selector pass using
// the ISelDag to gen Cpu0 code.
bool Cpu0PassConfig::addInstSelector() {
addPass(createCpu0SEISelDag(getCpu0TargetMachine(), getOptLevel()));
return false;
}
lbdex/chapters/Chapter3_3/Cpu0ISelDAGToDAG.h
//===---- Cpu0ISelDAGToDAG.h - A Dag to Dag Inst Selector for Cpu0 --------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines an instruction selector for the CPU0 target.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIB_TARGET_CPU0_CPU0ISELDAGTODAG_H
#define LLVM_LIB_TARGET_CPU0_CPU0ISELDAGTODAG_H
#include "Cpu0Config.h"
#include "Cpu0.h"
#include "Cpu0Subtarget.h"
#include "Cpu0TargetMachine.h"
#include "llvm/CodeGen/SelectionDAGISel.h"
#include "llvm/IR/Type.h"
#include "llvm/Support/Debug.h"
//===----------------------------------------------------------------------===//
// Instruction Selector Implementation
//===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
// Cpu0DAGToDAGISel - CPU0 specific code to select CPU0 machine
// instructions for SelectionDAG operations.
//===----------------------------------------------------------------------===//
namespace llvm {
class Cpu0DAGToDAGISel : public SelectionDAGISel {
public:
explicit Cpu0DAGToDAGISel(Cpu0TargetMachine &TM, CodeGenOpt::Level OL)
: SelectionDAGISel(TM, OL), Subtarget(nullptr) {}
// Pass Name
StringRef getPassName() const override {
return "CPU0 DAG->DAG Pattern Instruction Selection";
}
bool runOnMachineFunction(MachineFunction &MF) override;
protected:
/// Keep a pointer to the Cpu0Subtarget around so that we can make the right
/// decision when generating code for different targets.
const Cpu0Subtarget *Subtarget;
private:
// Include the pieces autogenerated from the target description.
#include "Cpu0GenDAGISel.inc"
/// getTargetMachine - Return a reference to the TargetMachine, casted
/// to the target-specific type.
const Cpu0TargetMachine &getTargetMachine() {
return static_cast<const Cpu0TargetMachine &>(TM);
}
void Select(SDNode *N) override;
virtual bool trySelect(SDNode *Node) = 0;
// Complex Pattern.
bool SelectAddr(SDNode *Parent, SDValue N, SDValue &Base, SDValue &Offset);
// getImm - Return a target constant with the specified value.
inline SDValue getImm(const SDNode *Node, unsigned Imm) {
return CurDAG->getTargetConstant(Imm, SDLoc(Node), Node->getValueType(0));
}
virtual void processFunctionAfterISel(MachineFunction &MF) = 0;
};
}
#endif
lbdex/chapters/Chapter3_3/Cpu0ISelDAGToDAG.cpp
//===-- Cpu0ISelDAGToDAG.cpp - A Dag to Dag Inst Selector for Cpu0 --------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines an instruction selector for the CPU0 target.
//
//===----------------------------------------------------------------------===//
#include "Cpu0ISelDAGToDAG.h"
#include "Cpu0.h"
#include "Cpu0MachineFunction.h"
#include "Cpu0RegisterInfo.h"
#include "Cpu0SEISelDAGToDAG.h"
#include "Cpu0TargetMachine.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/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetMachine.h"
using namespace llvm;
#define DEBUG_TYPE "cpu0-isel"
//===----------------------------------------------------------------------===//
// Instruction Selector Implementation
//===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
// Cpu0DAGToDAGISel - CPU0 specific code to select CPU0 machine
// instructions for SelectionDAG operations.
//===----------------------------------------------------------------------===//
bool Cpu0DAGToDAGISel::runOnMachineFunction(MachineFunction &MF) {
bool Ret = SelectionDAGISel::runOnMachineFunction(MF);
return Ret;
}
//@SelectAddr {
/// ComplexPattern used on Cpu0InstrInfo
/// Used on Cpu0 Load/Store instructions
bool Cpu0DAGToDAGISel::
SelectAddr(SDNode *Parent, SDValue Addr, SDValue &Base, SDValue &Offset) {
//@SelectAddr }
EVT ValTy = Addr.getValueType();
SDLoc DL(Addr);
// If Parent is an unaligned f32 load or store, select a (base + index)
// floating point load/store instruction (luxc1 or suxc1).
const LSBaseSDNode* LS = 0;
if (Parent && (LS = dyn_cast<LSBaseSDNode>(Parent))) {
EVT VT = LS->getMemoryVT();
if (VT.getSizeInBits() / 8 > LS->getAlignment()) {
assert(0 && "Unaligned loads/stores not supported for this type.");
if (VT == MVT::f32)
return false;
}
}
// if Address is FI, get the TargetFrameIndex.
if (FrameIndexSDNode *FIN = dyn_cast<FrameIndexSDNode>(Addr)) {
Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), ValTy);
Offset = CurDAG->getTargetConstant(0, DL, ValTy);
return true;
}
Base = Addr;
Offset = CurDAG->getTargetConstant(0, DL, ValTy);
return true;
}
//@Select {
/// Select instructions not customized! Used for
/// expanded, promoted and normal instructions
void Cpu0DAGToDAGISel::Select(SDNode *Node) {
//@Select }
unsigned Opcode = Node->getOpcode();
// If we have a custom node, we already have selected!
if (Node->isMachineOpcode()) {
LLVM_DEBUG(errs() << "== "; Node->dump(CurDAG); errs() << "\n");
Node->setNodeId(-1);
return;
}
// See if subclasses can handle this node.
if (trySelect(Node))
return;
switch(Opcode) {
default: break;
}
// Select the default instruction
SelectCode(Node);
}
lbdex/chapters/Chapter3_3/Cpu0SEISelDAGToDAG.h
//===-- Cpu0SEISelDAGToDAG.h - A Dag to Dag Inst Selector for Cpu0SE -----===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Subclass of Cpu0DAGToDAGISel specialized for cpu032.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIB_TARGET_CPU0_CPU0SEISELDAGTODAG_H
#define LLVM_LIB_TARGET_CPU0_CPU0SEISELDAGTODAG_H
#include "Cpu0Config.h"
#include "Cpu0ISelDAGToDAG.h"
namespace llvm {
class Cpu0SEDAGToDAGISel : public Cpu0DAGToDAGISel {
public:
explicit Cpu0SEDAGToDAGISel(Cpu0TargetMachine &TM, CodeGenOpt::Level OL)
: Cpu0DAGToDAGISel(TM, OL) {}
private:
bool runOnMachineFunction(MachineFunction &MF) override;
bool trySelect(SDNode *Node) override;
void processFunctionAfterISel(MachineFunction &MF) override;
// Insert instructions to initialize the global base register in the
// first MBB of the function.
// void initGlobalBaseReg(MachineFunction &MF);
};
FunctionPass *createCpu0SEISelDag(Cpu0TargetMachine &TM,
CodeGenOpt::Level OptLevel);
}
#endif
lbdex/chapters/Chapter3_3/Cpu0ISelDAGToDAG.cpp
//===-- Cpu0SEISelDAGToDAG.cpp - A Dag to Dag Inst Selector for Cpu0SE ----===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Subclass of Cpu0DAGToDAGISel specialized for cpu032.
//
//===----------------------------------------------------------------------===//
#include "Cpu0SEISelDAGToDAG.h"
#include "MCTargetDesc/Cpu0BaseInfo.h"
#include "Cpu0.h"
#include "Cpu0MachineFunction.h"
#include "Cpu0RegisterInfo.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/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/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetMachine.h"
using namespace llvm;
#define DEBUG_TYPE "cpu0-isel"
bool Cpu0SEDAGToDAGISel::runOnMachineFunction(MachineFunction &MF) {
Subtarget = &static_cast<const Cpu0Subtarget &>(MF.getSubtarget());
return Cpu0DAGToDAGISel::runOnMachineFunction(MF);
}
void Cpu0SEDAGToDAGISel::processFunctionAfterISel(MachineFunction &MF) {
}
//@selectNode
bool Cpu0SEDAGToDAGISel::trySelect(SDNode *Node) {
unsigned Opcode = Node->getOpcode();
SDLoc DL(Node);
///
// Instruction Selection not handled by the auto-generated
// tablegen selection should be handled here.
///
///
// Instruction Selection not handled by the auto-generated
// tablegen selection should be handled here.
///
EVT NodeTy = Node->getValueType(0);
unsigned MultOpc;
switch(Opcode) {
default: break;
}
return false;
}
FunctionPass *llvm::createCpu0SEISelDag(Cpu0TargetMachine &TM,
CodeGenOpt::Level OptLevel) {
return new Cpu0SEDAGToDAGISel(TM, OptLevel);
}
Function Cpu0DAGToDAGISel::Select() of Cpu0ISelDAGToDAG.cpp is for the selection of “OP code DAG node” while Cpu0DAGToDAGISel::SelectAddr() is for the selection of “DATA DAG node with addr type” which defined in Chapter2/Cpu0InstrInfo.td. This method’s name corresponding to Chapter2/Cpu0InstrInfo.td as follows,
lbdex/chapters/Chapter2/Cpu0InstrInfo.td
def addr : ComplexPattern<iPTR, 2, "SelectAddr", [frameindex], [SDNPWantParent]>;
The iPTR, ComplexPattern, frameindex and SDNPWantParent defined as follows,
llvm/include/llvm/Target/TargetSelection.td
def SDNPWantParent : SDNodeProperty; // ComplexPattern gets the parent
...
def frameindex : SDNode<"ISD::FrameIndex", SDTPtrLeaf, [],
"FrameIndexSDNode">;
...
// Complex patterns, e.g. X86 addressing mode, requires pattern matching code
// in C++. NumOperands is the number of operands returned by the select function;
// SelectFunc is the name of the function used to pattern match the max. pattern;
// RootNodes are the list of possible root nodes of the sub-dags to match.
// e.g. X86 addressing mode - def addr : ComplexPattern<4, "SelectAddr", [add]>;
//
class ComplexPattern<ValueType ty, int numops, string fn,
list<SDNode> roots = [], list<SDNodeProperty> props = []> {
ValueType Ty = ty;
int NumOperands = numops;
string SelectFunc = fn;
list<SDNode> RootNodes = roots;
list<SDNodeProperty> Properties = props;
}
llvm/include/llvm/CodeGen/ValueTypes.td
// Pseudo valuetype mapped to the current pointer size.
def iPTR : ValueType<0 , 255>;
Build Chapter3_3 and run with it, finding the error message of Chapter3_2 is gone. The new error message for Chapter3_3 as follows,
118-165-78-230:input Jonathan$ /Users/Jonathan/llvm/test/build/
bin/llc -march=cpu0 -relocation-model=pic -filetype=asm ch3.bc -o
ch3.cpu0.s
...
LLVM ERROR: Cannot select: t6: ch = Cpu0ISD::Ret t4, Register:i32 $lr
t5: i32 = Register $lr
...
Above can display the error message DAG node “Cpu0ISD::Ret” because the following code added in Chapter3_1/Cpu0ISelLowering.cpp.
lbdex/chapters/Chapter3_1/Cpu0ISelLowering.cpp
const char *Cpu0TargetLowering::getTargetNodeName(unsigned Opcode) const {
switch (Opcode) {
case Cpu0ISD::JmpLink: return "Cpu0ISD::JmpLink";
case Cpu0ISD::TailCall: return "Cpu0ISD::TailCall";
case Cpu0ISD::Hi: return "Cpu0ISD::Hi";
case Cpu0ISD::Lo: return "Cpu0ISD::Lo";
case Cpu0ISD::GPRel: return "Cpu0ISD::GPRel";
case Cpu0ISD::Ret: return "Cpu0ISD::Ret";
case Cpu0ISD::EH_RETURN: return "Cpu0ISD::EH_RETURN";
case Cpu0ISD::DivRem: return "Cpu0ISD::DivRem";
case Cpu0ISD::DivRemU: return "Cpu0ISD::DivRemU";
case Cpu0ISD::Wrapper: return "Cpu0ISD::Wrapper";
default: return NULL;
}
}
Handle return register $lr¶
The following code is the result of running Mips backend with ch3.cpp.
JonathantekiiMac:input Jonathan$ ~/llvm/debug/build/bin/llc
-march=mips -relocation-model=pic -filetype=asm ch3.bc -o -
.text
.abicalls
.section .mdebug.abi32,"",@progbits
.nan legacy
.file "ch3.bc"
.text
.globl main
.align 2
.type main,@function
.set nomicromips
.set nomips16
.ent main
main: # @main
.frame $fp,8,$ra
.mask 0x40000000,-4
.fmask 0x00000000,0
.set noreorder
.set nomacro
.set noat
# BB#0:
addiu $sp, $sp, -8
sw $fp, 4($sp) # 4-byte Folded Spill
move $fp, $sp
sw $zero, 0($fp)
addiu $2, $zero, 0
move $sp, $fp
lw $fp, 4($sp) # 4-byte Folded Reload
jr $ra
addiu $sp, $sp, 8
.set at
.set macro
.set reorder
.end main
$func_end0:
.size main, ($func_end0)-main
As you can see, Mips return to the caller by using “jr $ra” where $ra is a specific register which keeps the caller’s next instruction address. And it saves the return value in register $2. If we only create DAGs directly, then will having the following two problems.
LLVM can allocate any register for return value, for instance $3, rather than keeps it in $2.
2. LLVM will allocate a register randomly to “jr” since jr needs one operand. For instance, it generate “jr $8” rather than “jr $ra”.
If Backend uses the “jal sub-routine” and “jr”, and put the return address in the specific register $ra, then no the second problem. But in Mips, it allows programmer uses “jal $rx, sub-routine” and “jr $rx” whereas $rx is not $ra. Allowing programmer uses other register but $ra providing more flexibility in programming of high level language such as C with assembly. File ch8_2_longbranch.cpp in the following is an example, it uses jr $1 without spill $ra register. This will save a lot of time if it is in a hot function.
lbdex/input/ch8_2_longbranch.cpp
int test_longbranch()
{
volatile int a = 2;
volatile int b = 1;
int result = 0;
if (a < b)
result = 1;
return result;
}
JonathantekiiMac:input Jonathan$ clang -target mips-unknown-linux-gnu -c
ch8_2_longbranch.cpp -emit-llvm -o ch8_2_longbranch.bc
JonathantekiiMac:input Jonathan$ ~/llvm/debug/build/bin/llc
-march=mips -relocation-model=pic -filetype=asm -force-mips-long-branch
ch8_2_longbranch.bc -o -
...
.ent _Z15test_longbranchv
_Z15test_longbranchv: # @_Z15test_longbranchv
.frame $fp,16,$ra
.mask 0x40000000,-4
.fmask 0x00000000,0
.set noreorder
.set nomacro
.set noat
# BB#0:
addiu $sp, $sp, -16
sw $fp, 12($sp) # 4-byte Folded Spill
move $fp, $sp
addiu $1, $zero, 2
sw $1, 8($fp)
addiu $2, $zero, 1
sw $2, 4($fp)
sw $zero, 0($fp)
lw $1, 8($fp)
lw $3, 4($fp)
slt $1, $1, $3
bnez $1, $BB0_3
nop
# BB#1:
addiu $sp, $sp, -8
sw $ra, 0($sp)
lui $1, %hi(($BB0_4)-($BB0_2))
bal $BB0_2
addiu $1, $1, %lo(($BB0_4)-($BB0_2))
$BB0_2:
addu $1, $ra, $1
lw $ra, 0($sp)
jr $1
addiu $sp, $sp, 8
$BB0_3:
sw $2, 0($fp)
$BB0_4:
lw $2, 0($fp)
move $sp, $fp
lw $fp, 12($sp) # 4-byte Folded Reload
jr $ra
addiu $sp, $sp, 16
.set at
.set macro
.set reorder
.end _Z15test_longbranchv
$func_end0:
.size _Z15test_longbranchv, ($func_end0)-_Z15test_longbranchv
The following code handle the return register $lr.
lbdex/chapters/Chapter3_4/Cpu0CallingConv.td
def RetCC_Cpu0EABI : CallingConv<[
// i32 are returned in registers V0, V1, A0, A1
CCIfType<[i32], CCAssignToReg<[V0, V1, A0, A1]>>
]>;
def RetCC_Cpu0 : CallingConv<[
CCDelegateTo<RetCC_Cpu0EABI>
]>;
lbdex/chapters/Chapter3_4/Cpu0InstrFormats.td
// Cpu0 Pseudo Instructions Format
class Cpu0Pseudo<dag outs, dag ins, string asmstr, list<dag> pattern>:
Cpu0Inst<outs, ins, asmstr, pattern, IIPseudo, Pseudo> {
let isCodeGenOnly = 1;
let isPseudo = 1;
}
lbdex/chapters/Chapter3_4/Cpu0InstrInfo.td
let Predicates = [Ch3_4] in {
let isReturn=1, isTerminator=1, hasDelaySlot=1, isBarrier=1, hasCtrlDep=1 in
def RetLR : Cpu0Pseudo<(outs), (ins), "", [(Cpu0Ret)]>;
}
lbdex/chapters/Chapter3_4/Cpu0ISelLowering.h
/// Cpu0CC - This class provides methods used to analyze formal and call
/// arguments and inquire about calling convention information.
class Cpu0CC {
public:
enum SpecialCallingConvType {
NoSpecialCallingConv
};
Cpu0CC(CallingConv::ID CallConv, bool IsO32, CCState &Info,
SpecialCallingConvType SpecialCallingConv = NoSpecialCallingConv);
void analyzeCallResult(const SmallVectorImpl<ISD::InputArg> &Ins,
bool IsSoftFloat, const SDNode *CallNode,
const Type *RetTy) const;
void analyzeReturn(const SmallVectorImpl<ISD::OutputArg> &Outs,
bool IsSoftFloat, const Type *RetTy) const;
const CCState &getCCInfo() const { return CCInfo; }
/// hasByValArg - Returns true if function has byval arguments.
bool hasByValArg() const { return !ByValArgs.empty(); }
/// reservedArgArea - The size of the area the caller reserves for
/// register arguments. This is 16-byte if ABI is O32.
unsigned reservedArgArea() const;
typedef SmallVectorImpl<ByValArgInfo>::const_iterator byval_iterator;
byval_iterator byval_begin() const { return ByValArgs.begin(); }
byval_iterator byval_end() const { return ByValArgs.end(); }
private:
/// Return the type of the register which is used to pass an argument or
/// return a value. This function returns f64 if the argument is an i64
/// value which has been generated as a result of softening an f128 value.
/// Otherwise, it just returns VT.
MVT getRegVT(MVT VT, bool IsSoftFloat) const;
template<typename Ty>
void analyzeReturn(const SmallVectorImpl<Ty> &RetVals, bool IsSoftFloat,
const SDNode *CallNode, const Type *RetTy) const;
CCState &CCInfo;
CallingConv::ID CallConv;
bool IsO32;
SmallVector<ByValArgInfo, 2> ByValArgs;
};
lbdex/chapters/Chapter3_4/Cpu0ISelLowering.cpp
SDValue
Cpu0TargetLowering::LowerReturn(SDValue Chain,
CallingConv::ID CallConv, bool IsVarArg,
const SmallVectorImpl<ISD::OutputArg> &Outs,
const SmallVectorImpl<SDValue> &OutVals,
const SDLoc &DL, SelectionDAG &DAG) const {
// CCValAssign - represent the assignment of
// the return value to a location
SmallVector<CCValAssign, 16> RVLocs;
MachineFunction &MF = DAG.getMachineFunction();
// CCState - Info about the registers and stack slot.
CCState CCInfo(CallConv, IsVarArg, MF, RVLocs,
*DAG.getContext());
Cpu0CC Cpu0CCInfo(CallConv, ABI.IsO32(),
CCInfo);
// Analyze return values.
Cpu0CCInfo.analyzeReturn(Outs, Subtarget.abiUsesSoftFloat(),
MF.getFunction().getReturnType());
SDValue Flag;
SmallVector<SDValue, 4> RetOps(1, Chain);
// Copy the result values into the output registers.
for (unsigned i = 0; i != RVLocs.size(); ++i) {
SDValue Val = OutVals[i];
CCValAssign &VA = RVLocs[i];
assert(VA.isRegLoc() && "Can only return in registers!");
if (RVLocs[i].getValVT() != RVLocs[i].getLocVT())
Val = DAG.getNode(ISD::BITCAST, DL, RVLocs[i].getLocVT(), Val);
Chain = DAG.getCopyToReg(Chain, DL, VA.getLocReg(), Val, Flag);
// Guarantee that all emitted copies are stuck together with flags.
Flag = Chain.getValue(1);
RetOps.push_back(DAG.getRegister(VA.getLocReg(), VA.getLocVT()));
}
//@Ordinary struct type: 2 {
// The cpu0 ABIs for returning structs by value requires that we copy
// the sret argument into $v0 for the return. We saved the argument into
// a virtual register in the entry block, so now we copy the value out
// and into $v0.
if (MF.getFunction().hasStructRetAttr()) {
Cpu0FunctionInfo *Cpu0FI = MF.getInfo<Cpu0FunctionInfo>();
unsigned Reg = Cpu0FI->getSRetReturnReg();
if (!Reg)
llvm_unreachable("sret virtual register not created in the entry block");
SDValue Val =
DAG.getCopyFromReg(Chain, DL, Reg, getPointerTy(DAG.getDataLayout()));
unsigned V0 = Cpu0::V0;
Chain = DAG.getCopyToReg(Chain, DL, V0, Val, Flag);
Flag = Chain.getValue(1);
RetOps.push_back(DAG.getRegister(V0, getPointerTy(DAG.getDataLayout())));
}
//@Ordinary struct type: 2 }
RetOps[0] = Chain; // Update chain.
// Add the flag if we have it.
if (Flag.getNode())
RetOps.push_back(Flag);
// Return on Cpu0 is always a "ret $lr"
return DAG.getNode(Cpu0ISD::Ret, DL, MVT::Other, RetOps);
}
template<typename Ty>
void Cpu0TargetLowering::Cpu0CC::
analyzeReturn(const SmallVectorImpl<Ty> &RetVals, bool IsSoftFloat,
const SDNode *CallNode, const Type *RetTy) const {
CCAssignFn *Fn;
Fn = RetCC_Cpu0;
for (unsigned I = 0, E = RetVals.size(); I < E; ++I) {
MVT VT = RetVals[I].VT;
ISD::ArgFlagsTy Flags = RetVals[I].Flags;
MVT RegVT = this->getRegVT(VT, IsSoftFloat);
if (Fn(I, VT, RegVT, CCValAssign::Full, Flags, this->CCInfo)) {
#ifndef NDEBUG
dbgs() << "Call result #" << I << " has unhandled type "
<< EVT(VT).getEVTString() << '\n';
#endif
llvm_unreachable(nullptr);
}
}
}
void Cpu0TargetLowering::Cpu0CC::
analyzeCallResult(const SmallVectorImpl<ISD::InputArg> &Ins, bool IsSoftFloat,
const SDNode *CallNode, const Type *RetTy) const {
analyzeReturn(Ins, IsSoftFloat, CallNode, RetTy);
}
void Cpu0TargetLowering::Cpu0CC::
analyzeReturn(const SmallVectorImpl<ISD::OutputArg> &Outs, bool IsSoftFloat,
const Type *RetTy) const {
analyzeReturn(Outs, IsSoftFloat, nullptr, RetTy);
}
unsigned Cpu0TargetLowering::Cpu0CC::reservedArgArea() const {
return (IsO32 && (CallConv != CallingConv::Fast)) ? 8 : 0;
}
MVT Cpu0TargetLowering::Cpu0CC::getRegVT(MVT VT,
bool IsSoftFloat) const {
if (IsSoftFloat || IsO32)
return VT;
return VT;
}
lbdex/chapters/Chapter3_4/Cpu0MachineFunction.h
/// Cpu0FunctionInfo - This class is derived from MachineFunction private
/// Cpu0 target-specific information for each MachineFunction.
class Cpu0FunctionInfo : public MachineFunctionInfo {
SRetReturnReg(0), CallsEhReturn(false), CallsEhDwarf(false),
unsigned getSRetReturnReg() const { return SRetReturnReg; }
void setSRetReturnReg(unsigned Reg) { SRetReturnReg = Reg; }
bool hasByvalArg() const { return HasByvalArg; }
void setFormalArgInfo(unsigned Size, bool HasByval) {
IncomingArgSize = Size;
HasByvalArg = HasByval;
}
/// SRetReturnReg - 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;
/// True if function has a byval argument.
bool HasByvalArg;
/// Size of incoming argument area.
unsigned IncomingArgSize;
/// CallsEhReturn - Whether the function calls llvm.eh.return.
bool CallsEhReturn;
/// CallsEhDwarf - Whether the function calls llvm.eh.dwarf.
bool CallsEhDwarf;
/// Frame objects for spilling eh data registers.
int EhDataRegFI[2];
}
lbdex/chapters/Chapter3_4/Cpu0SEInstrInfo.h
//@expandPostRAPseudo
bool expandPostRAPseudo(MachineInstr &MI) const override;
private:
void expandRetLR(MachineBasicBlock &MBB, MachineBasicBlock::iterator I) const;
lbdex/chapters/Chapter3_4/Cpu0SEInstrInfo.cpp
//@expandPostRAPseudo
/// Expand Pseudo instructions into real backend instructions
bool Cpu0SEInstrInfo::expandPostRAPseudo(MachineInstr &MI) const {
//@expandPostRAPseudo-body
MachineBasicBlock &MBB = *MI.getParent();
switch (MI.getDesc().getOpcode()) {
default:
return false;
case Cpu0::RetLR:
expandRetLR(MBB, MI);
break;
}
MBB.erase(MI);
return true;
}
void Cpu0SEInstrInfo::expandRetLR(MachineBasicBlock &MBB,
MachineBasicBlock::iterator I) const {
BuildMI(MBB, I, I->getDebugLoc(), get(Cpu0::RET)).addReg(Cpu0::LR);
}
Build Chapter3_4 and run with it, finding the error message in Chapter3_3 is gone. The compile result will hang on and please press “ctrl+C” to abort as follows,
118-165-78-230:input Jonathan$ clang -target mips-unknown-linux-gnu -c
ch3.cpp -emit-llvm -o ch3.bc
118-165-78-230:input Jonathan$ ~/llvm/test/build/bin/llvm-dis
ch3.bc -o -
...
define i32 @main() #0 {
%1 = alloca i32, align 4
store i32 0, i32* %1
ret i32 0
}
118-165-78-230:input Jonathan$ /Users/Jonathan/llvm/test/build/
bin/llc -march=cpu0 -relocation-model=pic -filetype=asm ch3.bc -o -
...
.text
.section .mdebug.abiO32
.previous
.file "ch3.bc"
^C
It hangs on because Cpu0 backend has not handled stack slot for local variables.
Instruction “store i32 0, i32* %1” in above IR need Cpu0 allocate a stack slot
and save to the stack slot.
However, the ch3.cpp can be run with option clang -O2
as follows,
118-165-78-230:input Jonathan$ clang -O2 -target mips-unknown-linux-gnu -c
ch3.cpp -emit-llvm -o ch3.bc
118-165-78-230:input Jonathan$ ~/llvm/test/build/bin/llvm-dis
ch3.bc -o -
...
define i32 @main() #0 {
ret i32 0
}
118-165-78-230:input Jonathan$ /Users/Jonathan/llvm/test/build/
bin/llc -march=cpu0 -relocation-model=pic -filetype=asm ch3.bc -o -
.text
.section .mdebug.abiO32
.previous
.file "ch3.bc"
.globl main
.align 2
.type main,@function
.ent main # @main
main:
.frame $sp,0,$lr
.mask 0x00000000,0
.set noreorder
.set nomacro
# BB#0:
addiu $2, $zero, 0
ret $lr
.set macro
.set reorder
.end main
$func_end0:
.size main, ($func_end0)-main
To see how the ‘DAG->DAG Pattern Instruction Selection’ work in llc, let’s
compile with llc -print-before-all -print-after-all
option and get the following result.
The DAGs which before and after instruction selection stage are shown as follows,
118-165-78-230:input Jonathan$ clang -O2 -target mips-unknown-linux-gnu -c
ch3.cpp -emit-llvm -o ch3.bc
118-165-78-12:input Jonathan$ /Users/Jonathan/llvm/test/build/
bin/llc -march=cpu0 -relocation-model=pic -filetype=asm
-print-before-all -print-after-all ch3.bc -o -
...
*** IR Dump After Module Verifier ***
; Function Attrs: nounwind readnone
define i32 @main() #0 {
ret i32 0
}
...
Initial selection DAG: BB#0 'main:'
SelectionDAG has 5 nodes:
t0: ch = EntryToken
t3: ch,glue = CopyToReg t0, Register:i32 %V0, Constant:i32<0>
t4: ch = Cpu0ISD::Ret t3, Register:i32 %V0, t3:1
...
===== Instruction selection begins: BB#0 ''
Selecting: t4: ch = Cpu0ISD::Ret t3, Register:i32 %V0, t3:1
ISEL: Starting pattern match on root node: t4: ch = Cpu0ISD::Ret t3, Register:i32 %V0, t3:1
Morphed node: t4: ch = RetLR Register:i32 %V0, t3, t3:1
ISEL: Match complete!
Selecting: t3: ch,glue = CopyToReg t0, Register:i32 %V0, Constant:i32<0>
Selecting: t2: i32 = Register %V0
Selecting: t1: i32 = Constant<0>
ISEL: Starting pattern match on root node: t1: i32 = Constant<0>
Initial Opcode index to 3158
Morphed node: t1: i32 = ADDiu Register:i32 %ZERO, TargetConstant:i32<0>
ISEL: Match complete!
Selecting: t0: ch = EntryToken
===== Instruction selection ends:
Selected selection DAG: BB#0 'main:'
SelectionDAG has 7 nodes:
t0: ch = EntryToken
t1: i32 = ADDiu Register:i32 %ZERO, TargetConstant:i32<0>
t3: ch,glue = CopyToReg t0, Register:i32 %V0, t1
t4: ch = RetLR Register:i32 %V0, t3, t3:1
...
********** REWRITE VIRTUAL REGISTERS **********
********** Function: main
********** REGISTER MAP **********
[%vreg0 -> %V0] GPROut
0B BB#0: derived from LLVM BB %0
16B %vreg0<def> = ADDiu %ZERO, 0; GPROut:%vreg0
32B %V0<def> = COPY %vreg0<kill>; GPROut:%vreg0
48B RetLR %V0<imp-use>
> %V0<def> = ADDiu %ZERO, 0
> %V0<def> = COPY %V0<kill>
Identity copy: %V0<def> = COPY %V0<kill>
deleted.
> RetLR %V0<imp-use>
# *** IR Dump After Virtual Register Rewriter ***:
# Machine code for function main: Properties: <Post SSA, tracking liveness, AllVRegsAllocated>
0B BB#0: derived from LLVM BB %0
16B %V0<def> = ADDiu %ZERO, 0
48B RetLR %V0<imp-use>
...
********** EXPANDING POST-RA PSEUDO INSTRS **********
********** Function: main
# *** IR Dump After Post-RA pseudo instruction expansion pass ***:
# Machine code for function main: Properties: <Post SSA, tracking liveness, AllVRegsAllocated>
BB#0: derived from LLVM BB %0
%V0<def> = ADDiu %ZERO, 0
RET %LR
...
.globl main
.p2align 2
.type main,@function
.ent main # @main
main:
.frame $sp,0,$lr
.mask 0x00000000,0
.set noreorder
.set nomacro
# BB#0:
addiu $2, $zero, 0
ret $lr
.set macro
.set reorder
.end main
$func_end0:
.size main, ($func_end0)-main
.ident "Apple LLVM version 7.0.0 (clang-700.1.76)"
.section ".note.GNU-stack","",@progbits
Summary above translation into Table: Chapter 3 .bc IR instructions.
.bc |
Lower |
ISel |
RVR |
Post-RA |
AsmP |
---|---|---|---|---|---|
constant 0 |
constant 0 |
ADDiu |
ADDiu |
ADDiu |
addiu |
ret |
Cpu0ISD::Ret |
CopyToReg,RetLR |
RetLR |
RET |
ret |
Lower: Initial selection DAG (Cpu0ISelLowering.cpp, LowerReturn(…))
ISel: Instruction selection
RVR: REWRITE VIRTUAL REGISTERS, remove CopyToReg
AsmP: Cpu0 Asm Printer
Post-RA: Post-RA pseudo instruction expansion pass
From above llc -print-before-all -print-after-all
display, we see ret is translated into
Cpu0ISD::Ret in stage Optimized legalized selection DAG, and then
translated into Cpu0 instructions ret finally.
Since ret use constant 0 (ret i32 0 in this example), the
constant 0 will be translated into “addiu $2, $zero, 0” via the following
pattern defined in Cpu0InstrInfo.td.
lbdex/chapters/Chapter2/Cpu0InstrInfo.td
// Small immediates
def : Pat<(i32 immSExt16:$in),
(ADDiu ZERO, imm:$in)>;
In order to handle IR ret, these codes in Cpu0InstrInfo.td do things as below.
Declare a pseudo node Cpu0::RetLR to takes care the IR Cpu0ISD::Ret by the following code,
lbdex/chapters/Chapter3_4/Cpu0InstrInfo.td
// Return
def Cpu0Ret : SDNode<"Cpu0ISD::Ret", SDTNone,
[SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>;
let Predicates = [Ch3_4] in {
let isReturn=1, isTerminator=1, hasDelaySlot=1, isBarrier=1, hasCtrlDep=1 in
def RetLR : Cpu0Pseudo<(outs), (ins), "", [(Cpu0Ret)]>;
}
Create Cpu0ISD::Ret node in LowerReturn() of Cpu0ISelLowering.cpp, which is called when meets keyword of return in C. Remind, In LowerReturn() put return value in register $2 ($v0).
After instruction selection, the Cpu0ISD::Ret is replaced by Cpu0::RetLR as below. This effect come from “def RetLR” as step 1.
===== Instruction selection begins: BB#0 'entry'
Selecting: 0x1ea4050: ch = Cpu0ISD::Ret 0x1ea3f50, 0x1ea3e50,
0x1ea3f50:1 [ID=27]
ISEL: Starting pattern match on root node: 0x1ea4050: ch = Cpu0ISD::Ret
0x1ea3f50, 0x1ea3e50, 0x1ea3f50:1 [ID=27]
Morphed node: 0x1ea4050: ch = RetLR 0x1ea3e50, 0x1ea3f50, 0x1ea3f50:1
...
ISEL: Match complete!
=> 0x1ea4050: ch = RetLR 0x1ea3e50, 0x1ea3f50, 0x1ea3f50:1
...
===== Instruction selection ends:
Selected selection DAG: BB#0 'main:entry'
SelectionDAG has 28 nodes:
...
0x1ea3e50: <multiple use>
0x1ea3f50: <multiple use>
0x1ea3f50: <multiple use>
0x1ea4050: ch = RetLR 0x1ea3e50, 0x1ea3f50, 0x1ea3f50:1
Expand the Cpu0ISD::RetLR into instruction Cpu0::RET $lr in “Post-RA pseudo instruction expansion pass” stage by the code in Chapter3_4/Cpu0SEInstrInfo.cpp as above. This stage come after the register allocation, so we can replace the V0 ($r2) by LR ($lr) without any side effect.
Print assembly or obj according the information of *.inc generated by TableGen from *.td as the following at “Cpu0 Assembly Print” stage.
lbdex/chapters/Chapter2/Cpu0InstrInfo.td
//@JumpFR {
let isBranch=1, isTerminator=1, isBarrier=1, imm16=0, hasDelaySlot = 1,
isIndirectBranch = 1 in
class JumpFR<bits<8> op, string instr_asm, RegisterClass RC>:
FL<op, (outs), (ins RC:$ra),
!strconcat(instr_asm, "\t$ra"), [(brind RC:$ra)], IIBranch> {
let rb = 0;
let imm16 = 0;
}
def RET : RetBase<GPROut>;
Stage |
Function |
---|---|
Write Code |
Declare a pseudo node Cpu0::RetLR |
for IR Cpu0::Ret; |
|
Before CPU0 DAG->DAG Pattern Instruction Selection |
Create Cpu0ISD::Ret DAG |
Instruction selection |
Cpu0::Ret is replaced by Cpu0::RetLR |
Post-RA pseudo instruction expansion pass |
Cpu0::RetLR -> Cpu0::RET $lr |
Cpu0 Assembly Printer |
Print according “def RET” |
Function LowerReturn() of Cpu0ISelLowering.cpp handle return variable correctly. Chapter3_4/Cpu0ISelLowering.cpp create Cpu0ISD::Ret node in LowerReturn() which is called by llvm system when it meets C’s keyword of return. More specificly, it creates DAGs (Cpu0ISD::Ret (CopyToReg %X, %V0, %Y), %V0, Flag). Since the the V0 register is assigned in CopyToReg and Cpu0ISD::Ret use V0, the CopyToReg with V0 register will live out and won’t be removed at any later optimization steps. Remember, if use “return DAG.getNode(Cpu0ISD::Ret, DL, MVT::Other, Chain, DAG.getRegister(Cpu0::LR, MVT::i32));” instead of “return DAG.getNode (Cpu0ISD::Ret, DL, MVT::Other, &RetOps[0], RetOps.size());” then the V0 register won’t be live out, and the previous DAG (CopyToReg %X, %V0, %Y) will be removed at later optimization steps. It ending with the return value is error.
Add Prologue/Epilogue functions¶
Concept¶
Following come from tricore_llvm.pdf section “4.4.2 Non-static Register Information ”.
For some target architectures, some aspects of the target architecture’s register set are dependent upon variable factors and have to be determined at runtime. As a consequence, they cannot be generated statically from a TableGen description – although that would be possible for the bulk of them in the case of the TriCore backend. Among them are the following points:
Callee-saved registers. Normally, the ABI specifies a set of registers that a function must save on entry and restore on return if their contents are possibly modified during execution.
Reserved registers. Although the set of unavailable registers is already defined in the TableGen file, TriCoreRegisterInfo contains a method that marks all non-allocatable register numbers in a bit vector.
The following methods are implemented:
emitPrologue() inserts prologue code at the beginning of a function. Thanks to TriCore’s context model, this is a trivial task as it is not required to save any registers manually. The only thing that has to be done is reserving space for the function’s stack frame by decrementing the stack pointer. In addition, if the function needs a frame pointer, the frame register %a14 is set to the old value of the stack pointer beforehand.
emitEpilogue() is intended to emit instructions to destroy the stack frame and restore all previously saved registers before returning from a function. However, as %a10 (stack pointer), %a11 (return address), and %a14 (frame pointer, if any) are all part of the upper context, no epilogue code is needed at all. All cleanup operations are performed implicitly by the ret instruction.
eliminateFrameIndex() is called for each instruction that references a word of data in a stack slot. All previous passes of the code generator have been addressing stack slots through an abstract frame index and an immediate offset. The purpose of this function is to translate such a reference into a register–offset pair. Depending on whether the machine function that contains the instruction has a fixed or a variable stack frame, either the stack pointer %a10 or the frame pointer %a14 is used as the base register. The offset is computed accordingly. Fig. 26 demonstrates for both cases how a stack slot is addressed.
If the addressing mode of the affected instruction cannot handle the address because the offset is too large (the offset field has 10 bits for the BO addressing mode and 16 bits for the BOL mode), a sequence of instructions is emitted that explicitly computes the effective address. Interim results are put into an unused address register. If none is available, an already occupied address register is scavenged. For this purpose, LLVM’s framework offers a class named RegScavenger that takes care of all the details.
Prologue and Epilogue functions¶
The Prologue and Epilogue functions as follows,
lbdex/chapters/Chapter3_5/Cpu0SEFrameLowering.cpp
void Cpu0SEFrameLowering::emitPrologue(MachineFunction &MF,
MachineBasicBlock &MBB) const {
MachineFrameInfo &MFI = MF.getFrameInfo();
Cpu0FunctionInfo *Cpu0FI = MF.getInfo<Cpu0FunctionInfo>();
const Cpu0SEInstrInfo &TII =
*static_cast<const Cpu0SEInstrInfo*>(STI.getInstrInfo());
const Cpu0RegisterInfo &RegInfo =
*static_cast<const Cpu0RegisterInfo *>(STI.getRegisterInfo());
MachineBasicBlock::iterator MBBI = MBB.begin();
DebugLoc dl = MBBI != MBB.end() ? MBBI->getDebugLoc() : DebugLoc();
Cpu0ABIInfo ABI = STI.getABI();
unsigned SP = Cpu0::SP;
const TargetRegisterClass *RC = &Cpu0::GPROutRegClass;
// First, compute final stack size.
uint64_t StackSize = MFI.getStackSize();
// No need to allocate space on the stack.
if (StackSize == 0 && !MFI.adjustsStack()) return;
MachineModuleInfo &MMI = MF.getMMI();
const MCRegisterInfo *MRI = MMI.getContext().getRegisterInfo();
// Adjust stack.
TII.adjustStackPtr(SP, -StackSize, MBB, MBBI);
// emit ".cfi_def_cfa_offset StackSize"
unsigned CFIIndex =
MF.addFrameInst(
MCCFIInstruction::cfiDefCfaOffset(nullptr, StackSize));
BuildMI(MBB, MBBI, dl, TII.get(TargetOpcode::CFI_INSTRUCTION))
.addCFIIndex(CFIIndex);
const std::vector<CalleeSavedInfo> &CSI = MFI.getCalleeSavedInfo();
if (!CSI.empty()) {
// Find the instruction past the last instruction that saves a callee-saved
// register to the stack.
for (unsigned i = 0; i < CSI.size(); ++i)
++MBBI;
// Iterate over list of callee-saved registers and emit .cfi_offset
// directives.
for (std::vector<CalleeSavedInfo>::const_iterator I = CSI.begin(),
E = CSI.end(); I != E; ++I) {
int64_t Offset = MFI.getObjectOffset(I->getFrameIdx());
unsigned Reg = I->getReg();
{
// Reg is in CPURegs.
unsigned CFIIndex = MF.addFrameInst(MCCFIInstruction::createOffset(
nullptr, MRI->getDwarfRegNum(Reg, true), Offset));
BuildMI(MBB, MBBI, dl, TII.get(TargetOpcode::CFI_INSTRUCTION))
.addCFIIndex(CFIIndex);
}
}
}
}
void Cpu0SEFrameLowering::emitEpilogue(MachineFunction &MF,
MachineBasicBlock &MBB) const {
MachineBasicBlock::iterator MBBI = MBB.getFirstTerminator();
MachineFrameInfo &MFI = MF.getFrameInfo();
Cpu0FunctionInfo *Cpu0FI = MF.getInfo<Cpu0FunctionInfo>();
const Cpu0SEInstrInfo &TII =
*static_cast<const Cpu0SEInstrInfo *>(STI.getInstrInfo());
const Cpu0RegisterInfo &RegInfo =
*static_cast<const Cpu0RegisterInfo *>(STI.getRegisterInfo());
DebugLoc DL = MBBI != MBB.end() ? MBBI->getDebugLoc() : DebugLoc();
Cpu0ABIInfo ABI = STI.getABI();
unsigned SP = Cpu0::SP;
// Get the number of bytes from FrameInfo
uint64_t StackSize = MFI.getStackSize();
if (!StackSize)
return;
// Adjust stack.
TII.adjustStackPtr(SP, StackSize, MBB, MBBI);
}
bool
Cpu0SEFrameLowering::hasReservedCallFrame(const MachineFunction &MF) const {
const MachineFrameInfo &MFI = MF.getFrameInfo();
// Reserve call frame if the size of the maximum call frame fits into 16-bit
// immediate field and there are no variable sized objects on the stack.
// Make sure the second register scavenger spill slot can be accessed with one
// instruction.
return isInt<16>(MFI.getMaxCallFrameSize() + getStackAlignment()) &&
!MFI.hasVarSizedObjects();
}
lbdex/chapters/Chapter3_5/Cpu0MachineFunction.h
unsigned getIncomingArgSize() const { return IncomingArgSize; }
bool callsEhReturn() const { return CallsEhReturn; }
void setCallsEhReturn() { CallsEhReturn = true; }
bool callsEhDwarf() const { return CallsEhDwarf; }
void setCallsEhDwarf() { CallsEhDwarf = true; }
void createEhDataRegsFI();
int getEhDataRegFI(unsigned Reg) const { return EhDataRegFI[Reg]; }
unsigned getMaxCallFrameSize() const { return MaxCallFrameSize; }
void setMaxCallFrameSize(unsigned S) { MaxCallFrameSize = S; }
Now we explain the Prologue and Epilogue further by example code. For the following llvm IR code of ch3.cpp, Chapter3_5 of Cpu0 backend will emit the corresponding machine instructions as follows,
118-165-78-230:input Jonathan$ clang -target mips-unknown-linux-gnu -c
ch3.cpp -emit-llvm -o ch3.bc
118-165-78-230:input Jonathan$ ~/llvm/test/build/bin/llvm-dis
ch3.bc -o -
...
define i32 @main() #0 {
%1 = alloca i32, align 4
store i32 0, i32* %1
ret i32 0
}
118-165-78-230:input Jonathan$ /Users/Jonathan/llvm/test/build/
bin/llc -march=cpu0 -relocation-model=pic -filetype=asm ch3.bc -o -
...
.section .mdebug.abi32
.previous
.file "ch3.bc"
.text
.globl main//static void expandLargeImm\\n
.align 2
.type main,@function
.ent main # @main
main:
.cfi_startproc
.frame $sp,8,$lr
.mask 0x00000000,0
.set noreorder
.set nomacro
# BB#0:
addiu $sp, $sp, -8
$tmp1:
.cfi_def_cfa_offset 8
addiu $2, $zero, 0
st $2, 4($sp)
addiu $sp, $sp, 8
ret $lr
.set macro
.set reorder
.end main
$tmp2:
.size main, ($tmp2)-main
.cfi_endproc
LLVM get the stack size by counting how many virtual registers is assigned to local variables. After that, it calls emitPrologue().
virtual void adjustStackPtr(unsigned SP, int64_t Amount,
MachineBasicBlock &MBB,
MachineBasicBlock::iterator I) const = 0;
lbdex/chapters/Chapter3_5/Cpu0SEInstrInfo.h
/// Adjust SP by Amount bytes.
void adjustStackPtr(unsigned SP, int64_t Amount, MachineBasicBlock &MBB,
MachineBasicBlock::iterator I) const override;
/// Emit a series of instructions to load an immediate. If NewImm is a
/// non-NULL parameter, the last instruction is not emitted, but instead
/// its immediate operand is returned in NewImm.
unsigned loadImmediate(int64_t Imm, MachineBasicBlock &MBB,
MachineBasicBlock::iterator II, const DebugLoc &DL,
unsigned *NewImm) const;
lbdex/chapters/Chapter3_5/Cpu0SEInstrInfo.cpp
/// Adjust SP by Amount bytes.
void Cpu0SEInstrInfo::adjustStackPtr(unsigned SP, int64_t Amount,
MachineBasicBlock &MBB,
MachineBasicBlock::iterator I) const {
DebugLoc DL = I != MBB.end() ? I->getDebugLoc() : DebugLoc();
unsigned ADDu = Cpu0::ADDu;
unsigned ADDiu = Cpu0::ADDiu;
if (isInt<16>(Amount)) {
// addiu sp, sp, amount
BuildMI(MBB, I, DL, get(ADDiu), SP).addReg(SP).addImm(Amount);
}
else { // Expand immediate that doesn't fit in 16-bit.
unsigned Reg = loadImmediate(Amount, MBB, I, DL, nullptr);
BuildMI(MBB, I, DL, get(ADDu), SP).addReg(SP).addReg(Reg, RegState::Kill);
}
}
/// This function generates the sequence of instructions needed to get the
/// result of adding register REG and immediate IMM.
unsigned
Cpu0SEInstrInfo::loadImmediate(int64_t Imm, MachineBasicBlock &MBB,
MachineBasicBlock::iterator II,
const DebugLoc &DL,
unsigned *NewImm) const {
Cpu0AnalyzeImmediate AnalyzeImm;
unsigned Size = 32;
unsigned LUi = Cpu0::LUi;
unsigned ZEROReg = Cpu0::ZERO;
unsigned ATReg = Cpu0::AT;
bool LastInstrIsADDiu = NewImm;
const Cpu0AnalyzeImmediate::InstSeq &Seq =
AnalyzeImm.Analyze(Imm, Size, LastInstrIsADDiu);
Cpu0AnalyzeImmediate::InstSeq::const_iterator Inst = Seq.begin();
assert(Seq.size() && (!LastInstrIsADDiu || (Seq.size() > 1)));
// The first instruction can be a LUi, which is different from other
// instructions (ADDiu, ORI and SLL) in that it does not have a register
// operand.
if (Inst->Opc == LUi)
BuildMI(MBB, II, DL, get(LUi), ATReg).addImm(SignExtend64<16>(Inst->ImmOpnd));
else
BuildMI(MBB, II, DL, get(Inst->Opc), ATReg).addReg(ZEROReg)
.addImm(SignExtend64<16>(Inst->ImmOpnd));
// Build the remaining instructions in Seq.
for (++Inst; Inst != Seq.end() - LastInstrIsADDiu; ++Inst)
BuildMI(MBB, II, DL, get(Inst->Opc), ATReg).addReg(ATReg)
.addImm(SignExtend64<16>(Inst->ImmOpnd));
if (LastInstrIsADDiu)
*NewImm = Inst->ImmOpnd;
return ATReg;
}
In emitPrologue(), it emits machine instructions to adjust sp (stack pointer register) for local variables. For our example, it will emit the instruction,
addiu $sp, $sp, -8
The emitEpilogue() will emit “addiu $sp, $sp, 8”, where 8 is the stack size.
In above ch3.cpp assembly output, it generates “addiu $2, $zero, 0” rather than “ori $2, $zero, 0” because ADDiu defined before ORi as following, so it takes the priority. Of course, if the ORi is defined first, the it will translate into “ori” instruction.
lbdex/chapters/Chapter2/Cpu0InstrInfo.td
// Small immediates
def : Pat<(i32 immSExt16:$in),
(ADDiu ZERO, imm:$in)>;
lbdex/chapters/Chapter3_5/Cpu0InstrInfo.td
let Predicates = [Ch3_5] in {
def : Pat<(i32 immZExt16:$in),
(ORi ZERO, imm:$in)>;
def : Pat<(i32 immLow16Zero:$in),
(LUi (HI16 imm:$in))>;
// Arbitrary immediates
def : Pat<(i32 imm:$imm),
(ORi (LUi (HI16 imm:$imm)), (LO16 imm:$imm))>;
} // let Predicates = [Ch3_4]
Handle stack slot for local variables¶
The following code handle the stack slot for local variables.
lbdex/chapters/Chapter3_5/Cpu0RegisterInfo.cpp
//- If no eliminateFrameIndex(), it will hang on run.
// pure virtual method
// FrameIndex represent objects inside a abstract stack.
// We must replace FrameIndex with an stack/frame pointer
// direct reference.
void Cpu0RegisterInfo::
eliminateFrameIndex(MachineBasicBlock::iterator II, int SPAdj,
unsigned FIOperandNum, RegScavenger *RS) const {
MachineInstr &MI = *II;
MachineFunction &MF = *MI.getParent()->getParent();
MachineFrameInfo &MFI = MF.getFrameInfo();
Cpu0FunctionInfo *Cpu0FI = MF.getInfo<Cpu0FunctionInfo>();
unsigned i = 0;
while (!MI.getOperand(i).isFI()) {
++i;
assert(i < MI.getNumOperands() &&
"Instr doesn't have FrameIndex operand!");
}
LLVM_DEBUG(errs() << "\nFunction : " << MF.getFunction().getName() << "\n";
errs() << "<--------->\n" << MI);
int FrameIndex = MI.getOperand(i).getIndex();
uint64_t stackSize = MF.getFrameInfo().getStackSize();
int64_t spOffset = MF.getFrameInfo().getObjectOffset(FrameIndex);
LLVM_DEBUG(errs() << "FrameIndex : " << FrameIndex << "\n"
<< "spOffset : " << spOffset << "\n"
<< "stackSize : " << stackSize << "\n");
const std::vector<CalleeSavedInfo> &CSI = MFI.getCalleeSavedInfo();
int MinCSFI = 0;
int MaxCSFI = -1;
if (CSI.size()) {
MinCSFI = CSI[0].getFrameIdx();
MaxCSFI = CSI[CSI.size() - 1].getFrameIdx();
}
// The following stack frame objects are always referenced relative to $sp:
// 1. Outgoing arguments.
// 2. Pointer to dynamically allocated stack space.
// 3. Locations for callee-saved registers.
// Everything else is referenced relative to whatever register
// getFrameRegister() returns.
unsigned FrameReg;
FrameReg = Cpu0::SP;
// Calculate final offset.
// - There is no need to change the offset if the frame object is one of the
// following: an outgoing argument, pointer to a dynamically allocated
// stack space or a $gp restore location,
// - If the frame object is any of the following, its offset must be adjusted
// by adding the size of the stack:
// incoming argument, callee-saved register location or local variable.
int64_t Offset;
Offset = spOffset + (int64_t)stackSize;
Offset += MI.getOperand(i+1).getImm();
LLVM_DEBUG(errs() << "Offset : " << Offset << "\n" << "<--------->\n");
// If MI is not a debug value, make sure Offset fits in the 16-bit immediate
// field.
if (!MI.isDebugValue() && !isInt<16>(Offset)) {
errs() << "!!!ERROR!!! Not support large frame over 16-bit at this point.\n"
<< "Though CH3_5 support it."
<< "Reference: "
"http://jonathan2251.github.io/lbd/backendstructure.html#large-stack\n"
<< "However the CH9_3, dynamic-stack-allocation-support bring instruction "
"move $fp, $sp that make it complicated in coding against the tutoral "
"purpose of Cpu0.\n"
<< "Reference: "
"http://jonathan2251.github.io/lbd/funccall.html#dynamic-stack-allocation-support\n";
assert(0 && "(!MI.isDebugValue() && !isInt<16>(Offset))");
}
MI.getOperand(i).ChangeToRegister(FrameReg, false);
MI.getOperand(i+1).ChangeToImmediate(Offset);
}
The eliminateFrameIndex() of Cpu0RegisterInfo.cpp is called after stages “instruction selection” and “registers allocated”. It translates frame index to correct offset of stack pointer by “spOffset = MF.getFrameInfo()->getObjectOffset(FrameIndex);”. For instance of ch3.cpp, displaying the offset calculating as follows,
Spilling live registers at end of block.
BB#0: derived from LLVM BB %0
%V0<def> = ADDiu %ZERO, 0
ST %V0, <fi#0>, 0; mem:ST4[%1]
RetLR %V0<imp-use,kill>
alloc FI(0) at SP[-4]
Function : main
<--------->
ST %V0, <fi#0>, 0; mem:ST4[%1]
FrameIndex : 0
spOffset : -4
stackSize : 8
Offset : 4
<--------->
...
.file "ch3.bc"
...
.frame $sp,8,$lr
...
# BB#0:
addiu $sp, $sp, -8
$tmp1:
.cfi_def_cfa_offset 8
addiu $2, $zero, 0
st $2, 4($sp)
...
lbdex/chapters/Chapter3_5/Cpu0SEFrameLowering.cpp
// This method is called immediately before PrologEpilogInserter scans the
// physical registers used to determine what callee saved registers should be
// spilled. This method is optional.
void Cpu0SEFrameLowering::determineCalleeSaves(MachineFunction &MF,
BitVector &SavedRegs,
RegScavenger *RS) const {
//@determineCalleeSaves-body
TargetFrameLowering::determineCalleeSaves(MF, SavedRegs, RS);
Cpu0FunctionInfo *Cpu0FI = MF.getInfo<Cpu0FunctionInfo>();
if (MF.getFrameInfo().hasCalls())
setAliasRegs(MF, SavedRegs, Cpu0::LR);
return;
}
The determineCalleeSaves() of Cpu0SEFrameLowering.cpp as above determine the spill registers. Once the spill registers are determined, the function SpillCalleeSavedRegisters() will save/restore registers to/from stack slots via the following code.
lbdex/chapters/Chapter3_5/Cpu0InstrInfo.h
void storeRegToStackSlot(MachineBasicBlock &MBB,
MachineBasicBlock::iterator MBBI,
Register SrcReg, bool isKill, int FrameIndex,
const TargetRegisterClass *RC,
const TargetRegisterInfo *TRI) const override {
storeRegToStack(MBB, MBBI, SrcReg, isKill, FrameIndex, RC, TRI, 0);
}
void loadRegFromStackSlot(MachineBasicBlock &MBB,
MachineBasicBlock::iterator MBBI,
Register DestReg, int FrameIndex,
const TargetRegisterClass *RC,
const TargetRegisterInfo *TRI) const override {
loadRegFromStack(MBB, MBBI, DestReg, FrameIndex, RC, TRI, 0);
}
virtual void storeRegToStack(MachineBasicBlock &MBB,
MachineBasicBlock::iterator MI,
Register SrcReg, bool isKill, int FrameIndex,
const TargetRegisterClass *RC,
const TargetRegisterInfo *TRI,
int64_t Offset) const = 0;
virtual void loadRegFromStack(MachineBasicBlock &MBB,
MachineBasicBlock::iterator MI,
Register DestReg, int FrameIndex,
const TargetRegisterClass *RC,
const TargetRegisterInfo *TRI,
int64_t Offset) const = 0;
MachineMemOperand *GetMemOperand(MachineBasicBlock &MBB, int FI,
MachineMemOperand::Flags Flags) const;
lbdex/chapters/Chapter3_5/Cpu0InstrInfo.cpp
MachineMemOperand *
Cpu0InstrInfo::GetMemOperand(MachineBasicBlock &MBB, int FI,
MachineMemOperand::Flags Flags) const {
MachineFunction &MF = *MBB.getParent();
MachineFrameInfo &MFI = MF.getFrameInfo();
return MF.getMachineMemOperand(MachinePointerInfo::getFixedStack(MF, FI),
Flags, MFI.getObjectSize(FI),
MFI.getObjectAlign(FI));
}
lbdex/chapters/Chapter3_5/Cpu0SEInstrInfo.h
void storeRegToStack(MachineBasicBlock &MBB,
MachineBasicBlock::iterator MI,
Register SrcReg, bool isKill, int FrameIndex,
const TargetRegisterClass *RC,
const TargetRegisterInfo *TRI,
int64_t Offset) const override;
void loadRegFromStack(MachineBasicBlock &MBB,
MachineBasicBlock::iterator MI,
Register DestReg, int FrameIndex,
const TargetRegisterClass *RC,
const TargetRegisterInfo *TRI,
int64_t Offset) const override;
lbdex/chapters/Chapter3_5/Cpu0SEInstrInfo.cpp
void Cpu0SEInstrInfo::
storeRegToStack(MachineBasicBlock &MBB, MachineBasicBlock::iterator I,
Register SrcReg, bool isKill, int FI,
const TargetRegisterClass *RC, const TargetRegisterInfo *TRI,
int64_t Offset) const {
DebugLoc DL;
MachineMemOperand *MMO = GetMemOperand(MBB, FI, MachineMemOperand::MOStore);
unsigned Opc = 0;
Opc = Cpu0::ST;
assert(Opc && "Register class not handled!");
BuildMI(MBB, I, DL, get(Opc)).addReg(SrcReg, getKillRegState(isKill))
.addFrameIndex(FI).addImm(Offset).addMemOperand(MMO);
}
void Cpu0SEInstrInfo::
loadRegFromStack(MachineBasicBlock &MBB, MachineBasicBlock::iterator I,
Register DestReg, int FI, const TargetRegisterClass *RC,
const TargetRegisterInfo *TRI, int64_t Offset) const {
DebugLoc DL;
if (I != MBB.end()) DL = I->getDebugLoc();
MachineMemOperand *MMO = GetMemOperand(MBB, FI, MachineMemOperand::MOLoad);
unsigned Opc = 0;
Opc = Cpu0::LD;
assert(Opc && "Register class not handled!");
BuildMI(MBB, I, DL, get(Opc), DestReg).addFrameIndex(FI).addImm(Offset)
.addMemOperand(MMO);
}
Functions storeRegToStack() Cpu0SEInstrInfo.cpp and storeRegToStackSlot() of Cpu0InstrInfo.cpp handle the registers spilling during register allocation process. Since each local variable connecting to a frame index, code “.addFrameIndex(FI). addImm(Offset).addMemOperand(MMO);” in storeRegToStack() where Offset is 0 is added for each virtual register. The loadRegFromStackSlot() and loadRegFromStack() will be used when it needs reload from stack slot.
If adding V0 to Cpu0CallingConv.td as the following and without both storeRegToStack() and storeRegToStackSlot() in Cpu0SEInstrInfo.cpp, Cpu0SEInstrInfo.h and Cpu0InstrInfo.h it will get the belowing error.
lbdex/Cpu0/Cpu0CallingConv.td
def CSR_O32 : CalleeSavedRegs<(add LR, FP, V0,
(sequence "S%u", 1, 0))>;
114-43-191-19:input Jonathan$ ~/llvm/test/build/bin/llc
-march=cpu0 -relocation-model=pic -filetype=asm ch3.bc -o -
.text
.section .mdebug.abiO32
.previous
.file "ch3.bc"
Target didn't implement TargetInstrInfo::storeRegToStackSlot!
...
Stack dump:
...
Abort trap: 6
Stage |
Function |
---|---|
Prologue/Epilogue Insertion & Frame Finalization |
|
|
|
|
|
|
|
|
|
|
|
File PrologEpilogInserter.cpp includes the calling of backend functions spillCalleeSavedRegisters(), emitProlog(), emitEpilog() and eliminateFrameIndex() as follows,
lib/CodeGen/PrologEpilogInserter.cpp
class PEI : public MachineFunctionPass {
public:
static char ID;
explicit PEI(const TargetMachine *TM = nullptr) : MachineFunctionPass(ID) {
initializePEIPass(*PassRegistry::getPassRegistry());
if (TM && (!TM->usesPhysRegsForPEI())) {
...
} else {
SpillCalleeSavedRegisters = doSpillCalleeSavedRegs;
...
}
}
...
}
/// insertCSRSpillsAndRestores - Insert spill and restore code for
/// callee saved registers used in the function.
///
static void insertCSRSpillsAndRestores(MachineFunction &Fn,
const MBBVector &SaveBlocks,
const MBBVector &RestoreBlocks) {
...
// Spill using target interface.
for (MachineBasicBlock *SaveBlock : SaveBlocks) {
...
if (!TFI->spillCalleeSavedRegisters(*SaveBlock, I, CSI, TRI)) {
for (unsigned i = 0, e = CSI.size(); i != e; ++i) {
// Insert the spill to the stack frame.
...
TII.storeRegToStackSlot(*SaveBlock, I, Reg, true, CSI[i].getFrameIdx(),
RC, TRI);
}
}
...
}
// Restore using target interface.
for (MachineBasicBlock *MBB : RestoreBlocks) {
...
// Restore all registers immediately before the return and any
// terminators that precede it.
if (!TFI->restoreCalleeSavedRegisters(*MBB, I, CSI, TRI)) {
for (unsigned i = 0, e = CSI.size(); i != e; ++i) {
...
TII.loadRegFromStackSlot(*MBB, I, Reg, CSI[i].getFrameIdx(), RC, TRI);
...
}
...
}
...
}
static void doSpillCalleeSavedRegs(MachineFunction &Fn, RegScavenger *RS,
unsigned &MinCSFrameIndex,
unsigned &MaxCSFrameIndex,
const MBBVector &SaveBlocks,
const MBBVector &RestoreBlocks) {
const Function *F = Fn.getFunction();
const TargetFrameLowering *TFI = Fn.getSubtarget().getFrameLowering();
MinCSFrameIndex = std::numeric_limits<unsigned>::max();
MaxCSFrameIndex = 0;
// Determine which of the registers in the callee save list should be saved.
BitVector SavedRegs;
TFI->determineCalleeSaves(Fn, SavedRegs, RS);
// Assign stack slots for any callee-saved registers that must be spilled.
assignCalleeSavedSpillSlots(Fn, SavedRegs, MinCSFrameIndex, MaxCSFrameIndex);
// Add the code to save and restore the callee saved registers.
if (!F->hasFnAttribute(Attribute::Naked))
insertCSRSpillsAndRestores(Fn, SaveBlocks, RestoreBlocks);
}
void PEI::insertPrologEpilogCode(MachineFunction &Fn) {
const TargetFrameLowering &TFI = *Fn.getSubtarget().getFrameLowering();
// Add prologue to the function...
for (MachineBasicBlock *SaveBlock : SaveBlocks)
TFI.emitPrologue(Fn, *SaveBlock);
// Add epilogue to restore the callee-save registers in each exiting block.
for (MachineBasicBlock *RestoreBlock : RestoreBlocks)
TFI.emitEpilogue(Fn, *RestoreBlock);
...
}
void PEI::replaceFrameIndices(MachineBasicBlock *BB, MachineFunction &Fn,
int &SPAdj) {
...
for (unsigned i = 0, e = MI.getNumOperands(); i != e; ++i) {
...
// If this instruction has a FrameIndex operand, we need to
// use that target machine register info object to eliminate
// it.
TRI.eliminateFrameIndex(MI, SPAdj, i,
FrameIndexVirtualScavenging ? nullptr : RS);
...
}
...
}
/// replaceFrameIndices - Replace all MO_FrameIndex operands with physical
/// register references and actual offsets.
///
void PEI::replaceFrameIndices(MachineFunction &Fn) {
...
// Iterate over the reachable blocks in DFS order.
for (auto DFI = df_ext_begin(&Fn, Reachable), DFE = df_ext_end(&Fn, Reachable);
DFI != DFE; ++DFI) {
...
replaceFrameIndices(BB, Fn, SPAdj);
...
}
// Handle the unreachable blocks.
for (auto &BB : Fn) {
...
replaceFrameIndices(&BB, Fn, SPAdj);
}
}
bool PEI::runOnMachineFunction(MachineFunction &Fn) {
const TargetFrameLowering &TFI = *Fn.getSubtarget().getFrameLowering();
...
FrameIndexVirtualScavenging = TRI->requiresFrameIndexScavenging(Fn);
...
// Handle CSR spilling and restoring, for targets that need it.
SpillCalleeSavedRegisters(Fn, RS, MinCSFrameIndex, MaxCSFrameIndex,
SaveBlocks, RestoreBlocks);
...
// Calculate actual frame offsets for all abstract stack objects...
calculateFrameObjectOffsets(Fn);
// Add prolog and epilog code to the function. This function is required
// to align the stack frame as necessary for any stack variables or
// called functions. Because of this, calculateCalleeSavedRegisters()
// must be called before this function in order to set the AdjustsStack
// and MaxCallFrameSize variables.
if (!F->hasFnAttribute(Attribute::Naked))
insertPrologEpilogCode(Fn);
// Replace all MO_FrameIndex operands with physical register references
// and actual offsets.
//
replaceFrameIndices(Fn);
// If register scavenging is needed, as we've enabled doing it as a
// post-pass, scavenge the virtual registers that frame index elimination
// inserted.
if (TRI->requiresRegisterScavenging(Fn) && FrameIndexVirtualScavenging) {
ScavengeFrameVirtualRegs(Fn, RS);
// Clear any vregs created by virtual scavenging.
Fn.getRegInfo().clearVirtRegs();
}
...
}
Large stack¶
At this point, we have translated the very simple main() function with “return 0;” single instruction. Though Cpu0AnalyzeImmediate.cpp and the Cpu0InstrInfo.td instructions defined in Chapter3_5 as the following, which take care the 32 bits stack size adjustments. However the CH9_3, dynamic-stack-allocation-support bring instruction “move $fp, $sp” that make it complicated in coding against the tutoral purpose of Cpu0. [4]
lbdex/chapters/Chapter3_5/CMakeLists.txt
add_llvm_target(
...
Cpu0AnalyzeImmediate.cpp
...
)
lbdex/chapters/Chapter3_5/Cpu0AnalyzeImmediate.h
//===-- Cpu0AnalyzeImmediate.h - Analyze Immediates ------------*- C++ -*--===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef CPU0_ANALYZE_IMMEDIATE_H
#define CPU0_ANALYZE_IMMEDIATE_H
#include "Cpu0Config.h"
#if CH >= CH3_5
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/DataTypes.h"
namespace llvm {
class Cpu0AnalyzeImmediate {
public:
struct Inst {
unsigned Opc, ImmOpnd;
Inst(unsigned Opc, unsigned ImmOpnd);
};
typedef SmallVector<Inst, 7 > InstSeq;
/// Analyze - Get an instruction sequence to load immediate Imm. The last
/// instruction in the sequence must be an ADDiu if LastInstrIsADDiu is
/// true;
const InstSeq &Analyze(uint64_t Imm, unsigned Size, bool LastInstrIsADDiu);
private:
typedef SmallVector<InstSeq, 5> InstSeqLs;
/// AddInstr - Add I to all instruction sequences in SeqLs.
void AddInstr(InstSeqLs &SeqLs, const Inst &I);
/// GetInstSeqLsADDiu - Get instruction sequences which end with an ADDiu to
/// load immediate Imm
void GetInstSeqLsADDiu(uint64_t Imm, unsigned RemSize, InstSeqLs &SeqLs);
/// GetInstSeqLsORi - Get instruction sequences which end with an ORi to
/// load immediate Imm
void GetInstSeqLsORi(uint64_t Imm, unsigned RemSize, InstSeqLs &SeqLs);
/// GetInstSeqLsSHL - Get instruction sequences which end with a SHL to
/// load immediate Imm
void GetInstSeqLsSHL(uint64_t Imm, unsigned RemSize, InstSeqLs &SeqLs);
/// GetInstSeqLs - Get instruction sequences to load immediate Imm.
void GetInstSeqLs(uint64_t Imm, unsigned RemSize, InstSeqLs &SeqLs);
/// ReplaceADDiuSHLWithLUi - Replace an ADDiu & SHL pair with a LUi.
void ReplaceADDiuSHLWithLUi(InstSeq &Seq);
/// GetShortestSeq - Find the shortest instruction sequence in SeqLs and
/// return it in Insts.
void GetShortestSeq(InstSeqLs &SeqLs, InstSeq &Insts);
unsigned Size;
unsigned ADDiu, ORi, SHL, LUi;
InstSeq Insts;
};
}
#endif // #if CH >= CH3_5
#endif
lbdex/chapters/Chapter3_5/Cpu0AnalyzeImmediate.cpp
//===-- Cpu0AnalyzeImmediate.cpp - Analyze Immediates ---------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "Cpu0AnalyzeImmediate.h"
#include "Cpu0.h"
#if CH >= CH3_5
#include "llvm/Support/MathExtras.h"
using namespace llvm;
Cpu0AnalyzeImmediate::Inst::Inst(unsigned O, unsigned I) : Opc(O), ImmOpnd(I) {}
// Add I to the instruction sequences.
void Cpu0AnalyzeImmediate::AddInstr(InstSeqLs &SeqLs, const Inst &I) {
// Add an instruction seqeunce consisting of just I.
if (SeqLs.empty()) {
SeqLs.push_back(InstSeq(1, I));
return;
}
for (InstSeqLs::iterator Iter = SeqLs.begin(); Iter != SeqLs.end(); ++Iter)
Iter->push_back(I);
}
void Cpu0AnalyzeImmediate::GetInstSeqLsADDiu(uint64_t Imm, unsigned RemSize,
InstSeqLs &SeqLs) {
GetInstSeqLs((Imm + 0x8000ULL) & 0xffffffffffff0000ULL, RemSize, SeqLs);
AddInstr(SeqLs, Inst(ADDiu, Imm & 0xffffULL));
}
void Cpu0AnalyzeImmediate::GetInstSeqLsORi(uint64_t Imm, unsigned RemSize,
InstSeqLs &SeqLs) {
GetInstSeqLs(Imm & 0xffffffffffff0000ULL, RemSize, SeqLs);
AddInstr(SeqLs, Inst(ORi, Imm & 0xffffULL));
}
void Cpu0AnalyzeImmediate::GetInstSeqLsSHL(uint64_t Imm, unsigned RemSize,
InstSeqLs &SeqLs) {
unsigned Shamt = countTrailingZeros(Imm);
GetInstSeqLs(Imm >> Shamt, RemSize - Shamt, SeqLs);
AddInstr(SeqLs, Inst(SHL, Shamt));
}
void Cpu0AnalyzeImmediate::GetInstSeqLs(uint64_t Imm, unsigned RemSize,
InstSeqLs &SeqLs) {
uint64_t MaskedImm = Imm & (0xffffffffffffffffULL >> (64 - Size));
// Do nothing if Imm is 0.
if (!MaskedImm)
return;
// A single ADDiu will do if RemSize <= 16.
if (RemSize <= 16) {
AddInstr(SeqLs, Inst(ADDiu, MaskedImm));
return;
}
// Shift if the lower 16-bit is cleared.
if (!(Imm & 0xffff)) {
GetInstSeqLsSHL(Imm, RemSize, SeqLs);
return;
}
GetInstSeqLsADDiu(Imm, RemSize, SeqLs);
// If bit 15 is cleared, it doesn't make a difference whether the last
// instruction is an ADDiu or ORi. In that case, do not call GetInstSeqLsORi.
if (Imm & 0x8000) {
InstSeqLs SeqLsORi;
GetInstSeqLsORi(Imm, RemSize, SeqLsORi);
SeqLs.insert(SeqLs.end(), SeqLsORi.begin(), SeqLsORi.end());
}
}
// Replace a ADDiu & SHL pair with a LUi.
// e.g. the following two instructions
// ADDiu 0x0111
// SHL 18
// are replaced with
// LUi 0x444
void Cpu0AnalyzeImmediate::ReplaceADDiuSHLWithLUi(InstSeq &Seq) {
// Check if the first two instructions are ADDiu and SHL and the shift amount
// is at least 16.
if ((Seq.size() < 2) || (Seq[0].Opc != ADDiu) ||
(Seq[1].Opc != SHL) || (Seq[1].ImmOpnd < 16))
return;
// Sign-extend and shift operand of ADDiu and see if it still fits in 16-bit.
int64_t Imm = SignExtend64<16>(Seq[0].ImmOpnd);
int64_t ShiftedImm = (uint64_t)Imm << (Seq[1].ImmOpnd - 16);
if (!isInt<16>(ShiftedImm))
return;
// Replace the first instruction and erase the second.
Seq[0].Opc = LUi;
Seq[0].ImmOpnd = (unsigned)(ShiftedImm & 0xffff);
Seq.erase(Seq.begin() + 1);
}
void Cpu0AnalyzeImmediate::GetShortestSeq(InstSeqLs &SeqLs, InstSeq &Insts) {
InstSeqLs::iterator ShortestSeq = SeqLs.end();
// The length of an instruction sequence is at most 7.
unsigned ShortestLength = 8;
for (InstSeqLs::iterator S = SeqLs.begin(); S != SeqLs.end(); ++S) {
ReplaceADDiuSHLWithLUi(*S);
assert(S->size() <= 7);
if (S->size() < ShortestLength) {
ShortestSeq = S;
ShortestLength = S->size();
}
}
Insts.clear();
Insts.append(ShortestSeq->begin(), ShortestSeq->end());
}
const Cpu0AnalyzeImmediate::InstSeq
&Cpu0AnalyzeImmediate::Analyze(uint64_t Imm, unsigned Size,
bool LastInstrIsADDiu) {
this->Size = Size;
ADDiu = Cpu0::ADDiu;
ORi = Cpu0::ORi;
SHL = Cpu0::SHL;
LUi = Cpu0::LUi;
InstSeqLs SeqLs;
// Get the list of instruction sequences.
if (LastInstrIsADDiu | !Imm)
GetInstSeqLsADDiu(Imm, Size, SeqLs);
else
GetInstSeqLs(Imm, Size, SeqLs);
// Set Insts to the shortest instruction sequence.
GetShortestSeq(SeqLs, Insts);
return Insts;
}
#endif
lbdex/chapters/Chapter3_5/Cpu0InstrInfo.h
#include "Cpu0AnalyzeImmediate.h"
lbdex/chapters/Chapter3_5/Cpu0InstrInfo.td
class Cpu0InstAlias<string Asm, dag Result, bit Emit = 0b1> :
InstAlias<Asm, Result, Emit>;
def shamt : Operand<i32>;
// Unsigned Operand
def uimm16 : Operand<i32> {
let PrintMethod = "printUnsignedImm";
}
// Transformation Function - get the lower 16 bits.
def LO16 : SDNodeXForm<imm, [{
return getImm(N, N->getZExtValue() & 0xffff);
}]>;
// Transformation Function - get the higher 16 bits.
def HI16 : SDNodeXForm<imm, [{
return getImm(N, (N->getZExtValue() >> 16) & 0xffff);
}]>;
// Node immediate fits as 16-bit zero extended on target immediate.
// The LO16 param means that only the lower 16 bits of the node
// immediate are caught.
// e.g. addiu, sltiu
def immZExt16 : PatLeaf<(imm), [{
if (N->getValueType(0) == MVT::i32)
return (uint32_t)N->getZExtValue() == (unsigned short)N->getZExtValue();
else
return (uint64_t)N->getZExtValue() == (unsigned short)N->getZExtValue();
}], LO16>;
// Immediate can be loaded with LUi (32-bit int with lower 16-bit cleared).
def immLow16Zero : PatLeaf<(imm), [{
int64_t Val = N->getSExtValue();
return isInt<32>(Val) && !(Val & 0xffff);
}]>;
// shamt field must fit in 5 bits.
def immZExt5 : ImmLeaf<i32, [{return Imm == (Imm & 0x1f);}]>;
let Predicates = [Ch3_5] in {
// Arithmetic and logical instructions with 3 register operands.
class ArithLogicR<bits<8> op, string instr_asm, SDNode OpNode,
InstrItinClass itin, RegisterClass RC, bit isComm = 0>:
FA<op, (outs GPROut:$ra), (ins RC:$rb, RC:$rc),
!strconcat(instr_asm, "\t$ra, $rb, $rc"),
[(set GPROut:$ra, (OpNode RC:$rb, RC:$rc))], itin> {
let shamt = 0;
let isCommutable = isComm; // e.g. add rb rc = add rc rb
let isReMaterializable = 1;
}
}
let Predicates = [Ch3_5] in {
// Shifts
class shift_rotate_imm<bits<8> op, bits<4> isRotate, string instr_asm,
SDNode OpNode, PatFrag PF, Operand ImmOpnd,
RegisterClass RC>:
FA<op, (outs GPROut:$ra), (ins RC:$rb, ImmOpnd:$shamt),
!strconcat(instr_asm, "\t$ra, $rb, $shamt"),
[(set GPROut:$ra, (OpNode RC:$rb, PF:$shamt))], IIAlu> {
let rc = 0;
}
// 32-bit shift instructions.
class shift_rotate_imm32<bits<8> op, bits<4> isRotate, string instr_asm,
SDNode OpNode>:
shift_rotate_imm<op, isRotate, instr_asm, OpNode, immZExt5, shamt, CPURegs>;
}
let Predicates = [Ch3_5] in {
// Load Upper Imediate
class LoadUpper<bits<8> op, string instr_asm, RegisterClass RC, Operand Imm>:
FL<op, (outs RC:$ra), (ins Imm:$imm16),
!strconcat(instr_asm, "\t$ra, $imm16"), [], IIAlu> {
let rb = 0;
let isReMaterializable = 1;
}
}
let Predicates = [Ch3_5] in {
def ORi : ArithLogicI<0x0d, "ori", or, uimm16, immZExt16, CPURegs>;
}
let Predicates = [Ch3_5] in {
def LUi : LoadUpper<0x0F, "lui", GPROut, uimm16>;
}
let Predicates = [Ch3_5] in {
let Predicates = [DisableOverflow] in {
def ADDu : ArithLogicR<0x11, "addu", add, IIAlu, CPURegs, 1>;
}
}
let Predicates = [Ch3_5] in {
def SHL : shift_rotate_imm32<0x1e, 0x00, "shl", shl>;
}
let Predicates = [Ch3_5] in {
//===----------------------------------------------------------------------===//
// Instruction aliases
//===----------------------------------------------------------------------===//
def : Cpu0InstAlias<"move $dst, $src",
(ADDu GPROut:$dst, GPROut:$src,ZERO), 1>;
}
let Predicates = [Ch3_5] in {
def : Pat<(i32 immZExt16:$in),
(ORi ZERO, imm:$in)>;
def : Pat<(i32 immLow16Zero:$in),
(LUi (HI16 imm:$in))>;
// Arbitrary immediates
def : Pat<(i32 imm:$imm),
(ORi (LUi (HI16 imm:$imm)), (LO16 imm:$imm))>;
} // let Predicates = [Ch3_4]
The Cpu0AnalyzeImmediate.cpp written in recursive with a little complicate in logic. However, the recursive skills is used in the front end compile book, you should fimiliar with it. Instead of tracking the code, listing the stack size and the instructions generated in “Table: Cpu0 stack adjustment instructions before replace addiu and shl with lui instruction” as follows and “Table: Cpu0 stack adjustment instructions after replace addiu and shl with lui instruction” at next,
stack size range |
ex. stack size |
Cpu0 Prologue instructions |
Cpu0 Epilogue instructions |
---|---|---|---|
0 ~ 0x7ff8 |
|
|
|
0x8000 ~ 0xfff8 |
|
|
|
x10000 ~ 0xfffffff8 |
|
|
|
x10000 ~ 0xfffffff8 |
|
|
|
Since the Cpu0 stack is 8 bytes alignment, addresses from 0x7ff9 to 0x7fff are impossible existing.
Assume sp = 0xa0008000 and stack size = 0x90008000, then (0xa0008000 - 0x90008000) => 0x10000000. Verify with the Cpu0 Prologue instructions as follows,
“addiu $1, $zero, -9” => ($1 = 0 + 0xfffffff7) => $1 = 0xfffffff7.
“shl $1, $1, 28;” => $1 = 0x70000000.
“addiu $1, $1, -32768” => $1 = (0x70000000 + 0xffff8000) => $1 = 0x6fff8000.
“addu $sp, $sp, $1” => $sp = (0xa0008000 + 0x6fff8000) => $sp = 0x10000000.
Verify with the Cpu0 Epilogue instructions with sp = 0x10000000 and stack size = 0x90008000 as follows,
“addiu $1, $zero, -28671” => ($1 = 0 + 0xffff9001) => $1 = 0xffff9001.
“shl $1, $1, 16;” => $1 = 0x90010000.
“addiu $1, $1, -32768” => $1 = (0x90010000 + 0xffff8000) => $1 = 0x90008000.
“addu $sp, $sp, $1” => $sp = (0x10000000 + 0x90008000) => $sp = 0xa0008000.
The Cpu0AnalyzeImmediate::GetShortestSeq() will call Cpu0AnalyzeImmediate:: ReplaceADDiuSHLWithLUi() to replace addiu and shl with single instruction lui only. The effect as the following table.
stack size range |
ex. stack size |
Cpu0 Prologue instructions |
Cpu0 Epilogue instructions |
---|---|---|---|
0x8000 ~ 0xfff8 |
|
|
|
x10000 ~ 0xfffffff8 |
|
|
|
x10000 ~ 0xfffffff8 |
|
|
|
Assume sp = 0xa0008000 and stack size = 0x90008000, then (0xa0008000 - 0x90008000) => 0x10000000. Verify with the Cpu0 Prologue instructions as follows,
“lui $1, 28671” => $1 = 0x6fff0000.
“ori $1, $1, 32768” => $1 = (0x6fff0000 + 0x00008000) => $1 = 0x6fff8000.
“addu $sp, $sp, $1” => $sp = (0xa0008000 + 0x6fff8000) => $sp = 0x10000000.
Verify with the Cpu0 Epilogue instructions with sp = 0x10000000 and stack size = 0x90008000 as follows,
“lui $1, 36865” => $1 = 0x90010000.
“addiu $1, $1, -32768” => $1 = (0x90010000 + 0xffff8000) => $1 = 0x90008000.
“addu $sp, $sp, $1” => $sp = (0x10000000 + 0x90008000) => $sp = 0xa0008000.
File ch3_largeframe.cpp include the large frame test.
Run Chapter3_5 with ch3_largeframe.cpp will get the following result.
lbdex/input/ch3_largeframe.cpp
int test_largegframe() {
int a[469753856];
return 0;
}
118-165-78-12:input Jonathan$ clang -target mips-unknown-linux-gnu -c
ch3_largeframe.cpp -emit-llvm -o ch3_largeframe.bc
118-165-78-12:input Jonathan$ /Users/Jonathan/llvm/test/build/
bin/llc -march=cpu0 -relocation-model=pic -filetype=asm
ch3_largeframe.bc.bc -o -
...
.section .mdebug.abiO32
.previous
.file "ch3_largeframe.bc"
.globl _Z16test_largegframev
.align 2
.type _Z16test_largegframev,@function
.ent _Z16test_largegframev # @_Z16test_largegframev
_Z16test_largegframev:
.frame $fp,1879015424,$lr
.mask 0x00000000,0
.set noreorder
.set nomacro
.set noat
# BB#0:
lui $1, 36865
addiu $1, $1, -32768
addu $sp, $sp, $1
addiu $2, $zero, 0
lui $1, 28672
addiu $1, $1, -32768
addu $sp, $sp, $1
ret $lr
.set at
.set macro
.set reorder
.end _Z16test_largegframev
$func_end0:
.size _Z16test_largegframev, ($func_end0)-_Z16test_largegframev
Data operands DAGs¶
From above or compiler book, you can see all the OP code are the internal nodes in DAGs graph, and operands are the leaves of DAGs. To develop your backend, you can copy the related data operands DAGs node from other backend since the IR data nodes are take cared by all the backend. About the data DAGs nodes, you can understand some of them through the Cpu0InstrInfo.td and find them by command, grep -R “<datadag>” `find llvm/include/llvm`, with spending a little more time to think or guess about it. Some data DAGs we know more, some we know a little and some remains unknown but it’s OK for us. List some of data DAGs we understand and occured until now as follows,
include/llvm/Target/TargetSelectionDAG.td
// PatLeaf's are pattern fragments that have no operands. This is just a helper
// to define immediates and other common things concisely.
class PatLeaf<dag frag, code pred = [{}], SDNodeXForm xform = NOOP_SDNodeXForm>
: PatFrag<(ops), frag, pred, xform>;
// ImmLeaf is a pattern fragment with a constraint on the immediate. The
// constraint is a function that is run on the immediate (always with the value
// sign extended out to an int64_t) as Imm. For example:
//
// def immSExt8 : ImmLeaf<i16, [{ return (char)Imm == Imm; }]>;
//
// this is a more convenient form to match 'imm' nodes in than PatLeaf and also
// is preferred over using PatLeaf because it allows the code generator to
// reason more about the constraint.
//
// If FastIsel should ignore all instructions that have an operand of this type,
// the FastIselShouldIgnore flag can be set. This is an optimization to reduce
// the code size of the generated fast instruction selector.
class ImmLeaf<ValueType vt, code pred, SDNodeXForm xform = NOOP_SDNodeXForm>
: PatFrag<(ops), (vt imm), [{}], xform> {
let ImmediateCode = pred;
bit FastIselShouldIgnore = 0;
}
lbdex/chapters/Chapter3_5/Cpu0InstrInfo.td
// Signed Operand
def simm16 : Operand<i32> {
let DecoderMethod= "DecodeSimm16";
}
def shamt : Operand<i32>;
// Unsigned Operand
def uimm16 : Operand<i32> {
let PrintMethod = "printUnsignedImm";
}
// Address operand
def mem : Operand<iPTR> {
let PrintMethod = "printMemOperand";
let MIOperandInfo = (ops GPROut, simm16);
let EncoderMethod = "getMemEncoding";
}
// Transformation Function - get the lower 16 bits.
def LO16 : SDNodeXForm<imm, [{
return getImm(N, N->getZExtValue() & 0xffff);
}]>;
// Transformation Function - get the higher 16 bits.
def HI16 : SDNodeXForm<imm, [{
return getImm(N, (N->getZExtValue() >> 16) & 0xffff);
}]>;
// Node immediate fits as 16-bit sign extended on target immediate.
// e.g. addi, andi
def immSExt16 : PatLeaf<(imm), [{ return isInt<16>(N->getSExtValue()); }]>;
// Node immediate fits as 16-bit zero extended on target immediate.
// The LO16 param means that only the lower 16 bits of the node
// immediate are caught.
// e.g. addiu, sltiu
def immZExt16 : PatLeaf<(imm), [{
if (N->getValueType(0) == MVT::i32)
return (uint32_t)N->getZExtValue() == (unsigned short)N->getZExtValue();
else
return (uint64_t)N->getZExtValue() == (unsigned short)N->getZExtValue();
}], LO16>;
// Immediate can be loaded with LUi (32-bit int with lower 16-bit cleared).
def immLow16Zero : PatLeaf<(imm), [{
int64_t Val = N->getSExtValue();
return isInt<32>(Val) && !(Val & 0xffff);
}]>;
// shamt field must fit in 5 bits.
def immZExt5 : ImmLeaf<i32, [{return Imm == (Imm & 0x1f);}]>;
// Cpu0 Address Mode! SDNode frameindex could possibily be a match
// since load and store instructions from stack used it.
def addr :
ComplexPattern<iPTR, 2, "SelectAddr", [frameindex], [SDNPWantParent]>;
//===----------------------------------------------------------------------===//
// Pattern fragment for load/store
//===----------------------------------------------------------------------===//
class AlignedLoad<PatFrag Node> :
PatFrag<(ops node:$ptr), (Node node:$ptr), [{
LoadSDNode *LD = cast<LoadSDNode>(N);
return LD->getMemoryVT().getSizeInBits()/8 <= LD->getAlignment();
}]>;
class AlignedStore<PatFrag Node> :
PatFrag<(ops node:$val, node:$ptr), (Node node:$val, node:$ptr), [{
StoreSDNode *SD = cast<StoreSDNode>(N);
return SD->getMemoryVT().getSizeInBits()/8 <= SD->getAlignment();
}]>;
def load_a : AlignedLoad<load>;
def store_a : AlignedStore<store>;
As mentioned in sub-section “instruction selection” of last chapter, immSExt16 is a data leaf DAG node and it will return true if its value is in the range of signed 16 bits integer. The load_a, store_a and others are similar but they check with alignment.
The mem is explained in chapter3_2 for print operand; addr is explained in chapter3_3 for data DAG selection. The simm16, …, inherited from Operand<i32> because Cpu0 is 32 bits. It may over 16 bits, so immSExt16 pattern leaf is used to control it as example ADDiu mention in last chapter. PatLeaf immZExt16, immLow16Zero and ImmLeaf immZExt5 are similar to immSExt16.
Summary of this Chapter¶
Summary the functions for llvm backend stages as the following table.
118-165-79-200:input Jonathan$ /Users/Jonathan/llvm/test/build/
bin/llc -march=cpu0 -relocation-model=pic -filetype=asm ch3.bc
-debug-pass=Structure -o -
...
Machine Branch Probability Analysis
ModulePass Manager
FunctionPass Manager
...
CPU0 DAG->DAG Pattern Instruction Selection
Initial selection DAG
Optimized lowered selection DAG
Type-legalized selection DAG
Optimized type-legalized selection DAG
Legalized selection DAG
Optimized legalized selection DAG
Instruction selection
Selected selection DAG
Scheduling
...
Greedy Register Allocator
...
Prologue/Epilogue Insertion & Frame Finalization
...
Post-RA pseudo instruction expansion pass
...
Cpu0 Assembly Printer
Stage |
Function |
---|---|
Before CPU0 DAG->DAG Pattern Instruction Selection |
|
Instruction selection |
|
Prologue/Epilogue Insertion & Frame Finalization |
|
|
|
|
|
|
|
|
|
|
|
Post-RA pseudo instruction expansion pass |
|
Cpu0 Assembly Printer |
|
We add a pass in Instruction Section stage in section “Add Cpu0DAGToDAGISel
class”. You can embed your code into other passes like that. Please check
CodeGen/Passes.h for the information. Remember the pass is called according
the function unit as the llc -debug-pass=Structure
indicated.
We have finished a simple compiler for cpu0 which only supports ld, st, addiu, ori, lui, addu, shl and ret 8 instructions.
We are satisfied with this result.
But you may think “After so much code we program, and just get these 8
instructions!”.
The point is we have created a frame work for Cpu0 target machine (please
look back the llvm backend structure class inheritance tree early in this
chapter).
Until now, we have over 3000 lines of source code with comments which include
files *.cpp, *.h, *.td and CMakeLists.txt.
It can be counted by command wc `find dir -name *.cpp`
for files *.cpp,
*.h, *.td, *.txt.
LLVM front end tutorial have 700 lines of source code without comments in total.
Don’t feel down with this result.
In reality, writing a backend is warm up slowly but run fastly.
Clang has over 500,000 lines of source code with comments in clang/lib
directory which include C++ and Obj C support.
Mips backend of llvm 3.1 has only 15,000 lines with comments.
Even the complicate X86 CPU which CISC outside and RISC inside (micro
instruction), has only 45,000 lines in llvm 3.1 with comments.
In next chapter, we will show you that add a new instruction support is as easy
as 123.