diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..4585380 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,37 @@ +set(LLVM_TARGET_DEFINITIONS Epiphany.td) + +#tablegen(LLVM EpiphanyGenAsmMatcher.inc -gen-asm-matcher) +tablegen(LLVM EpiphanyGenAsmWriter.inc -gen-asm-writer) +tablegen(LLVM EpiphanyGenCallingConv.inc -gen-callingconv) +#tablegen(LLVM EpiphanyGenDisassemblerTables.inc -gen-disassembler) +tablegen(LLVM EpiphanyGenInstrInfo.inc -gen-instr-info) +#tablegen(LLVM EpiphanyGenMCCodeEmitter.inc -gen-emitter -mc-emitter) +tablegen(LLVM EpiphanyGenMCPseudoLowering.inc -gen-pseudo-lowering) +tablegen(LLVM EpiphanyGenRegisterInfo.inc -gen-register-info) +tablegen(LLVM EpiphanyGenDAGISel.inc -gen-dag-isel) +tablegen(LLVM EpiphanyGenSubtargetInfo.inc -gen-subtarget) +add_public_tablegen_target(EpiphanyCommonTableGen) + +add_llvm_target(EpiphanyCodeGen + EpiphanyAsmPrinter.cpp + EpiphanyFrameLowering.cpp + EpiphanyISelDAGToDAG.cpp + EpiphanyISelLowering.cpp + EpiphanyInstrInfo.cpp + EpiphanyMachineFunctionInfo.cpp + EpiphanyMCInstLower.cpp + EpiphanyRegisterInfo.cpp + EpiphanySelectionDAGInfo.cpp + EpiphanySubtarget.cpp + EpiphanyTargetMachine.cpp + EpiphanyTargetObjectFile.cpp + EpiphanyLSOptPass.cpp + CondMovPass.cpp + ) + +#add_subdirectory(AsmParser) +#add_subdirectory(Disassembler) +add_subdirectory(InstPrinter) +add_subdirectory(MCTargetDesc) +add_subdirectory(TargetInfo) +add_subdirectory(Utils) \ No newline at end of file diff --git a/CondMovPass.cpp b/CondMovPass.cpp new file mode 100644 index 0000000..d071ac1 --- /dev/null +++ b/CondMovPass.cpp @@ -0,0 +1,81 @@ +//===-- EpiphanyCondMovPass.cpp -----------------------===// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "epiphany_condmovpass" +#include "Epiphany.h" +#include "EpiphanyMachineFunctionInfo.h" +#include "EpiphanySubtarget.h" +#include "EpiphanyTargetMachine.h" +#include "llvm/CodeGen/MachineDominators.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineLoopInfo.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Target/TargetInstrInfo.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Target/TargetRegisterInfo.h" + +using namespace llvm; + +namespace { + +class EpiphanyCondMovPass : public MachineFunctionPass { + +private: + EpiphanyTargetMachine& QTM; + + public: + static char ID; + EpiphanyCondMovPass(EpiphanyTargetMachine& TM) : MachineFunctionPass(ID), QTM(TM) {} + + const char *getPassName() const { + return "Epiphany movCC cleanup"; + } + bool runOnMachineFunction(MachineFunction &Fn); +}; + +char EpiphanyCondMovPass::ID = 0; + +bool EpiphanyCondMovPass::runOnMachineFunction(MachineFunction &Fn) { + bool modified = false; + // Loop over all of the basic blocks. + for(MachineFunction::iterator MBBb = Fn.begin(), MBBe = Fn.end(); MBBb != MBBe; ++MBBb) { + MachineBasicBlock* MBB = MBBb; + + std::vector killmeplease; + // Loop over all instructions. + for(MachineBasicBlock::iterator MII = MBB->begin(), E = MBB->end(); MII != E; ++MII){ + MachineInstr *MI = &*MII; + if((MI->getOpcode() == Epiphany::MOVww || MI->getOpcode() == Epiphany::MOVss) && MI->getOperand(0).isReg() && MI->getOperand(1).isReg() && MI->getOperand(0).getReg() == MI->getOperand(1).getReg()) + killmeplease.push_back(MI); + } + + if(!killmeplease.empty()){ + modified = true; + for (std::vector::iterator kb = killmeplease.begin(), ke = killmeplease.end(); kb != ke ; kb++) + (*kb)->eraseFromParent(); // we can safely do this because mov Rd, Rd is a nop with no side effects. + } + + } + return modified; +} + +}// namespace + + +//===----------------------------------------------------------------------===// +// Public Constructor Functions +//===----------------------------------------------------------------------===// + +FunctionPass *llvm::createEpiphanyCondMovPass(EpiphanyTargetMachine &TM) { + return new EpiphanyCondMovPass(TM); +} diff --git a/Epiphany.h b/Epiphany.h new file mode 100644 index 0000000..be352de --- /dev/null +++ b/Epiphany.h @@ -0,0 +1,42 @@ +//==-- Epiphany.h - Top-level interface for Epiphany representation -*- 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 entry points for global functions defined in the LLVM +// Epiphany back-end. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TARGET_EPIPHANY_H +#define LLVM_TARGET_EPIPHANY_H + +#include "MCTargetDesc/EpiphanyMCTargetDesc.h" +#include "llvm/Target/TargetMachine.h" + +namespace llvm { + +class EpiphanyAsmPrinter; +class FunctionPass; +class EpiphanyTargetMachine; +class MachineInstr; +class MCInst; + +FunctionPass *createEpiphanyISelDAG(EpiphanyTargetMachine &TM, + CodeGenOpt::Level OptLevel); + +FunctionPass *createEpiphanyCondMovPass(EpiphanyTargetMachine &TM); + +FunctionPass *createEpiphanyLSOptPass(); + +void LowerEpiphanyMachineInstrToMCInst(const MachineInstr *MI, MCInst &OutMI, + EpiphanyAsmPrinter &AP); + + +} + +#endif diff --git a/Epiphany.td b/Epiphany.td new file mode 100644 index 0000000..1a7a13d --- /dev/null +++ b/Epiphany.td @@ -0,0 +1,60 @@ +//===- Epiphany.td - Describe the Epiphany Target Machine -------*- tblgen -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This is the top level entry point for the Epiphany target. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Target-independent interfaces +//===----------------------------------------------------------------------===// + +include "llvm/Target/Target.td" + +//===----------------------------------------------------------------------===// +// Epiphany Processors +// + +include "EpiphanySchedule.td" + +def : Processor<"generic", GenericItineraries, []>; + +//===----------------------------------------------------------------------===// +// Register File Description +//===----------------------------------------------------------------------===// + +include "EpiphanyRegisterInfo.td" + +include "EpiphanyCallingConv.td" + +//===----------------------------------------------------------------------===// +// Instruction Descriptions +//===----------------------------------------------------------------------===// + +include "EpiphanyInstrInfo.td" + +def EpiphanyInstrInfo : InstrInfo; + +//===----------------------------------------------------------------------===// +// Assembly printer +//===----------------------------------------------------------------------===// + +def A64InstPrinter : AsmWriter { + string AsmWriterClassName = "InstPrinter"; + bit isMCAsmWriter = 1; +} + +//===----------------------------------------------------------------------===// +// Declare the target which we are implementing +//===----------------------------------------------------------------------===// + +def Epiphany : Target { + let InstructionSet = EpiphanyInstrInfo; + let AssemblyWriters = [A64InstPrinter]; +} diff --git a/EpiphanyAsmPrinter.cpp b/EpiphanyAsmPrinter.cpp new file mode 100644 index 0000000..5c00eea --- /dev/null +++ b/EpiphanyAsmPrinter.cpp @@ -0,0 +1,333 @@ +//===-- EpiphanyAsmPrinter.cpp - Print machine code to an Epiphany .s file --===// +// +// 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 Epiphany assembly language. +// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "asm-printer" +#include "EpiphanyAsmPrinter.h" +#include "InstPrinter/EpiphanyInstPrinter.h" +#include "llvm/DebugInfo.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/CodeGen/MachineModuleInfoImpls.h" +#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCSymbol.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Target/Mangler.h" + +using namespace llvm; + +MachineLocation +EpiphanyAsmPrinter::getDebugValueLocation(const MachineInstr *MI) const { + // See emitFrameIndexDebugValue in InstrInfo for where this instruction is + // expected to be created. + assert(MI->getNumOperands() == 4 && MI->getOperand(0).isReg() + && MI->getOperand(1).isImm() && "unexpected custom DBG_VALUE"); + return MachineLocation(MI->getOperand(0).getReg(), + MI->getOperand(1).getImm()); +} + +/// Try to print a floating-point register as if it belonged to a specified +/// register-class. For example the inline asm operand modifier "b" requires its +/// argument to be printed as "bN". +static bool printModifiedFPRAsmOperand(const MachineOperand &MO, + const TargetRegisterInfo *TRI, + const TargetRegisterClass &RegClass, + raw_ostream &O) { + if (!MO.isReg()) + return true; + + for (MCRegAliasIterator AR(MO.getReg(), TRI, true); AR.isValid(); ++AR) { + if (RegClass.contains(*AR)) { + O << EpiphanyInstPrinter::getRegisterName(*AR); + return false; + } + } + return true; +} + +/// Implements the 'w' and 'x' inline asm operand modifiers, which print a GPR +/// with the obvious type and an immediate 0 as either wzr or xzr. +static bool printModifiedGPRAsmOperand(const MachineOperand &MO, + const TargetRegisterInfo *TRI, + const TargetRegisterClass &RegClass, + raw_ostream &O) { + char Prefix = &RegClass == &Epiphany::GPR32RegClass ? 'w' : 'x'; + + if (MO.isImm() && MO.getImm() == 0) { + O << Prefix << "zr"; + return false; + } else if (MO.isReg()) { + if (MO.getReg() == Epiphany::SP) { + O << (Prefix == 'x' ? "sp" : "wsp"); + return false; + } + + for (MCRegAliasIterator AR(MO.getReg(), TRI, true); AR.isValid(); ++AR) { + if (RegClass.contains(*AR)) { + O << EpiphanyInstPrinter::getRegisterName(*AR); + return false; + } + } + } + + return true; +} + +bool EpiphanyAsmPrinter::printSymbolicAddress(const MachineOperand &MO, + bool PrintImmediatePrefix, + StringRef Suffix, raw_ostream &O) { + StringRef Name; + StringRef Modifier; + switch (MO.getType()) { + default: + llvm_unreachable("Unexpected operand for symbolic address constraint"); + case MachineOperand::MO_GlobalAddress: + Name = Mang->getSymbol(MO.getGlobal())->getName(); + + // Global variables may be accessed either via a GOT or in various fun and + // interesting TLS-model specific ways. Set the prefix modifier as + // appropriate here. + if (const GlobalVariable *GV = dyn_cast(MO.getGlobal())) { + Reloc::Model RelocM = TM.getRelocationModel(); + if (Subtarget->GVIsIndirectSymbol(GV, RelocM)) { + Modifier = "got"; + } + } + break; + case MachineOperand::MO_BlockAddress: + Name = GetBlockAddressSymbol(MO.getBlockAddress())->getName(); + break; + case MachineOperand::MO_ExternalSymbol: + Name = MO.getSymbolName(); + break; + case MachineOperand::MO_ConstantPoolIndex: + Name = GetCPISymbol(MO.getIndex())->getName(); + break; + } + + // Some instructions (notably ADRP) don't take the # prefix for + // immediates. Only print it if asked to. + if (PrintImmediatePrefix) + O << '#'; + + // Only need the joining "_" if both the prefix and the suffix are + // non-null. This little block simply takes care of the four possibly + // combinations involved there. + if (Modifier == "" && Suffix == "") + O << Name; + else if (Modifier == "" && Suffix != "") + O << ":" << Suffix << ':' << Name; + else if (Modifier != "" && Suffix == "") + O << ":" << Modifier << ':' << Name; + else + O << ":" << Modifier << '_' << Suffix << ':' << Name; + + return false; +} + +bool EpiphanyAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNum, + unsigned AsmVariant, + const char *ExtraCode, raw_ostream &O) { + const TargetRegisterInfo *TRI = MF->getTarget().getRegisterInfo(); + if (!ExtraCode || !ExtraCode[0]) { + // There's actually no operand modifier, which leads to a slightly eclectic + // set of behaviour which we have to handle here. + const MachineOperand &MO = MI->getOperand(OpNum); + switch (MO.getType()) { + default: + llvm_unreachable("Unexpected operand for inline assembly"); + case MachineOperand::MO_Register: + // GCC prints the unmodified operand of a 'w' constraint as the vector + // register. Technically, we could allocate the argument as a VPR128, but + // that leads to extremely dodgy copies being generated to get the data + // there. + //if (printModifiedFPRAsmOperand(MO, TRI, Epiphany::VPR128RegClass, O)) + // O << EpiphanyInstPrinter::getRegisterName(MO.getReg()); + llvm_unreachable("oh fuck.. mo 128vpr"); + break; + case MachineOperand::MO_Immediate: + O << '#' << MO.getImm(); + break; + case MachineOperand::MO_FPImmediate: + assert(MO.getFPImm()->isExactlyValue(0.0) && "Only FP 0.0 expected"); + O << "#0.0"; + break; + case MachineOperand::MO_BlockAddress: + case MachineOperand::MO_ConstantPoolIndex: + case MachineOperand::MO_GlobalAddress: + case MachineOperand::MO_ExternalSymbol: + return printSymbolicAddress(MO, false, "", O); + } + return false; + } + + // We have a real modifier to handle. + switch(ExtraCode[0]) { + default: + // See if this is a generic operand + return AsmPrinter::PrintAsmOperand(MI, OpNum, AsmVariant, ExtraCode, O); + case 'c': // Don't print "#" before an immediate operand. + if (!MI->getOperand(OpNum).isImm()) + return true; + O << MI->getOperand(OpNum).getImm(); + return false; + case 'w': + // Output 32-bit general register operand, constant zero as wzr, or stack + // pointer as wsp. Ignored when used with other operand types. + return printModifiedGPRAsmOperand(MI->getOperand(OpNum), TRI, + Epiphany::GPR32RegClass, O); + //case 'x': + // // Output 64-bit general register operand, constant zero as xzr, or stack + // // pointer as sp. Ignored when used with other operand types. + // return printModifiedGPRAsmOperand(MI->getOperand(OpNum), TRI, + // Epiphany::GPR64RegClass, O); + case 'H': + // Output higher numbered of a 64-bit general register pair + case 'Q': + // Output least significant register of a 64-bit general register pair + case 'R': + // Output most significant register of a 64-bit general register pair + + // FIXME note: these three operand modifiers will require, to some extent, + // adding a paired GPR64 register class. Initial investigation suggests that + // assertions are hit unless it has a type and is made legal for that type + // in ISelLowering. After that step is made, the number of modifications + // needed explodes (operation legality, calling conventions, stores, reg + // copies ...). + llvm_unreachable("FIXME: Unimplemented register pairs"); + //case 'b': + // // Output 8-bit FP/SIMD scalar register operand, prefixed with b. + // return printModifiedFPRAsmOperand(MI->getOperand(OpNum), TRI, + // Epiphany::FPR8RegClass, O); + //case 'h': + // // Output 16-bit FP/SIMD scalar register operand, prefixed with h. + // return printModifiedFPRAsmOperand(MI->getOperand(OpNum), TRI, + // Epiphany::FPR16RegClass, O); + case 's': + // Output 32-bit FP/SIMD scalar register operand, prefixed with s. + return printModifiedFPRAsmOperand(MI->getOperand(OpNum), TRI, + Epiphany::FPR32RegClass, O); + //case 'd': + // // Output 64-bit FP/SIMD scalar register operand, prefixed with d. + // return printModifiedFPRAsmOperand(MI->getOperand(OpNum), TRI, + // Epiphany::FPR64RegClass, O); + //case 'q': + // // Output 128-bit FP/SIMD scalar register operand, prefixed with q. + // return printModifiedFPRAsmOperand(MI->getOperand(OpNum), TRI, + // Epiphany::FPR128RegClass, O); + case 'A': + // Output symbolic address with appropriate relocation modifier (also + // suitable for ADRP). + return printSymbolicAddress(MI->getOperand(OpNum), false, "", O); + case 'L': + // Output bits 11:0 of symbolic address with appropriate :lo12: relocation + // modifier. + return printSymbolicAddress(MI->getOperand(OpNum), true, "lo12", O); + case 'G': + // Output bits 23:12 of symbolic address with appropriate :hi12: relocation + // modifier (currently only for TLS local exec). + return printSymbolicAddress(MI->getOperand(OpNum), true, "hi12", O); + } + + +} + +bool EpiphanyAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, + unsigned OpNum, + unsigned AsmVariant, + const char *ExtraCode, + raw_ostream &O) { + // Currently both the memory constraints (m and Q) behave the same and amount + // to the address as a single register. In future, we may allow "m" to provide + // both a base and an offset. + const MachineOperand &MO = MI->getOperand(OpNum); + assert(MO.isReg() && "unexpected inline assembly memory operand"); + O << '[' << EpiphanyInstPrinter::getRegisterName(MO.getReg()) << ']'; + return false; +} + +void EpiphanyAsmPrinter::PrintDebugValueComment(const MachineInstr *MI, + raw_ostream &OS) { + unsigned NOps = MI->getNumOperands(); + assert(NOps==4); + OS << '\t' << MAI->getCommentString() << "DEBUG_VALUE: "; + // cast away const; DIetc do not take const operands for some reason. + DIVariable V(const_cast(MI->getOperand(NOps-1).getMetadata())); + OS << V.getName(); + OS << " <- "; + // Frame address. Currently handles register +- offset only. + assert(MI->getOperand(0).isReg() && MI->getOperand(1).isImm()); + OS << '[' << EpiphanyInstPrinter::getRegisterName(MI->getOperand(0).getReg()); + OS << '+' << MI->getOperand(1).getImm(); + OS << ']'; + OS << "+" << MI->getOperand(NOps - 2).getImm(); +} + + +#include "EpiphanyGenMCPseudoLowering.inc" + +void EpiphanyAsmPrinter::EmitInstruction(const MachineInstr *MI) { + // Do any auto-generated pseudo lowerings. + if (emitPseudoExpansionLowering(OutStreamer, MI)) + return; + + switch (MI->getOpcode()) { + case Epiphany::DBG_VALUE: { + if (isVerbose() && OutStreamer.hasRawTextSupport()) { + SmallString<128> TmpStr; + raw_svector_ostream OS(TmpStr); + PrintDebugValueComment(MI, OS); + OutStreamer.EmitRawText(StringRef(OS.str())); + } + return; + } + } + + MCInst TmpInst; + LowerEpiphanyMachineInstrToMCInst(MI, TmpInst, *this); + OutStreamer.EmitInstruction(TmpInst); +} + +void EpiphanyAsmPrinter::EmitEndOfAsmFile(Module &M) { + if (Subtarget->isTargetELF()) { + const TargetLoweringObjectFileELF &TLOFELF = + static_cast(getObjFileLowering()); + + MachineModuleInfoELF &MMIELF = MMI->getObjFileInfo(); + + // Output stubs for external and common global variables. + MachineModuleInfoELF::SymbolListTy Stubs = MMIELF.GetGVStubList(); + if (!Stubs.empty()) { + OutStreamer.SwitchSection(TLOFELF.getDataRelSection()); + const DataLayout *TD = TM.getDataLayout(); + + for (unsigned i = 0, e = Stubs.size(); i != e; ++i) { + OutStreamer.EmitLabel(Stubs[i].first); + OutStreamer.EmitSymbolValue(Stubs[i].second.getPointer(), + TD->getPointerSize(0), 0); + } + Stubs.clear(); + } + } +} + +bool EpiphanyAsmPrinter::runOnMachineFunction(MachineFunction &MF) { + return AsmPrinter::runOnMachineFunction(MF); +} + +// Force static initialization. +extern "C" void LLVMInitializeEpiphanyAsmPrinter() { + RegisterAsmPrinter X(TheEpiphanyTarget); +} + diff --git a/EpiphanyAsmPrinter.h b/EpiphanyAsmPrinter.h new file mode 100644 index 0000000..07da42f --- /dev/null +++ b/EpiphanyAsmPrinter.h @@ -0,0 +1,80 @@ +// EpiphanyAsmPrinter.h - Print machine code to an Epiphany .s file -*- 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 Epiphany assembly printer class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EPIPHANYASMPRINTER_H +#define LLVM_EPIPHANYASMPRINTER_H + +#include "Epiphany.h" +#include "EpiphanyTargetMachine.h" +#include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/Support/Compiler.h" + +namespace llvm { + +class MCOperand; + +class LLVM_LIBRARY_VISIBILITY EpiphanyAsmPrinter : public AsmPrinter { + + /// Subtarget - Keep a pointer to the EpiphanySubtarget around so that we can + /// make the right decision when printing asm code for different targets. + const EpiphanySubtarget *Subtarget; + + // emitPseudoExpansionLowering - tblgen'erated. + bool emitPseudoExpansionLowering(MCStreamer &OutStreamer, + const MachineInstr *MI); + + public: + explicit EpiphanyAsmPrinter(TargetMachine &TM, MCStreamer &Streamer) + : AsmPrinter(TM, Streamer) { + Subtarget = &TM.getSubtarget(); + } + + bool lowerOperand(const MachineOperand &MO, MCOperand &MCOp) const; + + MCOperand lowerSymbolOperand(const MachineOperand &MO, + const MCSymbol *Sym) const; + + void EmitInstruction(const MachineInstr *MI); + void EmitEndOfAsmFile(Module &M); + + bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNum, + unsigned AsmVariant, const char *ExtraCode, + raw_ostream &O); + bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNum, + unsigned AsmVariant, const char *ExtraCode, + raw_ostream &O); + + void PrintDebugValueComment(const MachineInstr *MI, raw_ostream &OS); + + /// printSymbolicAddress - Given some kind of reasonably bare symbolic + /// reference, print out the appropriate asm string to represent it. If + /// appropriate, a relocation-specifier will be produced, composed of a + /// general class derived from the MO parameter and an instruction-specific + /// suffix, provided in Suffix. E.g. ":got_lo12:" if a Suffix of "lo12" is + /// given. + bool printSymbolicAddress(const MachineOperand &MO, + bool PrintImmediatePrefix, + StringRef Suffix, raw_ostream &O); + + MachineLocation getDebugValueLocation(const MachineInstr *MI) const; + + virtual const char *getPassName() const { + return "Epiphany Assembly Printer"; + } + + virtual bool runOnMachineFunction(MachineFunction &MF); +}; +} // end namespace llvm + +#endif diff --git a/EpiphanyCallingConv.td b/EpiphanyCallingConv.td new file mode 100644 index 0000000..d47f61a --- /dev/null +++ b/EpiphanyCallingConv.td @@ -0,0 +1,25 @@ +//==-- EpiphanyCallingConv.td - Calling Conventions for ARM ----*- tblgen -*-==// +// +// 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 Epiphany architecture. +//===----------------------------------------------------------------------===// + +def CC_A64_APCS : CallingConv<[ + // Put ByVal arguments directly on the stack. + CCIfByVal>, + + //CCIfType<[v1i64, v2i32, v4i16, v8i8, v2f32], CCBitConvertToType> + // CCIfType<[f32], CCAssignToReg<[S0, S1, S2, S3, S4, S5, S6, S7]>>, + CCIfType<[i8, i16], CCPromoteToType>, + CCIfType<[i32,f32], CCAssignToReg<[R0, R1, R2, R3]>>, + // CCIfType<[i64,f64], CCAssignToReg<[D0, D1]>>, + CCIfType<[i32,f32], CCAssignToStack<4, 4>> + // CCIfType<[i64,f64], CCAssignToStack<8, 8>> +]>; + +def CSR_PCS : CalleeSavedRegs<(add LR, (sequence "R%u", 43, 32),(sequence "R%u", 11, 4))>; diff --git a/EpiphanyFrameLowering.cpp b/EpiphanyFrameLowering.cpp new file mode 100644 index 0000000..dfecad6 --- /dev/null +++ b/EpiphanyFrameLowering.cpp @@ -0,0 +1,563 @@ +//===- EpiphanyFrameLowering.cpp - Epiphany 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 Epiphany implementation of TargetFrameLowering class. +// +//===----------------------------------------------------------------------===// + +#include "Epiphany.h" +#include "EpiphanyFrameLowering.h" +#include "EpiphanyMachineFunctionInfo.h" +#include "EpiphanyInstrInfo.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineMemOperand.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/RegisterScavenging.h" +#include "llvm/IR/Function.h" +#include "llvm/MC/MachineLocation.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" + +using namespace llvm; + +void EpiphanyFrameLowering::splitSPAdjustments(uint64_t Total, + uint64_t &Initial, + uint64_t &Residual) const { + // 0x1f0 here is a pessimistic (i.e. realistic) boundary: x-register LDP + // instructions have a 7-bit signed immediate scaled by 8, giving a reach of + // 0x1f8, but stack adjustment should always be a multiple of 16. + if (Total <= 0x1f0) { + Initial = Total; + Residual = 0; + } else { + Initial = 0x1f0; + Residual = Total - Initial; + } +} + +void EpiphanyFrameLowering::emitPrologue(MachineFunction &MF) const { + EpiphanyMachineFunctionInfo *FuncInfo = + MF.getInfo(); + MachineBasicBlock &MBB = MF.front(); + MachineBasicBlock::iterator MBBI = MBB.begin(); + MachineFrameInfo *MFI = MF.getFrameInfo(); + const TargetInstrInfo &TII = *MF.getTarget().getInstrInfo(); + DebugLoc DL = MBBI != MBB.end() ? MBBI->getDebugLoc() : DebugLoc(); + + MachineModuleInfo &MMI = MF.getMMI(); + std::vector &Moves = MMI.getFrameMoves(); + bool NeedsFrameMoves = MMI.hasDebugInfo() || MF.getFunction()->needsUnwindTableEntry(); + + uint64_t NumInitialBytes, NumResidualBytes; + + // Currently we expect the stack to be laid out by + // sub sp, sp, #initial + // stp R11, W30, [sp, #offset] + // ... + // str xxx, [sp, #offset] + // sub sp, sp, #rest (possibly via extra instructions). + if (MFI->getCalleeSavedInfo().size()) { + // If there are callee-saved registers, we want to store them efficiently as + // a block, and virtual base assignment happens too early to do it for us so + // we adjust the stack in two phases: first just for callee-saved fiddling, + // then to allocate the rest of the frame. + splitSPAdjustments(MFI->getStackSize(), NumInitialBytes, NumResidualBytes); + } else { + // If there aren't any callee-saved registers, two-phase adjustment is + // inefficient. It's more efficient to adjust with NumInitialBytes too + // because when we're in a "callee pops argument space" situation, that pop + // must be tacked onto Initial for correctness. + NumInitialBytes = MFI->getStackSize(); + NumResidualBytes = 0; + } + + // Tell everyone else how much adjustment we're expecting them to use. In + // particular if an adjustment is required for a tail call the epilogue could + // have a different view of things. + FuncInfo->setInitialStackAdjust(NumInitialBytes); + + EPIPHemitSPUpdate(MBB, MBBI, DL, TII, Epiphany::R63, -NumInitialBytes, + MachineInstr::FrameSetup); + + if (NeedsFrameMoves && NumInitialBytes) { + // We emit this update even if the CFA is set from a frame pointer later so + // that the CFA is valid in the interim. + MCSymbol *SPLabel = MMI.getContext().CreateTempSymbol(); + BuildMI(MBB, MBBI, DL, TII.get(TargetOpcode::PROLOG_LABEL)) + .addSym(SPLabel); + + MachineLocation Dst(MachineLocation::VirtualFP); + MachineLocation Src(Epiphany::SP, NumInitialBytes); + Moves.push_back(MachineMove(SPLabel, Dst, Src)); + } + + // Otherwise we need to set the frame pointer and/or add a second stack + // adjustment. + + bool FPNeedsSetting = hasFP(MF); + for (; MBBI != MBB.end(); ++MBBI) { + // Note that this search makes strong assumptions about the operation used + // to store the frame-pointer: it must be "STP R11, W30, ...". This could + // change in future, but until then there's no point in implementing + // untestable more generic cases. + //if (FPNeedsSetting && MBBI->getOpcode() == Epiphany::LSPair64_STR <------------------------ we don't have a pair instruction :( + // && MBBI->getOperand(0).getReg() == Epiphany::R11) { + // int64_t R11FrameIdx = MBBI->getOperand(2).getIndex(); + // FuncInfo->setFramePointerOffset(MFI->getObjectOffset(R11FrameIdx)); + + // ++MBBI; + // EPIPHemitRegUpdate(MBB, MBBI, DL, TII, Epiphany::R11, Epiphany::SP, + // Epiphany::R11, + // NumInitialBytes + MFI->getObjectOffset(R11FrameIdx), + // MachineInstr::FrameSetup); + + // // The offset adjustment used when emitting debugging locations relative + // // to whatever frame base is set. Epiphany uses the default frame base (FP + // // or SP) and this adjusts the calculations to be correct. + // MFI->setOffsetAdjustment(- MFI->getObjectOffset(R11FrameIdx) + // - MFI->getStackSize()); + + // if (NeedsFrameMoves) { + // MCSymbol *FPLabel = MMI.getContext().CreateTempSymbol(); + // BuildMI(MBB, MBBI, DL, TII.get(TargetOpcode::PROLOG_LABEL)) + // .addSym(FPLabel); + // MachineLocation Dst(MachineLocation::VirtualFP); + // MachineLocation Src(Epiphany::R11, -MFI->getObjectOffset(R11FrameIdx)); + // Moves.push_back(MachineMove(FPLabel, Dst, Src)); + // } + + // FPNeedsSetting = false; + //} + + if (!MBBI->getFlag(MachineInstr::FrameSetup)) + break; + } + + assert(!FPNeedsSetting && "Frame pointer couldn't be set"); + + EPIPHemitSPUpdate(MBB, MBBI, DL, TII, Epiphany::R63, -NumResidualBytes, + MachineInstr::FrameSetup); + + // Now we emit the rest of the frame setup information, if necessary: we've + // already noted the FP and initial SP moves so we're left with the prologue's + // final SP update and callee-saved register locations. + if (!NeedsFrameMoves) + return; + + // Reuse the label if appropriate, so create it in this outer scope. + MCSymbol *CSLabel = 0; + + // The rest of the stack adjustment + if (!hasFP(MF) && NumResidualBytes) { + CSLabel = MMI.getContext().CreateTempSymbol(); + BuildMI(MBB, MBBI, DL, TII.get(TargetOpcode::PROLOG_LABEL)) + .addSym(CSLabel); + + MachineLocation Dst(MachineLocation::VirtualFP); + MachineLocation Src(Epiphany::SP, NumResidualBytes + NumInitialBytes); + Moves.push_back(MachineMove(CSLabel, Dst, Src)); + } + + // And any callee-saved registers (it's fine to leave them to the end here, + // because the old values are still valid at this point. + const std::vector &CSI = MFI->getCalleeSavedInfo(); + if (CSI.size()) { + if (!CSLabel) { + CSLabel = MMI.getContext().CreateTempSymbol(); + BuildMI(MBB, MBBI, DL, TII.get(TargetOpcode::PROLOG_LABEL)) + .addSym(CSLabel); + } + + for (std::vector::const_iterator I = CSI.begin(), + E = CSI.end(); I != E; ++I) { + MachineLocation Dst(MachineLocation::VirtualFP, + MFI->getObjectOffset(I->getFrameIdx())); + MachineLocation Src(I->getReg()); + Moves.push_back(MachineMove(CSLabel, Dst, Src)); + } + } +} + +void +EpiphanyFrameLowering::emitEpilogue(MachineFunction &MF, + MachineBasicBlock &MBB) const { + EpiphanyMachineFunctionInfo *FuncInfo = + MF.getInfo(); + + MachineBasicBlock::iterator MBBI = MBB.getLastNonDebugInstr(); + DebugLoc DL = MBBI->getDebugLoc(); + const TargetInstrInfo &TII = *MF.getTarget().getInstrInfo(); + MachineFrameInfo &MFI = *MF.getFrameInfo(); + unsigned RetOpcode = MBBI->getOpcode(); + + // Initial and residual are named for consitency with the prologue. Note that + // in the epilogue, the residual adjustment is executed first. + uint64_t NumInitialBytes = FuncInfo->getInitialStackAdjust(); + uint64_t NumResidualBytes = MFI.getStackSize() - NumInitialBytes; + uint64_t ArgumentPopSize = 0; + + ArgumentPopSize = FuncInfo->getArgumentStackToRestore(); + + assert(NumInitialBytes % 8 == 0 && NumResidualBytes % 8 == 0 + && "refusing to adjust stack by misaligned amt"); + + // We may need to address callee-saved registers differently, so find out the + // bound on the frame indices. + const std::vector &CSI = MFI.getCalleeSavedInfo(); + int MinCSFI = 0; + int MaxCSFI = -1; + + if (CSI.size()) { + MinCSFI = CSI[0].getFrameIdx(); + MaxCSFI = CSI[CSI.size() - 1].getFrameIdx(); + } + + // The "residual" stack update comes first from this direction and guarantees + // that SP is NumInitialBytes below its value on function entry, either by a + // direct update or restoring it from the frame pointer. + if (NumInitialBytes + ArgumentPopSize != 0) { + EPIPHemitSPUpdate(MBB, MBBI, DL, TII, Epiphany::R63, + NumInitialBytes + ArgumentPopSize); + --MBBI; + } + + + // MBBI now points to the instruction just past the last callee-saved + // restoration (either RET/B if NumInitialBytes == 0, or the "ADD sp, sp" + // otherwise). + + // Now we need to find out where to put the bulk of the stack adjustment + MachineBasicBlock::iterator FirstEpilogue = MBBI; + while (MBBI != MBB.begin()) { + --MBBI; + + unsigned FrameOp; + for (FrameOp = 0; FrameOp < MBBI->getNumOperands(); ++FrameOp) { + if (MBBI->getOperand(FrameOp).isFI()) + break; + } + + // If this instruction doesn't have a frame index we've reached the end of + // the callee-save restoration. + if (FrameOp == MBBI->getNumOperands()) + break; + + // Likewise if it *is* a local reference, but not to a callee-saved object. + int FrameIdx = MBBI->getOperand(FrameOp).getIndex(); + if (FrameIdx < MinCSFI || FrameIdx > MaxCSFI) + break; + + FirstEpilogue = MBBI; + } + + if (MF.getFrameInfo()->hasVarSizedObjects()) { + int64_t StaticFrameBase; + StaticFrameBase = -(NumInitialBytes + FuncInfo->getFramePointerOffset()); + EPIPHemitRegUpdate(MBB, FirstEpilogue, DL, TII, + Epiphany::SP, Epiphany::R11, Epiphany::NoRegister, + StaticFrameBase); + } else { + EPIPHemitSPUpdate(MBB, FirstEpilogue, DL,TII, Epiphany::R63, NumResidualBytes); + } +} + +int64_t +EpiphanyFrameLowering::resolveFrameIndexReference(MachineFunction &MF, + int FrameIndex, + unsigned &FrameReg, + int SPAdj, + bool IsCalleeSaveOp) const { + EpiphanyMachineFunctionInfo *FuncInfo = + MF.getInfo(); + MachineFrameInfo *MFI = MF.getFrameInfo(); + + int64_t TopOfFrameOffset = MFI->getObjectOffset(FrameIndex); + + assert(!(IsCalleeSaveOp && FuncInfo->getInitialStackAdjust() == 0) + && "callee-saved register in unexpected place"); + + // If the frame for this function is particularly large, we adjust the stack + // in two phases which means the callee-save related operations see a + // different (intermediate) stack size. + int64_t FrameRegPos; + if (IsCalleeSaveOp) { + FrameReg = Epiphany::SP; + FrameRegPos = -static_cast(FuncInfo->getInitialStackAdjust()); + } else if (useFPForAddressing(MF)) { + // Have to use the frame pointer since we have no idea where SP is. + FrameReg = Epiphany::R11; + FrameRegPos = FuncInfo->getFramePointerOffset(); + } else { + FrameReg = Epiphany::SP; + FrameRegPos = -static_cast(MFI->getStackSize()) + SPAdj; + } + + return TopOfFrameOffset - FrameRegPos; +} + +void +EpiphanyFrameLowering::processFunctionBeforeCalleeSavedScan(MachineFunction &MF, + RegScavenger *RS) const { + const EpiphanyRegisterInfo *RegInfo = static_cast(MF.getTarget().getRegisterInfo()); + MachineFrameInfo *MFI = MF.getFrameInfo(); + const EpiphanyInstrInfo &TII = *static_cast(MF.getTarget().getInstrInfo()); + + if (hasFP(MF)) { + MF.getRegInfo().setPhysRegUsed(Epiphany::R11); + MF.getRegInfo().setPhysRegUsed(Epiphany::LR); + } + + // If addressing of local variables is going to be more complicated than + // shoving a base register and an offset into the instruction then we may well + // need to scavenge registers. We should either specifically add an + // callee-save register for this purpose or allocate an extra spill slot. + + bool BigStack = + (RS && MFI->estimateStackSize(MF) >= TII.estimateRSStackLimit(MF)) + || MFI->hasVarSizedObjects() // Access will be from R11: messes things up + || (MFI->adjustsStack() && !hasReservedCallFrame(MF)); + + if (!BigStack) + return; + + // We certainly need some slack space for the scavenger, preferably an extra + // register. + const uint16_t *CSRegs = RegInfo->getCalleeSavedRegs(); + uint16_t ExtraReg = Epiphany::NoRegister; + + for (unsigned i = 0; CSRegs[i]; ++i) { + if (Epiphany::GPR32RegClass.contains(CSRegs[i]) && !MF.getRegInfo().isPhysRegUsed(CSRegs[i])) { + ExtraReg = CSRegs[i]; + break; + } + } + + if (ExtraReg != 0) { + MF.getRegInfo().setPhysRegUsed(ExtraReg); + } else { + // Create a stack slot for scavenging purposes. PrologEpilogInserter + // helpfully places it near either SP or FP for us to avoid + // infinitely-regression during scavenging. + const TargetRegisterClass *RC = &Epiphany::GPR32RegClass; + RS->setScavengingFrameIndex(MFI->CreateStackObject(RC->getSize(), + RC->getAlignment(), + false)); + } +} + +bool EpiphanyFrameLowering::determinePrologueDeath(MachineBasicBlock &MBB, + unsigned Reg) const { + // If @llvm.returnaddress is called then it will refer to W30 by some means; + // the prologue store does not kill the register. + if (Reg == Epiphany::LR) { + if (MBB.getParent()->getFrameInfo()->isReturnAddressTaken() + && MBB.getParent()->getRegInfo().isLiveIn(Reg)) + return false; + } + + // In all other cases, physical registers are dead after they've been saved + // but live at the beginning of the prologue block. + MBB.addLiveIn(Reg); + return true; +} + +void +EpiphanyFrameLowering::eliminateCallFramePseudoInstr(MachineFunction &MF, + MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI) const { + const EpiphanyInstrInfo &TII = *static_cast(MF.getTarget().getInstrInfo()); + DebugLoc dl = MI->getDebugLoc(); + int Opcode = MI->getOpcode(); + bool IsDestroy = Opcode == TII.getCallFrameDestroyOpcode(); + uint64_t CalleePopAmount = IsDestroy ? MI->getOperand(1).getImm() : 0; + + if (!hasReservedCallFrame(MF)) { + unsigned Align = getStackAlignment(); + + int64_t Amount = MI->getOperand(0).getImm(); + Amount = RoundUpToAlignment(Amount, Align); + if (!IsDestroy) Amount = -Amount; + + // N.b. if CalleePopAmount is valid but zero (i.e. callee would pop, but it + // doesn't have to pop anything), then the first operand will be zero too so + // this adjustment is a no-op. + if (CalleePopAmount == 0) { + // FIXME: in-function stack adjustment for calls is limited to 12-bits + // because there's no guaranteed temporary register available. Mostly call + // frames will be allocated at the start of a function so this is OK, but + // it is a limitation that needs dealing with. + assert(Amount > -(0x3FF) && Amount < 0x3FF && "call frame too large"); + EPIPHemitSPUpdate(MBB, MI, dl, TII, Epiphany::NoRegister, Amount); + } + } else if (CalleePopAmount != 0) { + // If the calling convention demands that the callee pops arguments from the + // stack, we want to add it back if we have a reserved call frame. + assert(CalleePopAmount < 0x3FF && "call frame too large"); + EPIPHemitSPUpdate(MBB, MI, dl, TII, Epiphany::NoRegister, -CalleePopAmount); + } + + MBB.erase(MI); +} + +void +EpiphanyFrameLowering::emitFrameMemOps(bool isPrologue, MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, + const std::vector &CSI, + const TargetRegisterInfo *TRI) const { + DebugLoc DL = MBB.findDebugLoc(MBBI); + MachineFunction &MF = *MBB.getParent(); + MachineFrameInfo &MFI = *MF.getFrameInfo(); + const TargetInstrInfo &TII = *MF.getTarget().getInstrInfo(); + + // A certain amount of implicit contract is present here. The actual stack + // offsets haven't been allocated officially yet, so for strictly correct code + // we rely on the fact that the elements of CSI are allocated in order + // starting at SP, purely as dictated by size and alignment. In practice since + // this function handles the only accesses to those slots it's not quite so + // important. + // + // We have also ordered the Callee-saved register list in EpiphanyCallingConv + // so that the above scheme puts registers in order: in particular we want + // &W30 to be &R11+8 for an ABI-correct frame record (PCS 5.2.2) + for (unsigned i = 0, e = CSI.size(); i < e; ++i) { + unsigned Reg = CSI[i].getReg(); + + // Let's try a double store first: regs must be a even-odd pair + MachineInstrBuilder NewMI; + bool Pair = false; + + // alignment? + //unsigned suba, subb, sra=0, srb=0; + //if(i+1 < CSI.size()){ + // suba = CSI[i].getReg(); + // subb = CSI[i+1].getReg(); + // sra = TRI->getMatchingSuperReg (suba, Epiphany::sub_even, &Epiphany::DPR64RegClass); + // srb = TRI->getMatchingSuperReg (subb, Epiphany::sub_odd, &Epiphany::DPR64RegClass); + // if( (!sra || !srb) || sra != srb){ + // srb = TRI->getMatchingSuperReg (suba, Epiphany::sub_odd, &Epiphany::DPR64RegClass); + // sra = TRI->getMatchingSuperReg (subb, Epiphany::sub_even, &Epiphany::DPR64RegClass); + // std::swap(suba,subb); + // } + //} + //if ((sra && srb) && sra == srb) { + // Pair = true; + // unsigned StLow = 0, StHigh = 0; + // if (isPrologue) { + // // Most of these registers will be live-in to the MBB and killed by our + // // store, though there are exceptions (see determinePrologueDeath). + // StLow = getKillRegState(determinePrologueDeath(MBB, CSI[i+1].getReg())); + // StHigh = getKillRegState(determinePrologueDeath(MBB, CSI[i].getReg())); + // } else { + // StLow = RegState::Define; + // StHigh = RegState::Define; + // } + + // //HACK FIXME liveness is a bit wrong... + // NewMI = BuildMI(MBB, MBBI, DL, TII.get(isPrologue ? Epiphany::LSFP64_STR : Epiphany::LSFP64_LDR)) + // .addReg(sra, RegState::Kill); + // //.addReg(suba, StLow) + // //.addReg(subb, StHigh | RegState::Implicit); + + // // If it's a paired op, we've consumed two registers + // ++i; + //} + //else { + unsigned State; + if (isPrologue) { + State = getKillRegState(determinePrologueDeath(MBB, CSI[i].getReg())); + } else { + State = RegState::Define; + } + + NewMI = BuildMI(MBB, MBBI, DL, TII.get(isPrologue ? Epiphany::LS32_STR : Epiphany::LS32_LDR)) + .addReg(CSI[i].getReg(), State); + //} + + // Are we grabbing the right index? FIXME + int FrameIdx = CSI[Pair ? i-1 : i].getFrameIdx(); + + MachineMemOperand *MMO = MF.getMachineMemOperand(MachinePointerInfo::getFixedStack(FrameIdx), + isPrologue ? MachineMemOperand::MOStore : MachineMemOperand::MOLoad, + Pair ? 8 : 4, + MFI.getObjectAlignment(FrameIdx)); + + NewMI.addFrameIndex(FrameIdx).addImm(0)/*address-register offset*/.addMemOperand(MMO); + + if (isPrologue) + NewMI.setMIFlags(MachineInstr::FrameSetup); + + // For aesthetic reasons, during an epilogue we want to emit complementary + // operations to the prologue, but in the opposite order. So we still + // iterate through the CalleeSavedInfo list in order, but we put the + // instructions successively earlier in the MBB. + if (!isPrologue) + --MBBI; + } +} + +bool +EpiphanyFrameLowering::spillCalleeSavedRegisters(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, + const std::vector &CSI, + const TargetRegisterInfo *TRI) const { + if (CSI.empty()) + return false; + emitFrameMemOps(/* isPrologue = */ true, MBB, MBBI, CSI, TRI); + + return true; +} + +bool +EpiphanyFrameLowering::restoreCalleeSavedRegisters(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, + const std::vector &CSI, + const TargetRegisterInfo *TRI) const { + + if (CSI.empty()) + return false; + emitFrameMemOps(/* isPrologue = */ false, MBB, MBBI, CSI, TRI); + + return true; +} + +bool +EpiphanyFrameLowering::hasFP(const MachineFunction &MF) const { + const MachineFrameInfo *MFI = MF.getFrameInfo(); + const TargetRegisterInfo *RI = MF.getTarget().getRegisterInfo(); + + // This is a decision of ABI compliance. The Epiphany PCS gives various options + // for conformance, and even at the most stringent level more or less permits + // elimination for leaf functions because there's no loss of functionality + // (for debugging etc).. + if (MF.getTarget().Options.DisableFramePointerElim(MF) && MFI->hasCalls()) + return true; + + // The following are hard-limits: incorrect code will be generated if we try + // to omit the frame. + return (RI->needsStackRealignment(MF) || + MFI->hasVarSizedObjects() || + MFI->isFrameAddressTaken()); +} + +bool +EpiphanyFrameLowering::useFPForAddressing(const MachineFunction &MF) const { + return MF.getFrameInfo()->hasVarSizedObjects(); +} + +bool +EpiphanyFrameLowering::hasReservedCallFrame(const MachineFunction &MF) const { + const MachineFrameInfo *MFI = MF.getFrameInfo(); + + // Of the various reasons for having a frame pointer, it's actually only + // variable-sized objects that prevent reservation of a call frame. + return !(hasFP(MF) && MFI->hasVarSizedObjects()); +} diff --git a/EpiphanyFrameLowering.h b/EpiphanyFrameLowering.h new file mode 100644 index 0000000..e705c9d --- /dev/null +++ b/EpiphanyFrameLowering.h @@ -0,0 +1,106 @@ +//==- EpiphanyFrameLowering.h - Define frame lowering for Epiphany -*- C++ -*--=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class implements the Epiphany-specific parts of the TargetFrameLowering +// class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EPIPHANY_FRAMEINFO_H +#define LLVM_EPIPHANY_FRAMEINFO_H + +#include "EpiphanySubtarget.h" +#include "llvm/Target/TargetFrameLowering.h" + +namespace llvm { +class EpiphanySubtarget; + +class EpiphanyFrameLowering : public TargetFrameLowering { +private: + // In order to unify the spilling and restoring of callee-saved registers into + // emitFrameMemOps, we need to be able to specify which instructions to use + // for the relevant memory operations on each register class. An array of the + // following struct is populated and passed in to achieve this. + struct LoadStoreMethod { + const TargetRegisterClass *RegClass; // E.g. GPR64RegClass + + // The preferred instruction. + unsigned PairOpcode; // E.g. LSPair64_STR + + // Sometimes only a single register can be handled at once. + unsigned SingleOpcode; // E.g. LS64_STR + }; +protected: + const EpiphanySubtarget &STI; + +public: + explicit EpiphanyFrameLowering(const EpiphanySubtarget &sti) + : TargetFrameLowering(TargetFrameLowering::StackGrowsDown, 8, 0, 4), + STI(sti) { + } + + /// emitProlog/emitEpilog - These methods insert prolog and epilog code into + /// the function. + virtual void emitPrologue(MachineFunction &MF) const; + virtual void emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const; + + /// Decides how much stack adjustment to perform in each phase of the prologue + /// and epilogue. + void splitSPAdjustments(uint64_t Total, uint64_t &Initial, + uint64_t &Residual) const; + + int64_t resolveFrameIndexReference(MachineFunction &MF, int FrameIndex, + unsigned &FrameReg, int SPAdj, + bool IsCalleeSaveOp) const; + + virtual void processFunctionBeforeCalleeSavedScan(MachineFunction &MF, + RegScavenger *RS) const; + + virtual bool spillCalleeSavedRegisters(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + const std::vector &CSI, + const TargetRegisterInfo *TRI) const; + virtual bool restoreCalleeSavedRegisters(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + const std::vector &CSI, + const TargetRegisterInfo *TRI) const; + + void eliminateCallFramePseudoInstr(MachineFunction &MF, + MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI) const; + + /// If the register is X30 (i.e. LR) and the return address is used in the + /// function then the callee-save store doesn't actually kill the register, + /// otherwise it does. + bool determinePrologueDeath(MachineBasicBlock &MBB, unsigned Reg) const; + + /// This function emits the loads or stores required during prologue and + /// epilogue as efficiently as possible. + /// + /// The operations involved in setting up and tearing down the frame are + /// similar enough to warrant a shared function, particularly as discrepancies + /// between the two would be disastrous. + void emitFrameMemOps(bool isStore, MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + const std::vector &CSI, + const TargetRegisterInfo *TRI) const; + + + virtual bool hasFP(const MachineFunction &MF) const; + + virtual bool useFPForAddressing(const MachineFunction &MF) const; + + /// On AA + virtual bool hasReservedCallFrame(const MachineFunction &MF) const; + +}; + +} // End llvm namespace + +#endif diff --git a/EpiphanyISelDAGToDAG.cpp b/EpiphanyISelDAGToDAG.cpp new file mode 100644 index 0000000..0f8d1f8 --- /dev/null +++ b/EpiphanyISelDAGToDAG.cpp @@ -0,0 +1,266 @@ +//===-- EpiphanyISelDAGToDAG.cpp - A dag to dag inst selector for Epiphany --===// +// +// 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 Epiphany target. +// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "epiphany-isel" +#include "Epiphany.h" +#include "EpiphanyInstrInfo.h" +#include "EpiphanySubtarget.h" +#include "EpiphanyTargetMachine.h" +#include "Utils/EpiphanyBaseInfo.h" +#include "llvm/ADT/APSInt.h" +#include "llvm/CodeGen/SelectionDAGISel.h" +#include "llvm/IR/GlobalValue.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +//===--------------------------------------------------------------------===// +/// Epiphany specific code to select Epiphany machine instructions for +/// SelectionDAG operations. +/// +namespace { + +class EpiphanyDAGToDAGISel : public SelectionDAGISel { + EpiphanyTargetMachine &TM; + const EpiphanyInstrInfo *TII; + + /// Keep a pointer to the EpiphanySubtarget around so that we can + /// make the right decision when generating code for different targets. + const EpiphanySubtarget *Subtarget; + +public: + explicit EpiphanyDAGToDAGISel(EpiphanyTargetMachine &tm, + CodeGenOpt::Level OptLevel) + : SelectionDAGISel(tm, OptLevel), TM(tm), + TII(static_cast(TM.getInstrInfo())), + Subtarget(&TM.getSubtarget()) { + } + + virtual const char *getPassName() const { + return "Epiphany Instruction Selection"; + } + + // Include the pieces autogenerated from the target description. +#include "EpiphanyGenDAGISel.inc" + + // getImm - Return a target constant with the specified value. + inline SDValue getImm(const SDNode *Node, unsigned Imm) { + return CurDAG->getTargetConstant(Imm, Node->getValueType(0)); + } + + template + bool SelectOffsetUImm11(SDValue N, SDValue &UImm12) { + const ConstantSDNode *CN = dyn_cast(N); + const int maxv = ((2<<10)-1); // = 2^11-1 -> 11 bit + if (!CN || CN->getSExtValue() % MemSize != 0 || !(CN->getSExtValue() / MemSize >= -maxv && CN->getSExtValue() / MemSize <= maxv)) + return false; + + UImm12 = CurDAG->getTargetConstant(CN->getSExtValue() / MemSize, MVT::i32); + return true; + } + + SDNode *TrySelectToMoveImm(SDNode *N); + SDNode *LowerToFPLitPool(SDNode *Node); + SDNode *SelectToLitPool(SDNode *N); + + SDNode* Select(SDNode*); +private: +}; +} + +SDNode *EpiphanyDAGToDAGISel::TrySelectToMoveImm(SDNode *Node) { + SDNode *ResNode; + DebugLoc dl = Node->getDebugLoc(); + EVT DestType = Node->getValueType(0); + unsigned DestWidth = DestType.getSizeInBits(); + + if (DestWidth != 32) + llvm_unreachable("move to dst !=32 ?!"); + + if(DestType.isInteger()){ + uint64_t BitPat = cast(Node)->getZExtValue(); + if (BitPat & ~0xffffffffULL){ + llvm_unreachable("wat."); + } + + // 0 or 16 lower bits + ResNode = CurDAG->getMachineNode(Epiphany::MOVri, dl, DestType, CurDAG->getTargetConstant(BitPat, MVT::i32)); + + if (BitPat & 0xffff0000ULL){// 16 upper bits + //this is LUi(LLi(val{15-0}), val{31-16}) + ResNode = CurDAG->getMachineNode(Epiphany::MOVTri, dl, DestType, SDValue(ResNode, 0), CurDAG->getTargetConstant(BitPat, MVT::i32)); + } + } else if(DestType.isFloatingPoint()){ + APFloat BitPat = cast(Node)->getValueAPF(); + + ResNode = CurDAG->getMachineNode(Epiphany::MOVri_nopat_f, dl, DestType, CurDAG->getTargetConstantFP(BitPat, MVT::f32)); + //this is LUi(LLi(val{15-0}), val{31-16}) + if(!BitPat.isPosZero()) + ResNode = CurDAG->getMachineNode(Epiphany::MOVTri_nopat_f, dl, DestType, SDValue(ResNode, 0), CurDAG->getTargetConstantFP(BitPat, MVT::f32)); + + } + + ReplaceUses(Node, ResNode); + return ResNode; +} + +SDNode *EpiphanyDAGToDAGISel::SelectToLitPool(SDNode *Node) { + DebugLoc DL = Node->getDebugLoc(); + uint64_t UnsignedVal = cast(Node)->getZExtValue(); + int64_t SignedVal = cast(Node)->getSExtValue(); + EVT DestType = Node->getValueType(0); + EVT PtrVT = TLI.getPointerTy(); + + // Since we may end up loading a 64-bit constant from a 32-bit entry the + // constant in the pool may have a different type to the eventual node. + ISD::LoadExtType Extension; + EVT MemType; + + assert((DestType == MVT::i64 || DestType == MVT::i32) + && "Only expect integer constants at the moment"); + + if (DestType == MVT::i32) { + Extension = ISD::NON_EXTLOAD; + MemType = MVT::i32; + } else if (UnsignedVal <= UINT32_MAX) { + Extension = ISD::ZEXTLOAD; + MemType = MVT::i32; + } else if (SignedVal >= INT32_MIN && SignedVal <= INT32_MAX) { + Extension = ISD::SEXTLOAD; + MemType = MVT::i32; + } else { + Extension = ISD::NON_EXTLOAD; + MemType = MVT::i32; + } + + Constant *CV = ConstantInt::get(Type::getIntNTy(*CurDAG->getContext(), + MemType.getSizeInBits()), + UnsignedVal); + SDValue PoolAddr; + unsigned Alignment = TLI.getDataLayout()->getABITypeAlignment(CV->getType()); + PoolAddr = CurDAG->getNode(EpiphanyISD::WrapperSmall, DL, PtrVT, + CurDAG->getTargetConstantPool(CV, PtrVT, 0, 0, + EpiphanyII::MO_HI16), + CurDAG->getTargetConstantPool(CV, PtrVT, 0, 0, + EpiphanyII::MO_LO16), + CurDAG->getConstant(Alignment, MVT::i32)); + + return CurDAG->getExtLoad(Extension, DL, DestType, CurDAG->getEntryNode(), + PoolAddr, + MachinePointerInfo::getConstantPool(), MemType, + /* isVolatile = */ false, + /* isNonTemporal = */ false, + Alignment).getNode(); +} + +SDNode *EpiphanyDAGToDAGISel::LowerToFPLitPool(SDNode *Node) { + DebugLoc DL = Node->getDebugLoc(); + const ConstantFP *FV = cast(Node)->getConstantFPValue(); + EVT PtrVT = TLI.getPointerTy(); + EVT DestType = Node->getValueType(0); + + unsigned Alignment = TLI.getDataLayout()->getABITypeAlignment(FV->getType()); + SDValue PoolAddr; + + assert(TM.getCodeModel() == CodeModel::Small && + "Only small code model supported"); + PoolAddr = CurDAG->getNode(EpiphanyISD::WrapperSmall, DL, PtrVT, + CurDAG->getTargetConstantPool(FV, PtrVT, 0, 0, + EpiphanyII::MO_HI16), + CurDAG->getTargetConstantPool(FV, PtrVT, 0, 0, + EpiphanyII::MO_LO16), + CurDAG->getConstant(Alignment, MVT::i32)); + + return CurDAG->getLoad(DestType, DL, CurDAG->getEntryNode(), PoolAddr, + MachinePointerInfo::getConstantPool(), + /* isVolatile = */ false, + /* isNonTemporal = */ false, + /* isInvariant = */ true, + Alignment).getNode(); +} + +SDNode *EpiphanyDAGToDAGISel::Select(SDNode *Node) { + // Dump information about the Node being selected + DEBUG(dbgs() << "Selecting: "; Node->dump(CurDAG); dbgs() << "\n"); + + if (Node->isMachineOpcode()) { + DEBUG(dbgs() << "== "; Node->dump(CurDAG); dbgs() << "\n"); + return NULL; + } + + switch (Node->getOpcode()) { + case ISD::FrameIndex: { + int FI = cast(Node)->getIndex(); + EVT PtrTy = TLI.getPointerTy(); + SDValue TFI = CurDAG->getTargetFrameIndex(FI, PtrTy); + return CurDAG->SelectNodeTo(Node, Epiphany::ADDri, PtrTy, + TFI, CurDAG->getTargetConstant(0, PtrTy)); + } + case ISD::ConstantPool: { + // Constant pools are fine, just create a Target entry. + ConstantPoolSDNode *CN = cast(Node); + const Constant *C = CN->getConstVal(); + SDValue CP = CurDAG->getTargetConstantPool(C, CN->getValueType(0)); + + ReplaceUses(SDValue(Node, 0), CP); + return NULL; + } + case ISD::Constant: { + SDNode *ResNode = 0; + ResNode = TrySelectToMoveImm(Node); + if (ResNode) + return ResNode; + + llvm_unreachable("contant fail."); + //ResNode = SelectToLitPool(Node); + //assert(ResNode && "We need *some* way to materialise a constant"); + + //// We want to continue selection at this point since the litpool access + //// generated used generic nodes for simplicity. + //ReplaceUses(SDValue(Node, 0), SDValue(ResNode, 0)); + //Node = ResNode; + //break; + } + case ISD::ConstantFP: { + //SDNode *ResNode = LowerToFPLitPool(Node); + //ReplaceUses(SDValue(Node, 0), SDValue(ResNode, 0)); + + //// We want to continue selection at this point since the litpool access + //// generated used generic nodes for simplicity. + //Node = ResNode; + //break; + return TrySelectToMoveImm(Node); + } + default: + break; // Let generic code handle it + } + + SDNode *ResNode = SelectCode(Node); + + DEBUG(dbgs() << "=> "; + if (ResNode == NULL || ResNode == Node) + Node->dump(CurDAG); + else + ResNode->dump(CurDAG); + dbgs() << "\n"); + + return ResNode; +} + +/// This pass converts a legalized DAG into a Epiphany-specific DAG, ready for +/// instruction scheduling. +FunctionPass *llvm::createEpiphanyISelDAG(EpiphanyTargetMachine &TM, + CodeGenOpt::Level OptLevel) { + return new EpiphanyDAGToDAGISel(TM, OptLevel); +} diff --git a/EpiphanyISelLowering.cpp b/EpiphanyISelLowering.cpp new file mode 100644 index 0000000..2d365e0 --- /dev/null +++ b/EpiphanyISelLowering.cpp @@ -0,0 +1,1074 @@ +//===-- EpiphanyISelLowering.cpp - Epiphany 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 Epiphany uses to lower LLVM code into a +// selection DAG. +// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "epiphany-isel" +#include "Epiphany.h" +#include "EpiphanyISelLowering.h" +#include "EpiphanyMachineFunctionInfo.h" +#include "EpiphanyTargetMachine.h" +#include "EpiphanyTargetObjectFile.h" +#include "Utils/EpiphanyBaseInfo.h" +#include "llvm/CodeGen/Analysis.h" +#include "llvm/CodeGen/CallingConvLower.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" +#include "llvm/IR/CallingConv.h" + +using namespace llvm; + +static TargetLoweringObjectFile *createTLOF(EpiphanyTargetMachine &TM) { + const EpiphanySubtarget *Subtarget = &TM.getSubtarget(); + + return new EpiphanyLinuxTargetObjectFile(); +} + + +EpiphanyTargetLowering::EpiphanyTargetLowering(EpiphanyTargetMachine &TM) + : TargetLowering(TM, createTLOF(TM)), + Subtarget(&TM.getSubtarget()), + RegInfo(TM.getRegisterInfo()), + Itins(TM.getInstrItineraryData()) { + + // Scalar register <-> type mapping + addRegisterClass(MVT::i32, &Epiphany::GPR32RegClass); + addRegisterClass(MVT::f32, &Epiphany::FPR32RegClass); + + // we do have a reg class, but the only supported operations are loads and stores + //addRegisterClass(MVT::i64, &Epiphany::DPR64RegClass); + //addRegisterClass(MVT::f64, &Epiphany::DPR64RegClass); + + computeRegisterProperties(); + + setTargetDAGCombine(ISD::FADD); + setTargetDAGCombine(ISD::FSUB); + + // Epiphany does not have i1 loads, or much of anything for i1 really. + setLoadExtAction(ISD::SEXTLOAD, MVT::i1, Promote); + setLoadExtAction(ISD::ZEXTLOAD, MVT::i1, Promote); + setLoadExtAction(ISD::EXTLOAD, MVT::i1, Promote); + + setStackPointerRegisterToSaveRestore(Epiphany::SP); + setOperationAction(ISD::DYNAMIC_STACKALLOC, MVT::i32, Expand); + setOperationAction(ISD::STACKRESTORE, MVT::Other, Expand); + setOperationAction(ISD::STACKSAVE, MVT::Other, Expand); + + // We'll lower globals to wrappers for selection. + setOperationAction(ISD::GlobalAddress, MVT::i32, Custom); + + // A64 instructions have the comparison predicate attached to the user of the + // result, but having a separate comparison is valuable for matching. + setOperationAction(ISD::BR_CC, MVT::i32, Custom); + setOperationAction(ISD::BR_CC, MVT::f32, Custom); + setOperationAction(ISD::SELECT, MVT::i32, Custom); + setOperationAction(ISD::SELECT, MVT::f32, Custom); + setOperationAction(ISD::SELECT_CC, MVT::i32, Custom); + setOperationAction(ISD::SELECT_CC, MVT::f32, Custom); + setOperationAction(ISD::BRCOND, MVT::Other, Custom); + setOperationAction(ISD::SETCC, MVT::i32, Custom); + setOperationAction(ISD::SETCC, MVT::f32, Custom); + setOperationAction(ISD::BR_JT, MVT::Other, Expand); + setOperationAction(ISD::JumpTable, MVT::i32, Custom); + setOperationAction(ISD::VASTART, MVT::Other, Expand); + setOperationAction(ISD::VACOPY, MVT::Other, Expand); + setOperationAction(ISD::VAEND, MVT::Other, Expand); + setOperationAction(ISD::VAARG, MVT::Other, Expand); + setOperationAction(ISD::BlockAddress, MVT::i32, Custom); + setOperationAction(ISD::ROTL, MVT::i32, Expand); + setOperationAction(ISD::UREM, MVT::i32, Expand); + setOperationAction(ISD::UDIVREM, MVT::i32, Expand); + setOperationAction(ISD::SREM, MVT::i32, Expand); + setOperationAction(ISD::SDIVREM, MVT::i32, Expand); + setOperationAction(ISD::SDIV, MVT::i32, Expand); + setOperationAction(ISD::UDIV, MVT::i32, Expand); + setOperationAction(ISD::CTPOP, MVT::i32, Expand); + + setOperationAction(ISD::FNEG, MVT::f32, Custom); + + // Legal floating-point operations. + setOperationAction(ISD::FMUL, MVT::f32, Legal); + setOperationAction(ISD::FADD, MVT::f32, Legal); + setOperationAction(ISD::FSUB, MVT::f32, Legal); + + + setOperationAction(ISD::FABS, MVT::f32, Legal); + setOperationAction(ISD::ConstantFP, MVT::f32, Legal); + + //setOperationAction(ISD::FMA, MVT::f32, Expand); custom combine due to fneg, we have fmsub + + // Illegal floating-point operations. + setOperationAction(ISD::FDIV, MVT::f32, Expand); + setOperationAction(ISD::FCOPYSIGN, MVT::f32, Expand); + setOperationAction(ISD::FCOS, MVT::f32, Expand); + setOperationAction(ISD::FEXP, MVT::f32, Expand); + setOperationAction(ISD::FEXP2, MVT::f32, Expand); + setOperationAction(ISD::FLOG, MVT::f32, Expand); + setOperationAction(ISD::FLOG2, MVT::f32, Expand); + setOperationAction(ISD::FLOG10, MVT::f32, Expand); + setOperationAction(ISD::FPOW, MVT::f32, Expand); + setOperationAction(ISD::FPOWI, MVT::f32, Expand); + setOperationAction(ISD::FREM, MVT::f32, Expand); + setOperationAction(ISD::FSIN, MVT::f32, Expand); + + +//fix/float + setOperationAction(ISD::FP_TO_SINT, MVT::i32, Legal); + setOperationAction(ISD::SINT_TO_FP, MVT::i32, Legal); + + } + +EVT EpiphanyTargetLowering::getSetCCResultType(EVT VT) const { + // It's reasonably important that this value matches the "natural" legal + // promotion from i1 for scalar types. Otherwise LegalizeTypes can get itself + // in a twist (e.g. inserting an any_extend which then becomes i32 -> i32). + if (!VT.isVector()) return MVT::i32; + return VT.changeVectorElementTypeToInteger(); +} + +MachineBasicBlock * +EpiphanyTargetLowering::EmitInstrWithCustomInserter(MachineInstr *MI, + MachineBasicBlock *MBB) const { + switch (MI->getOpcode()) { + default: llvm_unreachable("Unhandled instruction with custom inserter"); + //case Epiphany::F128CSEL: + // return EmitF128CSEL(MI, MBB); + } +} + + +const char *EpiphanyTargetLowering::getTargetNodeName(unsigned Opcode) const { + switch (Opcode) { + case EpiphanyISD::BR_CC: return "EpiphanyISD::BR_CC"; + case EpiphanyISD::Call: return "EpiphanyISD::Call"; + case EpiphanyISD::Ret: return "EpiphanyISD::Ret"; + case EpiphanyISD::SELECT_CC: return "EpiphanyISD::SELECT_CC"; + case EpiphanyISD::SETCC: return "EpiphanyISD::SETCC"; + case EpiphanyISD::WrapperSmall: return "EpiphanyISD::WrapperSmall"; + case EpiphanyISD::FM_A_S: return "EpiphanyISD::FM_A_S"; + + default: return NULL; + } +} + +static const uint16_t EpiphanyArgRegs[] = { + Epiphany::R0, Epiphany::R1, Epiphany::R2, Epiphany::R3 +}; +static const unsigned NumArgRegs = llvm::array_lengthof(EpiphanyArgRegs); + +#include "EpiphanyGenCallingConv.inc" + +CCAssignFn *EpiphanyTargetLowering::CCAssignFnForNode(CallingConv::ID CC) const { + + switch(CC) { + default: llvm_unreachable("Unsupported calling convention"); + case CallingConv::C: + return CC_A64_APCS; + } +} + +void +EpiphanyTargetLowering::SaveVarArgRegisters(CCState &CCInfo, SelectionDAG &DAG, + DebugLoc DL, SDValue &Chain) const { + MachineFunction &MF = DAG.getMachineFunction(); + MachineFrameInfo *MFI = MF.getFrameInfo(); + EpiphanyMachineFunctionInfo *FuncInfo + = MF.getInfo(); + + SmallVector MemOps; + + unsigned FirstVariadicGPR = CCInfo.getFirstUnallocated(EpiphanyArgRegs, + NumArgRegs); + + unsigned GPRSaveSize = 4 * (NumArgRegs - FirstVariadicGPR); + int GPRIdx = 0; + if (GPRSaveSize != 0) { + GPRIdx = MFI->CreateStackObject(GPRSaveSize, 4, false); + + SDValue FIN = DAG.getFrameIndex(GPRIdx, getPointerTy()); + + for (unsigned i = FirstVariadicGPR; i < NumArgRegs; ++i) { + unsigned VReg = MF.addLiveIn(EpiphanyArgRegs[i], &Epiphany::GPR32RegClass); + SDValue Val = DAG.getCopyFromReg(Chain, DL, VReg, MVT::i32); + SDValue Store = DAG.getStore(Val.getValue(1), DL, Val, FIN, + MachinePointerInfo::getStack(i * 4), + false, false, 0); + MemOps.push_back(Store); + FIN = DAG.getNode(ISD::ADD, DL, getPointerTy(), FIN, + DAG.getConstant(4, getPointerTy())); + } + } + + int StackIdx = MFI->CreateFixedObject(4, CCInfo.getNextStackOffset(), true); + + FuncInfo->setVariadicStackIdx(StackIdx); + FuncInfo->setVariadicGPRIdx(GPRIdx); + FuncInfo->setVariadicGPRSize(GPRSaveSize); + FuncInfo->setVariadicFPRIdx(0); + FuncInfo->setVariadicFPRSize(0); + + if (!MemOps.empty()) { + Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, &MemOps[0], + MemOps.size()); + } +} + + +SDValue +EpiphanyTargetLowering::LowerFormalArguments(SDValue Chain, + CallingConv::ID CallConv, bool isVarArg, + const SmallVectorImpl &Ins, + DebugLoc dl, SelectionDAG &DAG, + SmallVectorImpl &InVals) const { + MachineFunction &MF = DAG.getMachineFunction(); + EpiphanyMachineFunctionInfo *FuncInfo = MF.getInfo(); + MachineFrameInfo *MFI = MF.getFrameInfo(); + //bool TailCallOpt = MF.getTarget().Options.GuaranteedTailCallOpt; + + SmallVector ArgLocs; + CCState CCInfo(CallConv, isVarArg, DAG.getMachineFunction(), getTargetMachine(), ArgLocs, *DAG.getContext()); + CCInfo.AnalyzeFormalArguments(Ins, CCAssignFnForNode(CallConv)); + + SmallVector ArgValues; + + SDValue ArgValue; + for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) { + CCValAssign &VA = ArgLocs[i]; + + if (VA.isRegLoc()) { + MVT RegVT = VA.getLocVT(); + const TargetRegisterClass *RC = getRegClassFor(RegVT); + unsigned Reg = MF.addLiveIn(VA.getLocReg(), RC); + ArgValue = DAG.getCopyFromReg(Chain, dl, Reg, RegVT); + + switch (VA.getLocInfo()) { + default: llvm_unreachable("Unknown loc info!"); + case CCValAssign::Full: break; + case CCValAssign::BCvt: + ArgValue = DAG.getNode(ISD::BITCAST, dl, VA.getValVT(), ArgValue); + break; + case CCValAssign::SExt: + ArgValue = DAG.getNode(ISD::AssertSext, dl, RegVT, ArgValue, DAG.getValueType(VA.getValVT())); + ArgValue = DAG.getNode(ISD::TRUNCATE, dl, VA.getValVT(), ArgValue); + break; + case CCValAssign::ZExt: + ArgValue = DAG.getNode(ISD::AssertZext, dl, RegVT, ArgValue, DAG.getValueType(VA.getValVT())); + ArgValue = DAG.getNode(ISD::TRUNCATE, dl, VA.getValVT(), ArgValue); + break; + } + + } else { // VA.isRegLoc() + assert(VA.isMemLoc()); + // hm. byval? + int FI = MFI->CreateFixedObject(VA.getLocVT().getSizeInBits()/8, VA.getLocMemOffset(), true); + SDValue FIN = DAG.getFrameIndex(FI, getPointerTy()); + ArgValue = DAG.getLoad(VA.getLocVT(), dl, Chain, FIN, MachinePointerInfo::getFixedStack(FI),false, false, false, 0); + } + + InVals.push_back(ArgValue); + } + + if (isVarArg) + SaveVarArgRegisters(CCInfo, DAG, dl, Chain); + + unsigned StackArgSize = CCInfo.getNextStackOffset(); + // Even if we're not expected to free up the space, it's useful to know how + // much is there while considering tail calls (because we can reuse it). + FuncInfo->setBytesInStackArgArea(StackArgSize); + + return Chain; +} + +SDValue +EpiphanyTargetLowering::LowerFNEG(SDValue Op, SelectionDAG &DAG) const{ + DebugLoc dl = Op.getDebugLoc(); + SDValue Operand0 = Op.getOperand(0); + EVT VT = Op.getValueType(); + + // y = fneg(x) -> xor rd, 0x80000000 + SDValue Val = DAG.getConstant(0x80000000, MVT::i32); + SDValue Arg = DAG.getNode(ISD::BITCAST, dl, MVT::i32, Operand0); + SDValue Xor = DAG.getNode(ISD::XOR, dl, MVT::i32, Arg, Val); + return DAG.getNode(ISD::BITCAST, dl, VT, Xor); +} + +SDValue +EpiphanyTargetLowering::LowerReturn(SDValue Chain, + CallingConv::ID CallConv, bool isVarArg, + const SmallVectorImpl &Outs, + const SmallVectorImpl &OutVals, + DebugLoc dl, SelectionDAG &DAG) const { + // CCValAssign - represent the assignment of the return value to a location. + SmallVector RVLocs; + + // CCState - Info about the registers and stack slots. + CCState CCInfo(CallConv, isVarArg, DAG.getMachineFunction(), + getTargetMachine(), RVLocs, *DAG.getContext()); + + // Analyze outgoing return values. + CCInfo.AnalyzeReturn(Outs, CCAssignFnForNode(CallConv)); + + SDValue Flag; + SmallVector RetOps(1, Chain); + + for (unsigned i = 0, e = RVLocs.size(); i != e; ++i) { + // PCS: "If the type, T, of the result of a function is such that + // void func(T arg) would require that arg be passed as a value in a + // register (or set of registers) according to the rules in 5.4, then the + // result is returned in the same registers as would be used for such an + // argument. + // + // Otherwise, the caller shall reserve a block of memory of sufficient + // size and alignment to hold the result. The address of the memory block + // shall be passed as an additional argument to the function in x8." + // + // This is implemented in two places. The register-return values are dealt + // with here, more complex returns are passed as an sret parameter, which + // means we don't have to worry about it during actual return. + CCValAssign &VA = RVLocs[i]; + assert(VA.isRegLoc() && "Only register-returns should be created by PCS"); + + + SDValue Arg = OutVals[i]; + + // There's no convenient note in the ABI about this as there is for normal + // arguments, but it says return values are passed in the same registers as + // an argument would be. I believe that includes the comments about + // unspecified higher bits, putting the burden of widening on the *caller* + // for return values. + switch (VA.getLocInfo()) { + default: llvm_unreachable("Unknown loc info"); + case CCValAssign::Full: break; + case CCValAssign::SExt: + case CCValAssign::ZExt: + case CCValAssign::AExt: + // Floating-point values should only be extended when they're going into + // memory, which can't happen here so an integer extend is acceptable. + Arg = DAG.getNode(ISD::ANY_EXTEND, dl, VA.getLocVT(), Arg); + break; + case CCValAssign::BCvt: + Arg = DAG.getNode(ISD::BITCAST, dl, VA.getLocVT(), Arg); + break; + } + + Chain = DAG.getCopyToReg(Chain, dl, VA.getLocReg(), Arg, Flag); + Flag = Chain.getValue(1); + RetOps.push_back(DAG.getRegister(VA.getLocReg(), VA.getLocVT())); + } + + RetOps[0] = Chain; // Update chain. + + // Add the flag if we have it. + if (Flag.getNode()) + RetOps.push_back(Flag); + + return DAG.getNode(EpiphanyISD::Ret, dl, MVT::Other, + &RetOps[0], RetOps.size()); +} + +SDValue +EpiphanyTargetLowering::LowerCall(CallLoweringInfo &CLI, SmallVectorImpl &InVals) const { + SelectionDAG &DAG = CLI.DAG; + DebugLoc &dl = CLI.DL; + SmallVector &Outs = CLI.Outs; + SmallVector &OutVals = CLI.OutVals; + SmallVector &Ins = CLI.Ins; + SDValue Chain = CLI.Chain; + SDValue Callee = CLI.Callee; + bool &IsTailCall = CLI.IsTailCall; + CallingConv::ID CallConv = CLI.CallConv; + bool IsVarArg = CLI.IsVarArg; + + MachineFunction &MF = DAG.getMachineFunction(); + EpiphanyMachineFunctionInfo *FuncInfo = MF.getInfo(); + //bool TailCallOpt = MF.getTarget().Options.GuaranteedTailCallOpt; + bool IsStructRet = !Outs.empty() && Outs[0].Flags.isSRet(); + + + if (IsTailCall) { + IsTailCall = IsEligibleForTailCallOptimization(Callee, CallConv, IsVarArg, IsStructRet, MF.getFunction()->hasStructRetAttr(), Outs, OutVals, Ins, DAG); + } + + SmallVector ArgLocs; + CCState CCInfo(CallConv, IsVarArg, DAG.getMachineFunction(), getTargetMachine(), ArgLocs, *DAG.getContext()); + CCInfo.AnalyzeCallOperands(Outs, CCAssignFnForNode(CallConv)); + + // On Epiphany (and all other architectures I'm aware of) the most this has to + // do is adjust the stack pointer. + unsigned NumBytes = RoundUpToAlignment(CCInfo.getNextStackOffset(), 4); + + Chain = DAG.getCALLSEQ_START(Chain, DAG.getIntPtrConstant(NumBytes, true)); + + SDValue StackPtr = DAG.getCopyFromReg(Chain, dl, Epiphany::SP, getPointerTy()); + + SmallVector MemOpChains; + SmallVector, 8> RegsToPass; + + for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) { + CCValAssign &VA = ArgLocs[i]; + ISD::ArgFlagsTy Flags = Outs[i].Flags; + SDValue Arg = OutVals[i]; + + // Callee does the actual widening, so all extensions just use an implicit + // definition of the rest of the Loc. Aesthetically, this would be nicer as + // an ANY_EXTEND, but that isn't valid for floating-point types and this + // alternative works on integer types too. + switch (VA.getLocInfo()) { + default: llvm_unreachable("Unknown loc info!"); + case CCValAssign::Full: break; + case CCValAssign::SExt: + case CCValAssign::ZExt: + case CCValAssign::AExt: { + // Floating-point arguments only get extended/truncated if they're going + // in memory, so using the integer operation is acceptable here. + Arg = DAG.getNode(ISD::TRUNCATE, dl, VA.getValVT(), Arg); + break; + } + case CCValAssign::BCvt: + Arg = DAG.getNode(ISD::BITCAST, dl, VA.getLocVT(), Arg); + break; + } + + if (VA.isRegLoc()) { + // A normal register (sub-) argument. For now we just note it down because + // we want to copy things into registers as late as possible to avoid + // register-pressure (and possibly worse). + RegsToPass.push_back(std::make_pair(VA.getLocReg(), Arg)); + continue; + } + + assert(VA.isMemLoc() && "unexpected argument location"); + + SDValue PtrOff = DAG.getIntPtrConstant(VA.getLocMemOffset()); + SDValue DstAddr = DAG.getNode(ISD::ADD, dl, getPointerTy(), StackPtr, PtrOff); + MachinePointerInfo DstInfo = MachinePointerInfo::getStack(VA.getLocMemOffset()); + + + if (Flags.isByVal()) { + SDValue SizeNode = DAG.getConstant(Flags.getByValSize(), MVT::i32); + SDValue Cpy = DAG.getMemcpy(Chain, dl, DstAddr, Arg, SizeNode, + Flags.getByValAlign(), + /*isVolatile = */ false, + /*alwaysInline = */ false, + DstInfo, MachinePointerInfo(0)); + MemOpChains.push_back(Cpy); + } else { + // Normal stack argument, put it where it's needed. + SDValue Store = DAG.getStore(Chain, dl, Arg, DstAddr, DstInfo, false, false, 0); + MemOpChains.push_back(Store); + } + } + + // The loads and stores generated above shouldn't clash with each + // other. Combining them with this TokenFactor notes that fact for the rest of + // the backend. + if (!MemOpChains.empty()) + Chain = DAG.getNode(ISD::TokenFactor, dl, MVT::Other, &MemOpChains[0], MemOpChains.size()); + + // Most of the rest of the instructions need to be glued together; we don't + // want assignments to actual registers used by a call to be rearranged by a + // well-meaning scheduler. + SDValue InFlag; + + for (unsigned i = 0, e = RegsToPass.size(); i != e; ++i) { + Chain = DAG.getCopyToReg(Chain, dl, RegsToPass[i].first, RegsToPass[i].second, InFlag); + InFlag = Chain.getValue(1); + } + + // The linker is responsible for inserting veneers when necessary to put a + // function call destination in range, so we don't need to bother with a + // wrapper here. + if (GlobalAddressSDNode *G = dyn_cast(Callee)) { + const GlobalValue *GV = G->getGlobal(); + Callee = DAG.getTargetGlobalAddress(GV, dl, getPointerTy()); + } else if (ExternalSymbolSDNode *S = dyn_cast(Callee)) { + const char *Sym = S->getSymbol(); + Callee = DAG.getTargetExternalSymbol(Sym, getPointerTy()); + } + + // We produce the following DAG scheme for the actual call instruction: + // (EpiphanyCall Chain, Callee, reg1, ..., regn, preserveMask, inflag? + // + // Most arguments aren't going to be used and just keep the values live as + // far as LLVM is concerned. It's expected to be selected as simply "bl + // callee" (for a direct, non-tail call). + std::vector Ops; + Ops.push_back(Chain); + Ops.push_back(Callee); + + + for (unsigned i = 0, e = RegsToPass.size(); i != e; ++i) + Ops.push_back(DAG.getRegister(RegsToPass[i].first, RegsToPass[i].second.getValueType())); + + // Add a register mask operand representing the call-preserved registers. This + // is used later in codegen to constrain register-allocation. + const TargetRegisterInfo *TRI = getTargetMachine().getRegisterInfo(); + const uint32_t *Mask = TRI->getCallPreservedMask(CallConv); + assert(Mask && "Missing call preserved mask for calling convention"); + Ops.push_back(DAG.getRegisterMask(Mask)); + + // If we needed glue, put it in as the last argument. + if (InFlag.getNode()) + Ops.push_back(InFlag); + + SDVTList NodeTys = DAG.getVTList(MVT::Other, MVT::Glue); + + Chain = DAG.getNode(EpiphanyISD::Call, dl, NodeTys, &Ops[0], Ops.size()); + InFlag = Chain.getValue(1); + + // Now we can reclaim the stack, just as well do it before working out where + // our return value is. + + uint64_t CalleePopBytes = /*DoesCalleeRestoreStack(CallConv, TailCallOpt) ? NumBytes :*/ 0; + + Chain = DAG.getCALLSEQ_END(Chain, DAG.getIntPtrConstant(NumBytes, true), + DAG.getIntPtrConstant(CalleePopBytes, true), + InFlag); + InFlag = Chain.getValue(1); + + + return LowerCallResult(Chain, InFlag, CallConv, IsVarArg, Ins, dl, DAG, InVals); +} + +SDValue +EpiphanyTargetLowering::LowerCallResult(SDValue Chain, SDValue InFlag, + CallingConv::ID CallConv, bool IsVarArg, + const SmallVectorImpl &Ins, + DebugLoc dl, SelectionDAG &DAG, + SmallVectorImpl &InVals) const { + // Assign locations to each value returned by this call. + SmallVector RVLocs; + CCState CCInfo(CallConv, IsVarArg, DAG.getMachineFunction(), + getTargetMachine(), RVLocs, *DAG.getContext()); + CCInfo.AnalyzeCallResult(Ins, CCAssignFnForNode(CallConv)); + + for (unsigned i = 0; i != RVLocs.size(); ++i) { + CCValAssign VA = RVLocs[i]; + + // Return values that are too big to fit into registers should use an sret + // pointer, so this can be a lot simpler than the main argument code. + assert(VA.isRegLoc() && "Memory locations not expected for call return"); + + SDValue Val = DAG.getCopyFromReg(Chain, dl, VA.getLocReg(), VA.getLocVT(), + InFlag); + Chain = Val.getValue(1); + InFlag = Val.getValue(2); + + switch (VA.getLocInfo()) { + default: llvm_unreachable("Unknown loc info!"); + case CCValAssign::Full: break; + case CCValAssign::BCvt: + Val = DAG.getNode(ISD::BITCAST, dl, VA.getValVT(), Val); + break; + case CCValAssign::ZExt: + case CCValAssign::SExt: + case CCValAssign::AExt: + // Floating-point arguments only get extended/truncated if they're going + // in memory, so using the integer operation is acceptable here. + Val = DAG.getNode(ISD::TRUNCATE, dl, VA.getValVT(), Val); + break; + } + + InVals.push_back(Val); + } + + return Chain; +} + + +SDValue EpiphanyTargetLowering::addTokenForArgument(SDValue Chain, + SelectionDAG &DAG, + MachineFrameInfo *MFI, + int ClobberedFI) const { + SmallVector ArgChains; + int64_t FirstByte = MFI->getObjectOffset(ClobberedFI); + int64_t LastByte = FirstByte + MFI->getObjectSize(ClobberedFI) - 1; + + // Include the original chain at the beginning of the list. When this is + // used by target LowerCall hooks, this helps legalize find the + // CALLSEQ_BEGIN node. + ArgChains.push_back(Chain); + + // Add a chain value for each stack argument corresponding + for (SDNode::use_iterator U = DAG.getEntryNode().getNode()->use_begin(), + UE = DAG.getEntryNode().getNode()->use_end(); U != UE; ++U) + if (LoadSDNode *L = dyn_cast(*U)) + if (FrameIndexSDNode *FI = dyn_cast(L->getBasePtr())) + if (FI->getIndex() < 0) { + int64_t InFirstByte = MFI->getObjectOffset(FI->getIndex()); + int64_t InLastByte = InFirstByte; + InLastByte += MFI->getObjectSize(FI->getIndex()) - 1; + + if ((InFirstByte <= FirstByte && FirstByte <= InLastByte) || + (FirstByte <= InFirstByte && InFirstByte <= LastByte)) + ArgChains.push_back(SDValue(L, 1)); + } + + // Build a tokenfactor for all the chains. + return DAG.getNode(ISD::TokenFactor, Chain.getDebugLoc(), MVT::Other, + &ArgChains[0], ArgChains.size()); +} + +static EpiphanyCC::CondCodes IntCCToEpiphanyCC(ISD::CondCode CC) { + switch (CC) { + case ISD::SETEQ: return EpiphanyCC::EQ; + case ISD::SETGT: return EpiphanyCC::GT; + case ISD::SETGE: return EpiphanyCC::GTE; + case ISD::SETLT: return EpiphanyCC::LT; + case ISD::SETLE: return EpiphanyCC::LTE; + case ISD::SETNE: return EpiphanyCC::NE; + case ISD::SETUGT: return EpiphanyCC::GTU; + case ISD::SETUGE: return EpiphanyCC::GTEU; + case ISD::SETULT: return EpiphanyCC::LTU; + case ISD::SETULE: return EpiphanyCC::LTEU; + default: llvm_unreachable("Unexpected condition code"); + } +} + +bool EpiphanyTargetLowering::isLegalICmpImmediate(int64_t Val) const { + // icmp is implemented using adds/subs immediate with a 11bit signed imm + // Symmetric by using adds/subs + if (Val < 0) + Val = -Val; + + // 11bit imm -> 10 bit unsigned + 1 sign bit + return (Val & ~0x3FF) == 0; +} + +SDValue EpiphanyTargetLowering::getSelectableIntSetCC(SDValue LHS, SDValue RHS, + ISD::CondCode CC, SDValue &A64cc, + SelectionDAG &DAG, DebugLoc &dl) const { + if (ConstantSDNode *RHSC = dyn_cast(RHS.getNode())) { + int64_t C = 0; + EVT VT = RHSC->getValueType(0); + bool knownInvalid = false; + + // I'm not convinced the rest of LLVM handles these edge cases properly, but + // we can at least get it right. + if (isSignedIntSetCC(CC)) { + C = RHSC->getSExtValue(); + } else if (RHSC->getZExtValue() > INT64_MAX) { + // A 64-bit constant not representable by a signed 64-bit integer is far + // too big to fit into a SUBS immediate anyway. + knownInvalid = true; + } else { + C = RHSC->getZExtValue(); + } + + if (!knownInvalid && !isLegalICmpImmediate(C)) { + // Constant does not fit, try adjusting it by one? + switch (CC) { + default: break; + case ISD::SETLT: + case ISD::SETGE: + if (isLegalICmpImmediate(C-1)) { + CC = (CC == ISD::SETLT) ? ISD::SETLE : ISD::SETGT; + RHS = DAG.getConstant(C-1, VT); + } + break; + case ISD::SETULT: + case ISD::SETUGE: + if (isLegalICmpImmediate(C-1)) { + CC = (CC == ISD::SETULT) ? ISD::SETULE : ISD::SETUGT; + RHS = DAG.getConstant(C-1, VT); + } + break; + case ISD::SETLE: + case ISD::SETGT: + if (isLegalICmpImmediate(C+1)) { + CC = (CC == ISD::SETLE) ? ISD::SETLT : ISD::SETGE; + RHS = DAG.getConstant(C+1, VT); + } + break; + case ISD::SETULE: + case ISD::SETUGT: + if (isLegalICmpImmediate(C+1)) { + CC = (CC == ISD::SETULE) ? ISD::SETULT : ISD::SETUGE; + RHS = DAG.getConstant(C+1, VT); + } + break; + } + } + } + + EpiphanyCC::CondCodes CondCode = IntCCToEpiphanyCC(CC); + A64cc = DAG.getConstant(CondCode, MVT::i32); + return DAG.getNode(EpiphanyISD::SETCC, dl, MVT::i32, LHS, RHS, + DAG.getCondCode(CC)); +} + +static EpiphanyCC::CondCodes FPCCToEpiphanyCC(ISD::CondCode CC, bool& invert) { + EpiphanyCC::CondCodes CondCode = EpiphanyCC::Invalid; + invert = 0; + + switch (CC) { + default: llvm_unreachable("Unknown FP condition!"); + case ISD::SETEQ: + case ISD::SETOEQ: + case ISD::SETUEQ: CondCode = EpiphanyCC::BEQ; break; + + case ISD::SETNE: + case ISD::SETUNE: + case ISD::SETONE: CondCode = EpiphanyCC::BNE; break; + + case ISD::SETGT: + case ISD::SETOGT: + case ISD::SETUGT: CondCode = EpiphanyCC::BLT; invert = 1; break; + + case ISD::SETGE: + case ISD::SETOGE: + case ISD::SETUGE: CondCode = EpiphanyCC::BLTE; invert = 1; break; + + case ISD::SETLT: + case ISD::SETULT: + case ISD::SETOLT: CondCode = EpiphanyCC::BLT; break; + + case ISD::SETLE: + case ISD::SETULE: + case ISD::SETOLE: CondCode = EpiphanyCC::BLTE; break; + + //case ISD::SETO: + //case ISD::SETUO: ooops. call? + } + return CondCode; +} + +SDValue +EpiphanyTargetLowering::LowerBlockAddress(SDValue Op, SelectionDAG &DAG) const { + DebugLoc DL = Op.getDebugLoc(); + EVT PtrVT = getPointerTy(); + const BlockAddress *BA = cast(Op)->getBlockAddress(); + + assert(getTargetMachine().getCodeModel() == CodeModel::Small + && "Only small code model supported at the moment"); + + // The most efficient code is PC-relative anyway for the small memory model, + // so we don't need to worry about relocation model. + return DAG.getNode(EpiphanyISD::WrapperSmall, DL, PtrVT, + DAG.getTargetBlockAddress(BA, PtrVT, 0, + EpiphanyII::MO_HI16), + DAG.getTargetBlockAddress(BA, PtrVT, 0, + EpiphanyII::MO_LO16), + DAG.getConstant(/*Alignment=*/ 4, MVT::i32)); +} + + +// (BRCOND chain, val, dest) +SDValue +EpiphanyTargetLowering::LowerBRCOND(SDValue Op, SelectionDAG &DAG) const { + DebugLoc dl = Op.getDebugLoc(); + SDValue Chain = Op.getOperand(0); + SDValue TheBit = Op.getOperand(1); + SDValue DestBB = Op.getOperand(2); + + // Epiphany BooleanContents is the default UndefinedBooleanContent, which means + // that as the consumer we are responsible for ignoring rubbish in higher + // bits. + TheBit = DAG.getNode(ISD::AND, dl, MVT::i32, TheBit, + DAG.getConstant(1, MVT::i32)); + + SDValue A64CMP = DAG.getNode(EpiphanyISD::SETCC, dl, MVT::i32, TheBit, + DAG.getConstant(0, TheBit.getValueType()), + DAG.getCondCode(ISD::SETNE)); + + return DAG.getNode(EpiphanyISD::BR_CC, dl, MVT::Other, Chain, + A64CMP, DAG.getConstant(EpiphanyCC::NE, MVT::i32), + DestBB); +} + +// (BR_CC chain, condcode, lhs, rhs, dest) +SDValue +EpiphanyTargetLowering::LowerBR_CC(SDValue Op, SelectionDAG &DAG) const { + DebugLoc dl = Op.getDebugLoc(); + SDValue Chain = Op.getOperand(0); + ISD::CondCode CC = cast(Op.getOperand(1))->get(); + SDValue LHS = Op.getOperand(2); + SDValue RHS = Op.getOperand(3); + SDValue DestBB = Op.getOperand(4); + + if (LHS.getValueType().isInteger()) { + SDValue A64cc; + + // Integers are handled in a separate function because the combinations of + // immediates and tests can get hairy and we may want to fiddle things. + SDValue CmpOp = getSelectableIntSetCC(LHS, RHS, CC, A64cc, DAG, dl); + + return DAG.getNode(EpiphanyISD::BR_CC, dl, MVT::Other, + Chain, CmpOp, A64cc, DestBB); + } + + EpiphanyCC::CondCodes CondCode; + bool invert; + SDValue SetCC; + CondCode = FPCCToEpiphanyCC(CC, invert); + SDValue A64cc = DAG.getConstant(CondCode, MVT::i32); + if(invert) + std::swap(LHS, RHS); + SetCC = DAG.getNode(EpiphanyISD::SETCC, dl, MVT::i32, LHS, RHS, DAG.getCondCode(CC)); + SDValue A64BR_CC = DAG.getNode(EpiphanyISD::BR_CC, dl, MVT::Other, Chain, SetCC, A64cc, DestBB); + + return A64BR_CC; +} + +SDValue +EpiphanyTargetLowering::LowerGlobalAddressELF(SDValue Op, + SelectionDAG &DAG) const { + // TableGen doesn't have easy access to the CodeModel or RelocationModel, so + // we make that distinction here. + + // We support the small memory model for now. + assert(getTargetMachine().getCodeModel() == CodeModel::Small); + + EVT PtrVT = getPointerTy(); + DebugLoc dl = Op.getDebugLoc(); + const GlobalAddressSDNode *GN = cast(Op); + const GlobalValue *GV = GN->getGlobal(); + unsigned Alignment = GV->getAlignment(); + Reloc::Model RelocM = getTargetMachine().getRelocationModel(); + + //DAG.viewGraph(); + //DAG.dump(); + + + if (GV->isWeakForLinker() && RelocM == Reloc::Static) { + // Weak symbols can't use ADRP/ADD pair since they should evaluate to + // zero when undefined. In PIC mode the GOT can take care of this, but in + // absolute mode we use a constant pool load. + SDValue PoolAddr; + PoolAddr = DAG.getNode(EpiphanyISD::WrapperSmall, dl, PtrVT, + DAG.getTargetConstantPool(GV, PtrVT, 0, 0, EpiphanyII::MO_HI16), + DAG.getTargetConstantPool(GV, PtrVT, 0, 0, EpiphanyII::MO_LO16), + DAG.getConstant(4, MVT::i32)); + return DAG.getLoad(PtrVT, dl, DAG.getEntryNode(), PoolAddr, + MachinePointerInfo::getConstantPool(), + /*isVolatile=*/ false, /*isNonTemporal=*/ true, + /*isInvariant=*/ true, 4); + } + + if (Alignment == 0) { + const PointerType *GVPtrTy = cast(GV->getType()); + if (GVPtrTy->getElementType()->isSized()) { + Alignment = getDataLayout()->getABITypeAlignment(GVPtrTy->getElementType()); + } else { + // Be conservative if we can't guess, not that it really matters: + // functions and labels aren't valid for loads, and the methods used to + // actually calculate an address work with any alignment. + Alignment = 1; + } + } + + SDValue GlobalRef = DAG.getNode(EpiphanyISD::WrapperSmall, dl, PtrVT, + DAG.getTargetGlobalAddress(GV, dl, PtrVT, 0, EpiphanyII::MO_HI16), + DAG.getTargetGlobalAddress(GV, dl, PtrVT, 0, EpiphanyII::MO_LO16), + DAG.getConstant(Alignment, MVT::i32)); + + if (GN->getOffset() != 0) + return DAG.getNode(ISD::ADD, dl, PtrVT, GlobalRef, DAG.getConstant(GN->getOffset(), PtrVT)); + + return GlobalRef; +} + +SDValue +EpiphanyTargetLowering::LowerJumpTable(SDValue Op, SelectionDAG &DAG) const { + JumpTableSDNode *JT = cast(Op); + DebugLoc dl = JT->getDebugLoc(); + + // When compiling PIC, jump tables get put in the code section so a static + // relocation-style is acceptable for both cases. + return DAG.getNode(EpiphanyISD::WrapperSmall, dl, getPointerTy(), + DAG.getTargetJumpTable(JT->getIndex(), getPointerTy(), EpiphanyII::MO_HI16), + DAG.getTargetJumpTable(JT->getIndex(), getPointerTy(), EpiphanyII::MO_LO16), + DAG.getConstant(1, MVT::i32)); +} + +// (SELECT_CC lhs, rhs, iftrue, iffalse, condcode) +SDValue +EpiphanyTargetLowering::LowerSELECT_CC(SDValue Op, SelectionDAG &DAG) const { + DebugLoc dl = Op.getDebugLoc(); + SDValue LHS = Op.getOperand(0); + SDValue RHS = Op.getOperand(1); + SDValue IfTrue = Op.getOperand(2); + SDValue IfFalse = Op.getOperand(3); + ISD::CondCode CC = cast(Op.getOperand(4))->get(); + + if (LHS.getValueType().isInteger()) { + SDValue A64cc; + + // Integers are handled in a separate function because the combinations of + // immediates and tests can get hairy and we may want to fiddle things. + SDValue CmpOp = getSelectableIntSetCC(LHS, RHS, CC, A64cc, DAG, dl); + + return DAG.getNode(EpiphanyISD::SELECT_CC, dl, Op.getValueType(), + CmpOp, IfTrue, IfFalse, A64cc); + } + + + EpiphanyCC::CondCodes CondCode; + bool invert; + SDValue SetCC; + CondCode = FPCCToEpiphanyCC(CC, invert); + SDValue A64cc = DAG.getConstant(CondCode, MVT::i32); + if(invert) + std::swap(LHS, RHS); + SetCC = DAG.getNode(EpiphanyISD::SETCC, dl, MVT::i32, LHS, RHS, DAG.getCondCode(CC)); + SDValue A64SELECT_CC = DAG.getNode(EpiphanyISD::SELECT_CC, dl, Op.getValueType(), SetCC, IfTrue, IfFalse, A64cc); + + return A64SELECT_CC; +} + +// (SELECT testbit, iftrue, iffalse) +SDValue +EpiphanyTargetLowering::LowerSELECT(SDValue Op, SelectionDAG &DAG) const { + DebugLoc dl = Op.getDebugLoc(); + SDValue TheBit = Op.getOperand(0); + SDValue IfTrue = Op.getOperand(1); + SDValue IfFalse = Op.getOperand(2); + + // Epiphany BooleanContents is the default UndefinedBooleanContent, which means + // that as the consumer we are responsible for ignoring rubbish in higher + // bits. + TheBit = DAG.getNode(ISD::AND, dl, MVT::i32, TheBit, + DAG.getConstant(1, MVT::i32)); + SDValue A64CMP = DAG.getNode(EpiphanyISD::SETCC, dl, MVT::i32, TheBit, + DAG.getConstant(0, TheBit.getValueType()), + DAG.getCondCode(ISD::SETNE)); + + return DAG.getNode(EpiphanyISD::SELECT_CC, dl, Op.getValueType(), + A64CMP, IfTrue, IfFalse, + DAG.getConstant(EpiphanyCC::NE, MVT::i32)); +} + +// (SETCC lhs, rhs, condcode) +SDValue +EpiphanyTargetLowering::LowerSETCC(SDValue Op, SelectionDAG &DAG) const { + DebugLoc dl = Op.getDebugLoc(); + SDValue LHS = Op.getOperand(0); + SDValue RHS = Op.getOperand(1); + ISD::CondCode CC = cast(Op.getOperand(2))->get(); + EVT VT = Op.getValueType(); + + if (LHS.getValueType().isInteger()) { + SDValue A64cc; + + // Integers are handled in a separate function because the combinations of + // immediates and tests can get hairy and we may want to fiddle things. + SDValue CmpOp = getSelectableIntSetCC(LHS, RHS, CC, A64cc, DAG, dl); + + return DAG.getNode(EpiphanyISD::SELECT_CC, dl, VT, + CmpOp, DAG.getConstant(1, VT), DAG.getConstant(0, VT), + A64cc); + } + + EpiphanyCC::CondCodes CondCode; + bool invert; + SDValue CmpOp; + CondCode = FPCCToEpiphanyCC(CC, invert); + SDValue A64cc = DAG.getConstant(CondCode, MVT::i32); + if(invert) + std::swap(LHS, RHS); + CmpOp = DAG.getNode(EpiphanyISD::SETCC, dl, MVT::i32, LHS, RHS, DAG.getCondCode(CC)); + SDValue A64SELECT_CC = DAG.getNode(EpiphanyISD::SELECT_CC, dl, VT, CmpOp, DAG.getConstant(1, VT), DAG.getConstant(0, VT), A64cc); + + return A64SELECT_CC; +} + +SDValue +EpiphanyTargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const { + switch (Op.getOpcode()) { + default: llvm_unreachable("Don't know how to custom lower this!"); + + case ISD::BlockAddress: return LowerBlockAddress(Op, DAG); + case ISD::BRCOND: return LowerBRCOND(Op, DAG); + case ISD::BR_CC: return LowerBR_CC(Op, DAG); + case ISD::FNEG: return LowerFNEG(Op, DAG); + case ISD::GlobalAddress: return LowerGlobalAddressELF(Op, DAG); + case ISD::JumpTable: return LowerJumpTable(Op, DAG); + case ISD::SELECT: return LowerSELECT(Op, DAG); + case ISD::SELECT_CC: return LowerSELECT_CC(Op, DAG); + case ISD::SETCC: return LowerSETCC(Op, DAG); + } + + return SDValue(); +} + + +SDValue +PerformFSUBCombine(SDNode *N, TargetLowering::DAGCombinerInfo &DCI){ + +SDValue N0 = N->getOperand(0); +SDValue N1 = N->getOperand(1); +EVT VT = N->getValueType(0); +DebugLoc dl = N->getDebugLoc(); +SelectionDAG &DAG = DCI.DAG; + + // FSUB -> FMA combines: + + // fold (fsub (fmul x, y), z) -> (fma x, y, (fneg z)) + if (N0.getOpcode() == ISD::FMUL && N0->hasOneUse()) { + return DAG.getNode(EpiphanyISD::FM_A_S, N->getDebugLoc(), VT, N0.getOperand(0), N0.getOperand(1), N1, DAG.getConstant(1, MVT::i32)); + } + + // fold (fsub x, (fmul y, z)) -> (fma (fneg y), z, x) + // Note: Commutes FSUB operands. + if (N1.getOpcode() == ISD::FMUL && N1->hasOneUse()) { + return DAG.getNode(EpiphanyISD::FM_A_S, N->getDebugLoc(), VT, N1.getOperand(0), N1.getOperand(1), N0, DAG.getConstant(1, MVT::i32)); + } + + // fold (fsub (-(fmul, x, y)), z) -> (fma (fneg x), y, (fneg z)) + //if (N0.getOpcode() == ISD::FNEG && + // N0.getOperand(0).getOpcode() == ISD::FMUL && + // N0->hasOneUse() && N0.getOperand(0).hasOneUse()) { + // SDValue N00 = N0.getOperand(0).getOperand(0); + // SDValue N01 = N0.getOperand(0).getOperand(1); + // return DAG.getNode(ISD::FMA, dl, VT, + // DAG.getNode(ISD::FNEG, dl, VT, N00), N01, + // DAG.getNode(ISD::FNEG, dl, VT, N1)); + //} + return SDValue(); +} + +SDValue +PerformFADDCombine(SDNode *N, TargetLowering::DAGCombinerInfo &DCI){ + +SDValue N0 = N->getOperand(0); +SDValue N1 = N->getOperand(1); +EVT VT = N->getValueType(0); +DebugLoc dl = N->getDebugLoc(); +SelectionDAG &DAG = DCI.DAG; + + // FADD -> FMA combines: + // fold (fadd (fmul x, y), z) -> (fma x, y, z) + if (N0.getOpcode() == ISD::FMUL && N0->hasOneUse()) { + return DAG.getNode(EpiphanyISD::FM_A_S, N->getDebugLoc(), VT, N0.getOperand(0), N0.getOperand(1), N1, DAG.getConstant(0, MVT::i32)); + } + + // fold (fadd x, (fmul y, z)) -> (fma y, z, x) + // Note: Commutes FADD operands. + if (N1.getOpcode() == ISD::FMUL && N1->hasOneUse()) { + return DAG.getNode(EpiphanyISD::FM_A_S, N->getDebugLoc(), VT, N1.getOperand(0), N1.getOperand(1), N0, DAG.getConstant(0, MVT::i32)); + } + + return SDValue(); +} + +SDValue +EpiphanyTargetLowering::PerformDAGCombine(SDNode *N, DAGCombinerInfo &DCI) const { + switch (N->getOpcode()) { + case ISD::FADD: return PerformFADDCombine(N, DCI); + case ISD::FSUB: return PerformFSUBCombine(N, DCI); + default: break; + } + return SDValue(); +} + diff --git a/EpiphanyISelLowering.h b/EpiphanyISelLowering.h new file mode 100644 index 0000000..767ea58 --- /dev/null +++ b/EpiphanyISelLowering.h @@ -0,0 +1,155 @@ +//==-- EpiphanyISelLowering.h - Epiphany 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 Epiphany uses to lower LLVM code into a +// selection DAG. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TARGET_EPIPHANY_ISELLOWERING_H +#define LLVM_TARGET_EPIPHANY_ISELLOWERING_H + +#include "Utils/EpiphanyBaseInfo.h" +#include "llvm/CodeGen/CallingConvLower.h" +#include "llvm/CodeGen/SelectionDAG.h" +#include "llvm/Target/TargetLowering.h" + + +namespace llvm { +namespace EpiphanyISD { + enum NodeType { + // Start the numbering from where ISD NodeType finishes. + FIRST_NUMBER = ISD::BUILTIN_OP_END, + + // This is a conditional branch which also notes the flag needed + // (eq/sgt/...). A64 puts this information on the branches rather than + // compares as LLVM does. + BR_CC, + + // A node to be selected to an actual call operation: either BL or BLR in + // the absence of tail calls. + Call, + + // Simply a convenient node inserted during ISelLowering to represent + // procedure return. Will almost certainly be selected to "RET". + Ret, + + /// This is an A64-ification of the standard LLVM SELECT_CC operation. The + /// main difference is that it only has the values and an A64 condition, + /// which will be produced by a setcc instruction. + SELECT_CC, + + /// This serves most of the functions of the LLVM SETCC instruction, for two + /// purposes. First, it prevents optimisations from fiddling with the + /// compare after we've moved the CondCode information onto the SELECT_CC or + /// BR_CC instructions. Second, it gives a legal instruction for the actual + /// comparison. + /// + /// It keeps a record of the condition flags asked for because certain + /// instructions are only valid for a subset of condition codes. + SETCC, + + // Wraps an address which the ISelLowering phase has decided should be + // created using the small absolute memory model: i.e. adrp/add or + // adrp/mem-op. This exists to prevent bare TargetAddresses which may never + // get selected. + WrapperSmall, + + // Node for FMA and FMS + FM_A_S + }; +} + + +class EpiphanySubtarget; +class EpiphanyTargetMachine; + +class EpiphanyTargetLowering : public TargetLowering { +public: + explicit EpiphanyTargetLowering(EpiphanyTargetMachine &TM); + + const char *getTargetNodeName(unsigned Opcode) const; + + CCAssignFn *CCAssignFnForNode(CallingConv::ID CC) const; + + SDValue LowerFormalArguments(SDValue Chain, + CallingConv::ID CallConv, bool isVarArg, + const SmallVectorImpl &Ins, + DebugLoc dl, SelectionDAG &DAG, + SmallVectorImpl &InVals) const; + + SDValue LowerReturn(SDValue Chain, + CallingConv::ID CallConv, bool isVarArg, + const SmallVectorImpl &Outs, + const SmallVectorImpl &OutVals, + DebugLoc dl, SelectionDAG &DAG) const; + + SDValue LowerCall(CallLoweringInfo &CLI, + SmallVectorImpl &InVals) const; + + SDValue LowerCallResult(SDValue Chain, SDValue InFlag, + CallingConv::ID CallConv, bool IsVarArg, + const SmallVectorImpl &Ins, + DebugLoc dl, SelectionDAG &DAG, + SmallVectorImpl &InVals) const; + + void SaveVarArgRegisters(CCState &CCInfo, SelectionDAG &DAG, + DebugLoc DL, SDValue &Chain) const; + + + /// Finds the incoming stack arguments which overlap the given fixed stack + /// object and incorporates their load into the current chain. This prevents + /// an upcoming store from clobbering the stack argument before it's used. + SDValue addTokenForArgument(SDValue Chain, SelectionDAG &DAG, + MachineFrameInfo *MFI, int ClobberedFI) const; + + EVT getSetCCResultType(EVT VT) const; + + SDValue LowerOperation(SDValue Op, SelectionDAG &DAG) const; + + bool isLegalICmpImmediate(int64_t Val) const; + SDValue getSelectableIntSetCC(SDValue LHS, SDValue RHS, ISD::CondCode CC, + SDValue &A64cc, SelectionDAG &DAG, DebugLoc &dl) const; + + virtual MachineBasicBlock * + EmitInstrWithCustomInserter(MachineInstr *MI, MachineBasicBlock *MBB) const; + + SDValue LowerBlockAddress(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerBRCOND(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerBR_CC(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerFNEG(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerGlobalAddressELF(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerJumpTable(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerSELECT(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerSELECT_CC(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerSETCC(SDValue Op, SelectionDAG &DAG) const; + + virtual SDValue PerformDAGCombine(SDNode *N, DAGCombinerInfo &DCI) const; + + //nope. + bool IsEligibleForTailCallOptimization(SDValue Callee, + CallingConv::ID CalleeCC, + bool IsVarArg, + bool IsCalleeStructRet, + bool IsCallerStructRet, + const SmallVectorImpl &Outs, + const SmallVectorImpl &OutVals, + const SmallVectorImpl &Ins, + SelectionDAG& DAG) const { return false;} +// custom fma due to fneg, so we say no + virtual bool isFMAFasterThanMulAndAdd(EVT) const { return false; } + +private: + const EpiphanySubtarget *Subtarget; + const TargetRegisterInfo *RegInfo; + const InstrItineraryData *Itins; +}; +} // namespace llvm + +#endif // LLVM_TARGET_EPIPHANY_ISELLOWERING_H diff --git a/EpiphanyInstrFormats.td b/EpiphanyInstrFormats.td new file mode 100644 index 0000000..4734d77 --- /dev/null +++ b/EpiphanyInstrFormats.td @@ -0,0 +1,93 @@ +//===- EpiphanyInstrFormats.td - Epiphany Instruction Formats --*- tablegen -*-=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// This file describes Epiphany instruction formats, down to the level of the +// instruction's overall class. +// ===----------------------------------------------------------------------===// + + +//===----------------------------------------------------------------------===// +// A64 Instruction Format Definitions. +//===----------------------------------------------------------------------===// + +class PseudoInst patterns> : Instruction { + let Namespace = "Epiphany"; + + let OutOperandList = outs; + let InOperandList= ins; + let Pattern = patterns; + let isCodeGenOnly = 1; + let isPseudo = 1; + let Size = 4; +} + +// As above, this will be a single A64 instruction, but we can actually give the +// expansion in TableGen. +class A64PseudoExpand patterns, dag Result> + : PseudoInst, + PseudoInstExpansion; + + +class EP1INST patterns, InstrItinClass itin> : Instruction { + +field bits<32> Inst =0 ; + // LLVM-level model of the Epiphany/A64 distinction. + let Namespace = "Epiphany"; + let Size = 4; + + // Set the templated fields + let OutOperandList = outs; + let InOperandList = ins; + let AsmString = asmstr; + let Pattern = patterns; + let Itinerary = itin; +} + +class EP2INST patterns, InstrItinClass itin> : Instruction { + +field bits<32> Inst =0 ; + // LLVM-level model of the Epiphany/A64 distinction. + let Namespace = "Epiphany"; + let Size = 4; + + // Set the templated fields + let OutOperandList = outs; + let InOperandList = ins; + let AsmString = asmstr; + let Pattern = patterns; + let Itinerary = itin; +} +class EP3INST patterns, InstrItinClass itin> : Instruction { + +field bits<32> Inst =0 ; + // LLVM-level model of the Epiphany/A64 distinction. + let Namespace = "Epiphany"; + let Size = 4; + + // Set the templated fields + let OutOperandList = outs; + let InOperandList = ins; + let AsmString = asmstr; + let Pattern = patterns; + let Itinerary = itin; +} + +class EP4INST patterns, InstrItinClass itin> : Instruction { + +field bits<32> Inst =0 ; + // LLVM-level model of the Epiphany/A64 distinction. + let Namespace = "Epiphany"; + let Size = 4; + + // Set the templated fields + let OutOperandList = outs; + let InOperandList = ins; + let AsmString = asmstr; + let Pattern = patterns; + let Itinerary = itin; +} \ No newline at end of file diff --git a/EpiphanyInstrInfo.cpp b/EpiphanyInstrInfo.cpp new file mode 100644 index 0000000..2a4c762 --- /dev/null +++ b/EpiphanyInstrInfo.cpp @@ -0,0 +1,751 @@ +//===- EpiphanyInstrInfo.cpp - Epiphany 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 Epiphany implementation of the TargetInstrInfo class. +// +//===----------------------------------------------------------------------===// + +#include "Epiphany.h" +#include "EpiphanyInstrInfo.h" +#include "EpiphanyMachineFunctionInfo.h" +#include "EpiphanyTargetMachine.h" +#include "MCTargetDesc/EpiphanyMCTargetDesc.h" +#include "Utils/EpiphanyBaseInfo.h" +#include "llvm/CodeGen/MachineConstantPool.h" +#include "llvm/CodeGen/MachineDominators.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Metadata.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/TargetRegistry.h" + +#include + +#define GET_INSTRINFO_CTOR +#include "EpiphanyGenInstrInfo.inc" + +using namespace llvm; + +EpiphanyInstrInfo::EpiphanyInstrInfo(const EpiphanySubtarget &STI) + : EpiphanyGenInstrInfo(Epiphany::ADJCALLSTACKDOWN, Epiphany::ADJCALLSTACKUP), + RI(*this, STI), Subtarget(STI) {} + +void EpiphanyInstrInfo::copyPhysReg(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, DebugLoc DL, + unsigned DestReg, unsigned SrcReg, + bool KillSrc) const { + + bool GPRDest = Epiphany::GPR32RegClass.contains(DestReg); + bool GPRSrc = Epiphany::GPR32RegClass.contains(SrcReg); + + if (GPRDest && GPRSrc) { + BuildMI(MBB, I, DL, get(Epiphany::MOVww), DestReg) + .addReg(SrcReg, getKillRegState(KillSrc)); + return; + } else if(!GPRDest && !GPRSrc) { + BuildMI(MBB, I, DL, get(Epiphany::MOVss), DestReg) + .addReg(SrcReg, getKillRegState(KillSrc)); + return; + } + + llvm_unreachable("Unknown register class in copyPhysReg"); + + + // //MDNode* comment = MDNode::get(getGlobalContext(), ArrayRef(llvm::MDString::get(getGlobalContext(), "BAH"))); + // // E.g. ORR xDst, xzr, xSrc, lsl #0 + // BuildMI(MBB, I, DL, get(Opc), DestReg) + // .addReg(ZeroReg) + // .addReg(SrcReg) + //.addImm(0);//.addMetadata(comment) +} + +MachineInstr * +EpiphanyInstrInfo::emitFrameIndexDebugValue(MachineFunction &MF, int FrameIx, + uint64_t Offset, const MDNode *MDPtr, + DebugLoc DL) const { + MachineInstrBuilder MIB = BuildMI(MF, DL, get(Epiphany::DBG_VALUE)) + .addFrameIndex(FrameIx).addImm(0) + .addImm(Offset) + .addMetadata(MDPtr); + return &*MIB; +} + + +bool +EpiphanyInstrInfo::analyzeCompare(const MachineInstr *MI, unsigned &SrcReg, unsigned &SrcReg2, + int &CmpMask, int &CmpValue) const { + switch (MI->getOpcode()) { + default: break; + case Epiphany::SUBri_cmp: + SrcReg = MI->getOperand(0).getReg(); + SrcReg2 = 0; + CmpMask = ~0; + CmpValue = MI->getOperand(1).getImm(); + return true; + case Epiphany::CMPrr: + SrcReg = MI->getOperand(0).getReg(); + SrcReg2 = MI->getOperand(1).getReg(); + CmpMask = ~0; + CmpValue = 0; + return true; + } + return false; +} + +/// OptimizeCompareInstr - Convert the instruction supplying the argument to the +/// comparison into one that sets the zero bit in the flags register. Convert +/// the SUBrr(r1,r2)|Subri(r1,CmpValue) instruction into one that sets the flags +/// register and remove the CMPrr(r1,r2)|CMPrr(r2,r1)|CMPri(r1,CmpValue) +/// instruction. +bool +EpiphanyInstrInfo::optimizeCompareInstr(MachineInstr *CmpInstr, unsigned SrcReg, unsigned SrcReg2, int CmpMask, int CmpValue, const MachineRegisterInfo *MRI) const { + + MachineRegisterInfo::def_iterator DI = MRI->def_begin(SrcReg); + if (llvm::next(DI) != MRI->def_end()) + // Only support one definition. + return false; + + MachineInstr *MI = &*DI; + + // Get ready to iterate backward from CmpInstr. + MachineBasicBlock::iterator I = CmpInstr, E = MI, B = CmpInstr->getParent()->begin(); + + // Early exit if CmpInstr is at the beginning of the BB. + if (I == B) return false; + + // There are two possible candidates which can be changed to set CPSR: + // One is MI, the other is a SUB instruction. + // For CMPrr(r1,r2), we are looking for SUB(r1,r2) or SUB(r2,r1). + // For CMPri(r1, CmpValue), we are looking for SUBri(r1, CmpValue). + MachineInstr *Sub = NULL; + unsigned SrcReg2X = 0; + if (CmpInstr->getOpcode() == Epiphany::CMPrr) { + SrcReg2X = CmpInstr->getOperand(1).getReg(); + // MI is not a candidate for CMPrr. + MI = NULL; + } else if (MI->getParent() != CmpInstr->getParent() || CmpValue != 0) { + // Conservatively refuse to convert an instruction which isn't in the same + // BB as the comparison. + // For CMPri, we need to check Sub, thus we can't return here. + if(CmpInstr->getOpcode() == Epiphany::SUBri_cmp) + MI = NULL; + else + return false; + } + + // Check that CPSR isn't set between the comparison instruction and the one we + // want to change. At the same time, search for Sub. + --I; + for (; I != E; --I) { + const MachineInstr &Instr = *I; + + for (unsigned IO = 0, EO = Instr.getNumOperands(); IO != EO; ++IO) { + const MachineOperand &MO = Instr.getOperand(IO); + if (MO.isRegMask() && MO.clobbersPhysReg(Epiphany::NZCV)) + return false; + if (!MO.isReg()) continue; + + // This instruction modifies or uses CPSR after the one we want to + // change. We can't do this transformation. + if (MO.getReg() == Epiphany::NZCV) + return false; + } + + // Check whether the current instruction is SUB(r1, r2) or SUB(r2, r1). + if (SrcReg2X != 0 && Instr.getOpcode() == Epiphany::SUBrr && + ((Instr.getOperand(1).getReg() == SrcReg && + Instr.getOperand(2).getReg() == SrcReg2X) || + (Instr.getOperand(1).getReg() == SrcReg2X && + Instr.getOperand(2).getReg() == SrcReg))) { + Sub = &*I; + break; + } + + // Check whether the current instruction is SUBri(r1, CmpValue). + if ((CmpInstr->getOpcode() == Epiphany::SUBri_cmp) && + Instr.getOpcode() == Epiphany::SUBri && CmpValue != 0 && + Instr.getOperand(1).getReg() == SrcReg && + Instr.getOperand(2).getImm() == CmpValue) { + Sub = &*I; + break; + } + + if (I == B) + // The 'and' is below the comparison instruction. + return false; + } + + // Return false if no candidates exist. + if (!MI && !Sub) + return false; + + // The single candidate is called MI. + if (!MI) MI = Sub; + + switch (MI->getOpcode()) { + default: break; + case Epiphany::ADDrr: + case Epiphany::ADDri: + case Epiphany::SUBrr: + case Epiphany::SUBri: + case Epiphany::ANDrr: + case Epiphany::ORRrr: + case Epiphany::EORrr: { + // Scan forward for the use of CPSR + // When checking against MI: if it's a conditional code requires + // checking of V bit, then this is not safe to do. If we can't find the + // CPSR use (i.e. used in another block), then it's not safe to perform + // the optimization. + // When checking against Sub, we handle the condition codes GE, LT, GT, LE. + SmallVector OperandsToUpdate; + bool isSafe = false; + I = CmpInstr; + E = CmpInstr->getParent()->end(); + while (!isSafe && ++I != E) { + const MachineInstr &Instr = *I; + for (unsigned IO = 0, EO = Instr.getNumOperands(); !isSafe && IO != EO; ++IO) { + const MachineOperand &MO = Instr.getOperand(IO); + if (MO.isRegMask() && MO.clobbersPhysReg(Epiphany::NZCV)) { + isSafe = true; + break; + } + if (!MO.isReg() || MO.getReg() != Epiphany::NZCV) + continue; + if (MO.isDef()) { + isSafe = true; + break; + } + + EpiphanyCC::CondCodes CC; + switch(Instr.getOpcode()){ + case Epiphany::MOVCCrr: + case Epiphany::MOVCCss: + CC = (EpiphanyCC::CondCodes)Instr.getOperand(3).getImm(); + break; + case Epiphany::Bcc: + CC = (EpiphanyCC::CondCodes)Instr.getOperand(0).getImm(); + break; + } + + if (Sub) + switch (CC) { + default: + return false; + case EpiphanyCC::GTE: + case EpiphanyCC::LT: + case EpiphanyCC::GT: + case EpiphanyCC::LTE: + // If we have SUB(r1, r2) and CMP(r2, r1), the condition code based + // on CMP needs to be updated to be based on SUB. + // Push the condition code operands to OperandsToUpdate. + // If it is safe to remove CmpInstr, the condition code of these + // operands will be modified. + if (SrcReg2 != 0 && Sub->getOperand(1).getReg() == SrcReg2 && + Sub->getOperand(2).getReg() == SrcReg) + OperandsToUpdate.push_back(&((*I).getOperand(IO-1))); + break; + } + else + switch (CC) { + default: + isSafe = true; + break; + //case EpiphanyCC::VS: + //case EpiphanyCC::VC: + case EpiphanyCC::GTE: + case EpiphanyCC::LT: + case EpiphanyCC::GT: + case EpiphanyCC::LTE: + return false; + } + } + } + + // If the candidate is Sub, we may exit the loop at end of the basic block. + // In that case, it is still safe to remove CmpInstr. + if (!isSafe && !Sub) + return false; + + // Toggle the optional operand to CPSR. + //MI->getOperand(5).setReg(Epiphany::NZCV); + //MI->getOperand(5).setIsDef(true); + CmpInstr->eraseFromParent(); + + // Modify the condition code of operands in OperandsToUpdate. + // Since we have SUB(r1, r2) and CMP(r2, r1), the condition code needs to + // be changed from r2 > r1 to r1 < r2, from r2 < r1 to r1 > r2, etc. + for (unsigned i = 0; i < OperandsToUpdate.size(); i++) { + EpiphanyCC::CondCodes CC = (EpiphanyCC::CondCodes)OperandsToUpdate[i]->getImm(); + EpiphanyCC::CondCodes NewCC; + switch(CC) { + default: break; + case EpiphanyCC::GTE: NewCC = EpiphanyCC::LTE; break; + case EpiphanyCC::LT: NewCC = EpiphanyCC::GT; break; + case EpiphanyCC::GT: NewCC = EpiphanyCC::LT; break; + case EpiphanyCC::LTE: NewCC = EpiphanyCC::GT; break; + } + OperandsToUpdate[i]->setImm(NewCC); + } + return true; + } + } + + return false; +} + +/// Does the Opcode represent a conditional branch that we can remove and re-add +/// at the end of a basic block? +static bool isCondBranch(unsigned Opc) { + return Opc == Epiphany::Bcc; +} + +/// Takes apart a given conditional branch MachineInstr (see isCondBranch), +/// setting TBB to the destination basic block and populating the Cond vector +/// with data necessary to recreate the conditional branch at a later +/// date. First element will be the opcode, and subsequent ones define the +/// conditions being branched on in an instruction-specific manner. +static void classifyCondBranch(MachineInstr *I, MachineBasicBlock *&TBB, + SmallVectorImpl &Cond) { + switch(I->getOpcode()) { + case Epiphany::Bcc: + // These instructions just have one predicate operand in position 0 (either + // a condition code or a register being compared). + Cond.push_back(MachineOperand::CreateImm(I->getOpcode())); + Cond.push_back(I->getOperand(0)); + TBB = I->getOperand(1).getMBB(); + return; + default: + llvm_unreachable("Unknown conditional branch to classify"); + } +} + + +bool +EpiphanyInstrInfo::AnalyzeBranch(MachineBasicBlock &MBB,MachineBasicBlock *&TBB, + MachineBasicBlock *&FBB, + SmallVectorImpl &Cond, + bool AllowModify) const { + // If the block has no terminators, it just falls into the block after it. + MachineBasicBlock::iterator I = MBB.end(); + if (I == MBB.begin()) + return false; + --I; + while (I->isDebugValue()) { + if (I == MBB.begin()) + return false; + --I; + } + if (!isUnpredicatedTerminator(I)) + return false; + + // Get the last instruction in the block. + MachineInstr *LastInst = I; + + // If there is only one terminator instruction, process it. + unsigned LastOpc = LastInst->getOpcode(); + if (I == MBB.begin() || !isUnpredicatedTerminator(--I)) { + if (LastOpc == Epiphany::Bimm) { + TBB = LastInst->getOperand(0).getMBB(); + return false; + } + if (isCondBranch(LastOpc)) { + classifyCondBranch(LastInst, TBB, Cond); + return false; + } + return true; // Can't handle indirect branch. + } + + // Get the instruction before it if it is a terminator. + MachineInstr *SecondLastInst = I; + unsigned SecondLastOpc = SecondLastInst->getOpcode(); + + // If AllowModify is true and the block ends with two or more unconditional + // branches, delete all but the first unconditional branch. + if (AllowModify && LastOpc == Epiphany::Bimm) { + while (SecondLastOpc == Epiphany::Bimm) { + LastInst->eraseFromParent(); + LastInst = SecondLastInst; + LastOpc = LastInst->getOpcode(); + if (I == MBB.begin() || !isUnpredicatedTerminator(--I)) { + // Return now the only terminator is an unconditional branch. + TBB = LastInst->getOperand(0).getMBB(); + return false; + } else { + SecondLastInst = I; + SecondLastOpc = SecondLastInst->getOpcode(); + } + } + } + + // If there are three terminators, we don't know what sort of block this is. + if (SecondLastInst && I != MBB.begin() && isUnpredicatedTerminator(--I)) + return true; + + // If the block ends with a B and a Bcc, handle it. + if (LastOpc == Epiphany::Bimm) { + if (SecondLastOpc == Epiphany::Bcc) { + TBB = SecondLastInst->getOperand(1).getMBB(); + Cond.push_back(MachineOperand::CreateImm(Epiphany::Bcc)); + Cond.push_back(SecondLastInst->getOperand(0)); + FBB = LastInst->getOperand(0).getMBB(); + return false; + } else if (isCondBranch(SecondLastOpc)) { + classifyCondBranch(SecondLastInst, TBB, Cond); + FBB = LastInst->getOperand(0).getMBB(); + return false; + } + } + + // If the block ends with two unconditional branches, handle it. The second + // one is not executed, so remove it. + if (SecondLastOpc == Epiphany::Bimm && LastOpc == Epiphany::Bimm) { + TBB = SecondLastInst->getOperand(0).getMBB(); + I = LastInst; + if (AllowModify) + I->eraseFromParent(); + return false; + } + + // Otherwise, can't handle this. + return true; +} + +unsigned +EpiphanyInstrInfo::InsertBranch(MachineBasicBlock &MBB, MachineBasicBlock *TBB, + MachineBasicBlock *FBB, + const SmallVectorImpl &Cond, + DebugLoc DL) const { + if (FBB == 0 && Cond.empty()) { + BuildMI(&MBB, DL, get(Epiphany::Bimm)).addMBB(TBB); + return 1; + } else if (FBB == 0) { + MachineInstrBuilder MIB = BuildMI(&MBB, DL, get(Cond[0].getImm())); + for (int i = 1, e = Cond.size(); i != e; ++i) + MIB.addOperand(Cond[i]); + MIB.addMBB(TBB); + return 1; + } + + MachineInstrBuilder MIB = BuildMI(&MBB, DL, get(Cond[0].getImm())); + for (int i = 1, e = Cond.size(); i != e; ++i) + MIB.addOperand(Cond[i]); + MIB.addMBB(TBB); + + BuildMI(&MBB, DL, get(Epiphany::Bimm)).addMBB(FBB); + return 2; +} + +unsigned EpiphanyInstrInfo::RemoveBranch(MachineBasicBlock &MBB) const { + MachineBasicBlock::iterator I = MBB.end(); + if (I == MBB.begin()) return 0; + --I; + while (I->isDebugValue()) { + if (I == MBB.begin()) + return 0; + --I; + } + if (I->getOpcode() != Epiphany::Bimm && !isCondBranch(I->getOpcode())) + return 0; + + // Remove the branch. + I->eraseFromParent(); + + I = MBB.end(); + + if (I == MBB.begin()) return 1; + --I; + if (!isCondBranch(I->getOpcode())) + return 1; + + // Remove the branch. + I->eraseFromParent(); + return 2; +} + +bool +EpiphanyInstrInfo::expandPostRAPseudo(MachineBasicBlock::iterator MBBI) const { + return false; +} + +void +EpiphanyInstrInfo::storeRegToStackSlot(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, + unsigned SrcReg, bool isKill, + int FrameIdx, + const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI) const { + DebugLoc DL = MBB.findDebugLoc(MBBI); + MachineFunction &MF = *MBB.getParent(); + MachineFrameInfo &MFI = *MF.getFrameInfo(); + unsigned Align = MFI.getObjectAlignment(FrameIdx); + + MachineMemOperand *MMO + = MF.getMachineMemOperand(MachinePointerInfo::getFixedStack(FrameIdx), + MachineMemOperand::MOStore, + MFI.getObjectSize(FrameIdx), + Align); + + unsigned StoreOp = 0; + if (RC->hasType(MVT::i64) || RC->hasType(MVT::i32)) { + switch(RC->getSize()) { + case 4: StoreOp = Epiphany::LS32_STR; break; + //case 8: StoreOp = Epiphany::LS64_STR; break; + default: + llvm_unreachable("Unknown size for regclass"); + } + } else { + assert((RC->hasType(MVT::f32) || RC->hasType(MVT::f64)) + && "Expected integer or floating type for store"); + switch (RC->getSize()) { + case 4: StoreOp = Epiphany::LSFP32_STR; break; + //case 8: StoreOp = Epiphany::LSFP64_STR; break; + default: + llvm_unreachable("Unknown size for regclass"); + } + } + + MachineInstrBuilder NewMI = BuildMI(MBB, MBBI, DL, get(StoreOp)); + NewMI.addReg(SrcReg, getKillRegState(isKill)) + .addFrameIndex(FrameIdx) + .addImm(0) + .addMemOperand(MMO); + +} + +void +EpiphanyInstrInfo::loadRegFromStackSlot(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, + unsigned DestReg, int FrameIdx, + const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI) const { + DebugLoc DL = MBB.findDebugLoc(MBBI); + MachineFunction &MF = *MBB.getParent(); + MachineFrameInfo &MFI = *MF.getFrameInfo(); + unsigned Align = MFI.getObjectAlignment(FrameIdx); + + MachineMemOperand *MMO + = MF.getMachineMemOperand(MachinePointerInfo::getFixedStack(FrameIdx), + MachineMemOperand::MOLoad, + MFI.getObjectSize(FrameIdx), + Align); + + unsigned LoadOp = 0; + if (RC->hasType(MVT::i64) || RC->hasType(MVT::i32)) { + switch(RC->getSize()) { + case 4: LoadOp = Epiphany::LS32_LDR; break; + //case 8: LoadOp = Epiphany::LS64_LDR; break; + default: + llvm_unreachable("Unknown size for regclass"); + } + } else { + assert((RC->hasType(MVT::f32) || RC->hasType(MVT::f64)) + && "Expected integer or floating type for store"); + switch (RC->getSize()) { + case 4: LoadOp = Epiphany::LSFP32_LDR; break; + //case 8: LoadOp = Epiphany::LSFP64_LDR; break; + default: + llvm_unreachable("Unknown size for regclass"); + } + } + + MachineInstrBuilder NewMI = BuildMI(MBB, MBBI, DL, get(LoadOp), DestReg); + NewMI.addFrameIndex(FrameIdx) + .addImm(0) + .addMemOperand(MMO); +} + +unsigned EpiphanyInstrInfo::estimateRSStackLimit(MachineFunction &MF) const { + unsigned Limit = (1 << 16) - 1; + for (MachineFunction::iterator BB = MF.begin(),E = MF.end(); BB != E; ++BB) { + for (MachineBasicBlock::iterator I = BB->begin(), E = BB->end(); + I != E; ++I) { + for (unsigned i = 0, e = I->getNumOperands(); i != e; ++i) { + if (!I->getOperand(i).isFI()) continue; + + // When using ADDwwi_lsl0_s to get the address of a stack object, 0x3FF + // is the largest offset guaranteed to fit in the immediate offset. + if (I->getOpcode() == Epiphany::ADDri) { + Limit = std::min(Limit, 0x3FFu); + break; + } + + int AccessScale, MinOffset, MaxOffset; + getAddressConstraints(*I, AccessScale, MinOffset, MaxOffset); + Limit = std::min(Limit, static_cast(MaxOffset)); + + break; // At most one FI per instruction + } + } + } + + return Limit; +} +void EpiphanyInstrInfo::getAddressConstraints(const MachineInstr &MI, + int &AccessScale, int &MinOffset, + int &MaxOffset) const { + switch (MI.getOpcode()) { + default: llvm_unreachable("Unkown load/store kind"); + case TargetOpcode::DBG_VALUE: + AccessScale = 1; + MinOffset = INT_MIN; + MaxOffset = INT_MAX; + return; + case Epiphany::LS8_LDR: case Epiphany::LS8_STR: + AccessScale = 1; + MinOffset = 0; + MaxOffset = 0x7FF; + return; + case Epiphany::LS16_LDR: case Epiphany::LS16_STR: + AccessScale = 2; + MinOffset = 0; + MaxOffset = 0x7FF * AccessScale; + return; + case Epiphany::LS32_LDR: case Epiphany::LS32_STR: + case Epiphany::LSFP32_LDR: case Epiphany::LSFP32_STR: + AccessScale = 4; + MinOffset = 0; + MaxOffset = 0x7FF * AccessScale; + return; + //case Epiphany::LSFP64_LDR: case Epiphany::LSFP64_STR: + // AccessScale = 8; + // MinOffset = 0; + // MaxOffset = 0x7FF * AccessScale; + // return; + } +} + +unsigned EpiphanyInstrInfo::getInstSizeInBytes(const MachineInstr &MI) const { + const MCInstrDesc &MCID = MI.getDesc(); + const MachineBasicBlock &MBB = *MI.getParent(); + const MachineFunction &MF = *MBB.getParent(); + const MCAsmInfo &MAI = *MF.getTarget().getMCAsmInfo(); + + if (MCID.getSize()) + return MCID.getSize(); + + if (MI.getOpcode() == Epiphany::INLINEASM) + return getInlineAsmLength(MI.getOperand(0).getSymbolName(), MAI); + + if (MI.isLabel()) + return 0; + + switch (MI.getOpcode()) { + case TargetOpcode::BUNDLE: + return getInstBundleLength(MI); + case TargetOpcode::IMPLICIT_DEF: + case TargetOpcode::KILL: + case TargetOpcode::PROLOG_LABEL: + case TargetOpcode::EH_LABEL: + case TargetOpcode::DBG_VALUE: + return 0; + default: + llvm_unreachable("Unknown instruction class"); + } +} + +unsigned EpiphanyInstrInfo::getInstBundleLength(const MachineInstr &MI) const { + unsigned Size = 0; + MachineBasicBlock::const_instr_iterator I = MI; + MachineBasicBlock::const_instr_iterator E = MI.getParent()->instr_end(); + while (++I != E && I->isInsideBundle()) { + assert(!I->isBundle() && "No nested bundle!"); + Size += getInstSizeInBytes(*I); + } + return Size; +} + +bool llvm::rewriteA64FrameIndex(MachineInstr &MI, unsigned FrameRegIdx, + unsigned FrameReg, int &Offset, + const EpiphanyInstrInfo &TII) { + MachineBasicBlock &MBB = *MI.getParent(); + MachineFunction &MF = *MBB.getParent(); + MachineFrameInfo &MFI = *MF.getFrameInfo(); + + MFI.getObjectOffset(FrameRegIdx); + llvm_unreachable("Unimplemented rewriteFrameIndex"); +} + +void llvm::EPIPHemitRegUpdate(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, + DebugLoc dl, const TargetInstrInfo &TII, + unsigned DstReg, unsigned SrcReg, unsigned ScratchReg, + int64_t NumBytes, MachineInstr::MIFlag MIFlags) { + if (NumBytes == 0 && DstReg == SrcReg) + return; + else if (abs(NumBytes) & ~0x3FF) { // 11bit signed = 10b unsigned + // Generically, we have to materialize the offset into a temporary register + // and subtract it. There are a couple of ways this could be done, for now + // we'll use a movz/movk or movn/movk sequence. + uint64_t Bits = static_cast(abs(NumBytes)); + BuildMI(MBB, MBBI, dl, TII.get(Epiphany::MOVri), ScratchReg) + .addImm(0xffff & Bits).setMIFlags(MIFlags); + + Bits >>= 16; + if (Bits & 0xffff) { + BuildMI(MBB, MBBI, dl, TII.get(Epiphany::MOVTri), ScratchReg) + .addImm(0xffff & Bits).setMIFlags(MIFlags); + } + + //HACK since we have only 32b at most this needs only a mov + movt + // ADD DST, SRC, xTMP (, lsl #0) + unsigned AddOp = NumBytes > 0 ? Epiphany::ADDrr : Epiphany::SUBrr; + BuildMI(MBB, MBBI, dl, TII.get(AddOp), DstReg) + .addReg(SrcReg, RegState::Kill) + .addReg(ScratchReg, RegState::Kill) + .addImm(0) + .setMIFlag(MIFlags); + return; + } + + // Now we know that the adjustment can be done in at most two add/sub + // (immediate) instructions, which is always more efficient than a + // literal-pool load, or even a hypothetical movz/movk/add sequence + + // Decide whether we're doing addition or subtraction + unsigned LowOp, HighOp; + if (NumBytes >= 0) { + LowOp = Epiphany::ADDri; + //HighOp = Epiphany::ADDwwi_lsl12_s; + } else { + LowOp = Epiphany::SUBri; + //HighOp = Epiphany::SUBwwi_lsl12_s; + NumBytes = abs(NumBytes); + } + + // If we're here, at the very least a move needs to be produced, which just + // happens to be materializable by an ADD. + if ((NumBytes & 0x3FF) || NumBytes == 0) { + BuildMI(MBB, MBBI, dl, TII.get(LowOp), DstReg) + .addReg(SrcReg, RegState::Kill) + .addImm(NumBytes & 0x3FF) + .setMIFlag(MIFlags); + + // Next update should use the register we've just defined. + SrcReg = DstReg; + } + + if (NumBytes & 0xfff000) { + llvm_unreachable("oh fuck.... hi bits reg update"); + BuildMI(MBB, MBBI, dl, TII.get(HighOp), DstReg) + .addReg(SrcReg, RegState::Kill) + .addImm(NumBytes >> 12) + .setMIFlag(MIFlags); + } +} + +void llvm::EPIPHemitSPUpdate(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, + DebugLoc dl, const TargetInstrInfo &TII, + unsigned ScratchReg, int64_t NumBytes, + MachineInstr::MIFlag MIFlags) { + EPIPHemitRegUpdate(MBB, MI, dl, TII, Epiphany::SP, Epiphany::SP, Epiphany::R63, + NumBytes, MIFlags); +} diff --git a/EpiphanyInstrInfo.h b/EpiphanyInstrInfo.h new file mode 100644 index 0000000..14dc274 --- /dev/null +++ b/EpiphanyInstrInfo.h @@ -0,0 +1,120 @@ +//===- EpiphanyInstrInfo.h - Epiphany 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 Epiphany implementation of the TargetInstrInfo class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TARGET_EPIPHANYINSTRINFO_H +#define LLVM_TARGET_EPIPHANYINSTRINFO_H + +#include "llvm/Target/TargetInstrInfo.h" +#include "EpiphanyRegisterInfo.h" + +#define GET_INSTRINFO_HEADER +#include "EpiphanyGenInstrInfo.inc" + +namespace llvm { + +class EpiphanySubtarget; + +class EpiphanyInstrInfo : public EpiphanyGenInstrInfo { + const EpiphanyRegisterInfo RI; + const EpiphanySubtarget &Subtarget; +public: + explicit EpiphanyInstrInfo(const EpiphanySubtarget &TM); + + /// 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). + /// + const TargetRegisterInfo &getRegisterInfo() const { return RI; } + + const EpiphanySubtarget &getSubTarget() const { return Subtarget; } + + void copyPhysReg(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, DebugLoc DL, + unsigned DestReg, unsigned SrcReg, + bool KillSrc) const; + + MachineInstr *emitFrameIndexDebugValue(MachineFunction &MF, int FrameIx, + uint64_t Offset, const MDNode *MDPtr, + DebugLoc DL) const; + + void storeRegToStackSlot(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + unsigned SrcReg, bool isKill, int FrameIndex, + const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI) const; + void loadRegFromStackSlot(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, + unsigned DestReg, int FrameIdx, + const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI) const; + + bool analyzeCompare(const MachineInstr *MI, unsigned &SrcReg, + unsigned &SrcReg2, + int &CmpMask, int &CmpValue) const; + + bool optimizeCompareInstr(MachineInstr *CmpInstr, unsigned SrcReg, + unsigned SrcReg2, int CmpMask, int CmpValue, + const MachineRegisterInfo *MRI) const; + + + bool AnalyzeBranch(MachineBasicBlock &MBB, MachineBasicBlock *&TBB, + MachineBasicBlock *&FBB, + SmallVectorImpl &Cond, + bool AllowModify = false) const; + unsigned InsertBranch(MachineBasicBlock &MBB, MachineBasicBlock *TBB, + MachineBasicBlock *FBB, + const SmallVectorImpl &Cond, + DebugLoc DL) const; + unsigned RemoveBranch(MachineBasicBlock &MBB) const; + + bool expandPostRAPseudo(MachineBasicBlock::iterator MI) const; + + /// Look through the instructions in this function and work out the largest + /// the stack frame can be while maintaining the ability to address local + /// slots with no complexities. + unsigned estimateRSStackLimit(MachineFunction &MF) const; + + /// getAddressConstraints - For loads and stores (and PRFMs) taking an + /// immediate offset, this function determines the constraints required for + /// the immediate. It must satisfy: + /// + MinOffset <= imm <= MaxOffset + /// + imm % OffsetScale == 0 + void getAddressConstraints(const MachineInstr &MI, int &AccessScale, + int &MinOffset, int &MaxOffset) const; + + + unsigned getInstSizeInBytes(const MachineInstr &MI) const; + + unsigned getInstBundleLength(const MachineInstr &MI) const; + +}; + +bool rewriteA64FrameIndex(MachineInstr &MI, unsigned FrameRegIdx, + unsigned FrameReg, int &Offset, + const EpiphanyInstrInfo &TII); + + +void EPIPHemitRegUpdate(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, + DebugLoc dl, const TargetInstrInfo &TII, + unsigned DstReg, unsigned SrcReg, unsigned ScratchReg, + int64_t NumBytes, + MachineInstr::MIFlag MIFlags = MachineInstr::NoFlags); + +void EPIPHemitSPUpdate(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, + DebugLoc dl, const TargetInstrInfo &TII, + unsigned ScratchReg, int64_t NumBytes, + MachineInstr::MIFlag MIFlags = MachineInstr::NoFlags); + +} + +#endif diff --git a/EpiphanyInstrInfo.td b/EpiphanyInstrInfo.td new file mode 100644 index 0000000..2b09f86 --- /dev/null +++ b/EpiphanyInstrInfo.td @@ -0,0 +1,612 @@ +//===----- EpiphanyInstrInfo.td - Epiphany Instruction Info ----*- tablegen -*-=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file describes the Epiphany scalar instructions in TableGen format. +// +//===----------------------------------------------------------------------===// + +include "EpiphanyInstrFormats.td" + +//===----------------------------------------------------------------------===// +// Target-specific ISD nodes and profiles +//===----------------------------------------------------------------------===// + +//(outs rd), ins (rd, rn, rm, is_sub_flag) +def SDT_fm_a_s : SDTypeProfile<1, 4, [SDTCisSameAs<0, 1>, SDTCisSameAs<0, 2>, SDTCisSameAs<0, 3>, SDTCisFP<0>, SDTCisVT<4, i32>]>; +def EPIfmas : SDNode<"EpiphanyISD::FM_A_S", SDT_fm_a_s>; + +def SDT_A64ret : SDTypeProfile<0, 0, []>; +def A64ret : SDNode<"EpiphanyISD::Ret", SDT_A64ret, [SDNPHasChain, + SDNPOptInGlue, + SDNPVariadic]>; + +// (ins NZCV, Condition, Dest) +def SDT_A64br_cc : SDTypeProfile<0, 3, [SDTCisVT<0, i32>]>; +def A64br_cc : SDNode<"EpiphanyISD::BR_CC", SDT_A64br_cc, [SDNPHasChain]>; + +// (outs Result), (ins NZCV, IfTrue, IfFalse, Condition) +def SDT_A64select_cc : SDTypeProfile<1, 4, [SDTCisVT<1, i32>, + SDTCisSameAs<0, 2>, + SDTCisSameAs<2, 3>]>; +def A64select_cc : SDNode<"EpiphanyISD::SELECT_CC", SDT_A64select_cc>; + +// (outs NZCV), (ins LHS, RHS, Condition) +def SDT_A64setcc : SDTypeProfile<1, 3, [SDTCisVT<0, i32>, + SDTCisSameAs<1, 2>]>; +def A64setcc : SDNode<"EpiphanyISD::SETCC", SDT_A64setcc>; + + +// (outs GPR32), (ins) +//def A64threadpointer : SDNode<"EpiphanyISD::THREAD_POINTER", SDTPtrLeaf>; + +// A64 compares don't care about the cond really (they set all flags) so a +// simple binary operator is useful. +def A64cmp : PatFrag<(ops node:$lhs, node:$rhs), (A64setcc node:$lhs, node:$rhs, cond)>; + + + +// There are two layers of indirection here, driven by the following +// considerations. +// + TableGen does not know CodeModel or Reloc so that decision should be +// made for a variable/address at ISelLowering. +// + The output of ISelLowering should be selectable (hence the Wrapper, +// rather than a bare target opcode) +def SDTEpiphanyWrapper : SDTypeProfile<1, 3, [SDTCisSameAs<0, 1>, + SDTCisSameAs<1, 2>, + SDTCisVT<3, i32>, + SDTCisPtrTy<0>]>; + +def A64WrapperSmall : SDNode<"EpiphanyISD::WrapperSmall", SDTEpiphanyWrapper>; + + +//===----------------------------------------------------------------------===// +// Call sequence pseudo-instructions +//===----------------------------------------------------------------------===// + + +def SDT_EpiphanyCall : SDTypeProfile<0, -1, [SDTCisPtrTy<0>]>; +def EpiphanyCall : SDNode<"EpiphanyISD::Call", SDT_EpiphanyCall, + [SDNPHasChain, SDNPOptInGlue, SDNPOutGlue, SDNPVariadic]>; + +def SDT_EpiphanyCallSeqStart : SDCallSeqStart<[ SDTCisPtrTy<0> ]>; +def Epiphanycallseq_start : SDNode<"ISD::CALLSEQ_START", SDT_EpiphanyCallSeqStart, + [SDNPHasChain, SDNPOutGlue]>; + +def SDT_EpiphanyCallSeqEnd : SDCallSeqEnd<[ SDTCisPtrTy<0>, SDTCisPtrTy<1> ]>; +def Epiphanycallseq_end : SDNode<"ISD::CALLSEQ_END", SDT_EpiphanyCallSeqEnd, + [SDNPHasChain, SDNPOptInGlue, SDNPOutGlue]>; + + + +// These pseudo-instructions have special semantics by virtue of being passed to +// the InstrInfo constructor. CALLSEQ_START/CALLSEQ_END are produced by +// LowerCall to (in our case) tell the back-end about stack adjustments for +// arguments passed on the stack. Here we select those markers to +// pseudo-instructions which explicitly set the stack, and finally in the +// RegisterInfo we convert them to a true stack adjustment. +let Defs = [SP], Uses = [SP] in { + def ADJCALLSTACKDOWN : PseudoInst<(outs), (ins i32imm:$amt), + [(Epiphanycallseq_start timm:$amt)]>; + + def ADJCALLSTACKUP : PseudoInst<(outs), (ins i32imm:$amt1, i32imm:$amt2), + [(Epiphanycallseq_end timm:$amt1, timm:$amt2)]>; +} + +//===----------------------------------------------------------------------===// +// Logical (register) instructions +//===----------------------------------------------------------------------===// +let Defs = [NZCV] in { + def ANDrr : EP3INST<(outs GPR32:$Rd),(ins GPR32:$Rn, GPR32:$Rm),"and\t$Rd, $Rn, $Rm",[(set GPR32:$Rd, (and GPR32:$Rn, GPR32:$Rm))],NoItinerary>; + def ORRrr : EP3INST<(outs GPR32:$Rd),(ins GPR32:$Rn, GPR32:$Rm),"orr\t$Rd, $Rn, $Rm",[(set GPR32:$Rd, (or GPR32:$Rn, GPR32:$Rm))],NoItinerary>; + def EORrr : EP3INST<(outs GPR32:$Rd),(ins GPR32:$Rn, GPR32:$Rm),"eor\t$Rd, $Rn, $Rm",[(set GPR32:$Rd, (xor GPR32:$Rn, GPR32:$Rm))],NoItinerary>; +} +//===----------------------------------------------------------------------===// +// Add-subtract (immediate) instructions +//===----------------------------------------------------------------------===// + +def neg_XFORM : SDNodeXFormgetTargetConstant(-N->getZExtValue(), MVT::i32); +}]>; + + + let PrintMethod = "printAddSubImmOperand" in { + def addsubimm_i32_normal : Operand, ImmLeaf= -1024 && Imm <= 1023; }]>; + def addsubimm_i32_special_minus : Operand, ImmLeaf; + } + +let Defs = [NZCV] in { +def ADDri : EP3INST<(outs GPR32:$Rd),(ins GPR32:$Rn, addsubimm_i32_normal:$Imm12),"add\t$Rd, $Rn, $Imm12",[/*(set GPR32:$Rd, (addc GPR32:$Rn, imm:$Imm12))*/],NoItinerary>; +def SUBri : EP3INST<(outs GPR32:$Rd),(ins GPR32:$Rn, addsubimm_i32_normal:$Imm12),"sub\t$Rd, $Rn, $Imm12",[/*(set GPR32:$Rd, (subc GPR32:$Rn, imm:$Imm12))*/],NoItinerary>; + + let isCompare = 1 in{ + def SUBri_cmp : EP3INST<(outs),(ins GPR32:$Rn, addsubimm_i32_normal:$Imm12),"cmp\tR63, $Rn, $Imm12",[/*(set NZCV, (A64setcc GPR32:$Rn, imm:$Imm12, cond))*/],NoItinerary>; + } +} + +// special case: add +1024 -> sub -1024 and sub +1024 -> add -1024 +def : Pat<(add GPR32:$Rn, addsubimm_i32_special_minus:$Imm12),(SUBri GPR32:$Rn, addsubimm_i32_special_minus:$Imm12)>; +def : Pat<(sub GPR32:$Rn, addsubimm_i32_special_minus:$Imm12),(ADDri GPR32:$Rn, addsubimm_i32_special_minus:$Imm12)>; + +def : Pat<(add GPR32:$Rn, addsubimm_i32_normal:$Imm12),(ADDri GPR32:$Rn, addsubimm_i32_normal:$Imm12)>; +def : Pat<(addc GPR32:$Rn, addsubimm_i32_normal:$Imm12),(ADDri GPR32:$Rn, addsubimm_i32_normal:$Imm12)>; + + +def : Pat<(sub GPR32:$Rn, addsubimm_i32_normal:$Imm12),(SUBri GPR32:$Rn, addsubimm_i32_normal:$Imm12)>; +def : Pat<(subc GPR32:$Rn, addsubimm_i32_normal:$Imm12),(SUBri GPR32:$Rn, addsubimm_i32_normal:$Imm12)>; + +// def : Pat<(A64setcc GPR32:$Rn, addsubimm_i32_normal:$Imm12, cond),(ADDri_cmp GPR32:$Rn, addsubimm_i32_normal:$Imm12)>; +def : Pat<(A64setcc GPR32:$Rn, addsubimm_i32_normal:$Imm12, cond),(SUBri_cmp GPR32:$Rn, addsubimm_i32_normal:$Imm12)>; + +//===----------------------------------------------------------------------===// +// Add-subtract ( register) instructions +//===----------------------------------------------------------------------===// + +let Defs = [NZCV] in { +def ADDrr : EP3INST<(outs GPR32:$Rd),(ins GPR32:$Rn, GPR32:$Rm),"add\t$Rd, $Rn, $Rm",[(set GPR32:$Rd, (addc GPR32:$Rn, (i32 GPR32:$Rm)))],NoItinerary>; +def SUBrr : EP3INST<(outs GPR32:$Rd),(ins GPR32:$Rn, GPR32:$Rm),"sub\t$Rd, $Rn, $Rm",[(set GPR32:$Rd, (subc GPR32:$Rn, (i32 GPR32:$Rm)))],NoItinerary>; + let isCompare = 1 in { + def CMPrr : EP3INST<(outs),(ins GPR32:$Rn, GPR32:$Rm),"cmp\tR63, $Rn, $Rm",[(set NZCV, (A64cmp GPR32:$Rn, (i32 GPR32:$Rm)))],NoItinerary>; + } + // let Rd = 0b11111, isCompare = 1 in { + // defm CMPw : addsub_exts<0b0, 0b1, 0b1, "cmp\t", SetNZCV, (outs), GPR32>; + // } +} + +def : Pat<(add GPR32:$Rn, (i32 GPR32:$Rm)), (ADDrr GPR32:$Rn, GPR32:$Rm)>; +def : Pat<(sub GPR32:$Rn, (i32 GPR32:$Rm)), (SUBrr GPR32:$Rn, GPR32:$Rm)>; +def : Pat<(A64cmp GPR32:$Rn, (i32 GPR32:$Rm)), (CMPrr GPR32:$Rn, GPR32:$Rm)>; + +//===----------------------------------------------------------------------===// +// Data Processing (1 source) instructions +//===----------------------------------------------------------------------===// +def REVr : EP2INST<(outs GPR32:$Rd),(ins GPR32:$Rn),"rev\t$Rd, $Rn",[(set GPR32:$Rd, (bswap GPR32:$Rn))],NoItinerary>; + +//===----------------------------------------------------------------------===// +// Data Processing (2 sources) instructions +//===----------------------------------------------------------------------===// + +def shifts_5bit : Operand, ImmLeaf= 0 && Imm <= 31 }]>; + +let Defs = [NZCV] in { + def LSLri : EP3INST<(outs GPR32:$Rd),(ins GPR32:$Rn, shifts_5bit:$UImm5),"lsl\t$Rd, $Rn, $UImm5",[(set GPR32:$Rd, (shl GPR32:$Rn, (i32 imm:$UImm5)) )],NoItinerary>; + def LSRri : EP3INST<(outs GPR32:$Rd),(ins GPR32:$Rn, shifts_5bit:$UImm5),"lsr\t$Rd, $Rn, $UImm5",[(set GPR32:$Rd, (srl GPR32:$Rn, (i32 imm:$UImm5)) )],NoItinerary>; + def ASRri : EP3INST<(outs GPR32:$Rd),(ins GPR32:$Rn, shifts_5bit:$UImm5),"asr\t$Rd, $Rn, $UImm5",[(set GPR32:$Rd, (sra GPR32:$Rn, (i32 imm:$UImm5)) )],NoItinerary>; + + def LSLrr : EP3INST<(outs GPR32:$Rd),(ins GPR32:$Rn, GPR32:$Rm),"lsl\t$Rd, $Rn, $Rm",[(set GPR32:$Rd, (shl GPR32:$Rn, GPR32:$Rm))],NoItinerary>; + def LSRrr : EP3INST<(outs GPR32:$Rd),(ins GPR32:$Rn, GPR32:$Rm),"lsr\t$Rd, $Rn, $Rm",[(set GPR32:$Rd, (srl GPR32:$Rn, GPR32:$Rm))],NoItinerary>; + def ASRrr : EP3INST<(outs GPR32:$Rd),(ins GPR32:$Rn, GPR32:$Rm),"asr\t$Rd, $Rn, $Rm",[(set GPR32:$Rd, (sra GPR32:$Rn, GPR32:$Rm))],NoItinerary>; +} +//===----------------------------------------------------------------------===// +// Floating-point compare instructions +//===----------------------------------------------------------------------===// +let Defs = [NZCV] in { + def FCMPss : EP3INST<(outs), (ins FPR32:$Rn, FPR32:$Rm), "fcmp\t$Rn, $Rm", [(set NZCV, (A64cmp (f32 FPR32:$Rn), FPR32:$Rm))], NoItinerary> {} + + let isCommutable = 1 in { + def FMUL_ss : EP3INST<(outs FPR32:$Rd),(ins FPR32:$Rn, FPR32:$Rm),"fmul\t$Rd, $Rn, $Rm",[(set (f32 FPR32:$Rd), (fmul FPR32:$Rn, FPR32:$Rm))],NoItinerary>; + def FADD_ss : EP3INST<(outs FPR32:$Rd),(ins FPR32:$Rn, FPR32:$Rm),"fadd\t$Rd, $Rn, $Rm",[(set (f32 FPR32:$Rd), (fadd FPR32:$Rn, FPR32:$Rm))],NoItinerary>; + } + def FSUB_ss : EP3INST<(outs FPR32:$Rd),(ins FPR32:$Rn, FPR32:$Rm),"fsub\t$Rd, $Rn, $Rm",[(set (f32 FPR32:$Rd), (fsub FPR32:$Rn, FPR32:$Rm))],NoItinerary>; + + let Constraints = "$Rd = $Ra" in { + def FMADDsss : EP3INST<(outs FPR32:$Rd),(ins FPR32:$Ra, FPR32:$Rn, FPR32:$Rm),"fmadd\t$Rd, $Rn, $Rm",[/*(set FPR32:$Rd, (fadd FPR32:$Ra, (fmul FPR32:$Rn, FPR32:$Rm)))*/],NoItinerary>; + def FMSUBsss : EP3INST<(outs FPR32:$Rd),(ins FPR32:$Ra, FPR32:$Rn, FPR32:$Rm),"fmsub\t$Rd, $Rn, $Rm",[/*(set FPR32:$Rd, (fsub FPR32:$Ra, (fmul FPR32:$Rn, FPR32:$Rm)))*/],NoItinerary>; + } +} + + def : Pat<(EPIfmas FPR32:$Rd, FPR32:$Rn, FPR32:$Rm, 0),(FMADDsss FPR32:$Rd, FPR32:$Rn, FPR32:$Rm)>; + def : Pat<(EPIfmas FPR32:$Rd, FPR32:$Rn, FPR32:$Rm, 1),(FMSUBsss FPR32:$Rd, FPR32:$Rn, FPR32:$Rm)>; + +let Defs = [NZCV] in { + //===----------------------------------------------------------------------===// + // Floating-point <-> integer conversion instructions + //===----------------------------------------------------------------------===// + def FIXrs : EP3INST<(outs GPR32:$Rd),(ins FPR32:$Rn),"FIX\t$Rd, $Rn",[(set (i32 GPR32:$Rd), (i32 (fp_to_sint FPR32:$Rn)) )],NoItinerary>; + def FLOATsr : EP3INST<(outs FPR32:$Rd),(ins GPR32:$Rn),"FLOAT\t$Rd, $Rn",[(set (f32 FPR32:$Rd), (f32 (sint_to_fp GPR32:$Rn)) )],NoItinerary>; + def FABSss : EP3INST<(outs FPR32:$Rd), (ins FPR32:$Rn), "FABS\t$Rd, $Rn",[(set FPR32:$Rd, (fabs FPR32:$Rn))],NoItinerary>; +} +//===----------------------------------------------------------------------===// +// Move wide (immediate) instructions +//===----------------------------------------------------------------------===// + +def LO16 : SDNodeXFormgetZExtValue() & 0xFFFF); +}]>; + +def HI16 : SDNodeXFormgetZExtValue() >> 16) & 0xFFFF); +}]>; + +def immZExt16 : PatLeaf<(imm), [{ + return (uint32_t)N->getZExtValue() == (unsigned short)N->getZExtValue(); +}], LO16>; + +def immLow16Zero : PatLeaf<(imm), [{ + int64_t Val = N->getSExtValue(); + return isInt<32>(Val) && !(Val & 0xffff); +}]>; + +def fmov32_operand : Operand, PatLeaf<(f32 fpimm), [{ return EpiphanyImms::isFPImm(N->getValueAPF()); }]> { let PrintMethod = "printFPImmOperand";} + + +let isMoveImm = 1, isAsCheapAsAMove = 1, hasSideEffects = 0 in { + let Constraints = "$src = $rt" in { + def MOVTri : EP2INST<(outs GPR32:$rt), (ins GPR32:$src, i32imm:$imm16),"movt\t$rt, $imm16",[(set GPR32:$rt, (or (and GPR32:$src, 0xffff), immLow16Zero:$imm16))],NoItinerary>; + def MOVTri_nopat : EP2INST<(outs GPR32:$rt), (ins GPR32:$src, i32imm:$imm16),"movt\t$rt, $imm16",[],NoItinerary>; + + def MOVTri_nopat_f : EP2INST<(outs FPR32:$rt), (ins FPR32:$src, fmov32_operand:$imm16),"movt\t$rt, $imm16",[],NoItinerary>; + } + def MOVTri_nopat_nodstsrc : EP2INST<(outs GPR32:$rt), (ins i32imm:$imm16),"movt\t$rt, $imm16",[],NoItinerary>; + + def MOVri : EP2INST<(outs GPR32:$rt), (ins i32imm:$imm16), "mov\t$rt, $imm16", [(set GPR32:$rt, immZExt16:$imm16)], NoItinerary>; + def MOVri_nopat : EP2INST<(outs GPR32:$rt),(ins i32imm:$imm16), "mov\t$rt, $imm16", [], NoItinerary>; + + def MOVri_nopat_f : EP2INST<(outs FPR32:$rt),(ins fmov32_operand:$imm16), "mov\t$rt, $imm16", [], NoItinerary>; + // def FMOVsi : EP2INST<(outs FPR32:$Rd), (ins fmov32_operand:$Imm8), "fmov\t$Rd, $Imm8", [], NoItinerary>; +} + +//Arbitrary immediates - unfortunately selecting those instead of the rr version fails, so -> code. +// def : Pat<(i32 imm:$imm), (MOVTri_nopat (MOVri_nopat (imm:$imm)),(imm:$imm))>; +// def : Pat<(f32 fpimm:$imm), (MOVTri_nopat_f (MOVri_nopat_f (fmov32_operand:$imm)),(fmov32_operand:$imm))>; +def : Pat<(or GPR32:$src, 0xffff0000), (MOVTri_nopat GPR32:$src, 0xffff)>; + + +class BitconvertPat : Pat<(DstVT (bitconvert (SrcVT SrcRC:$src))), (COPY_TO_REGCLASS SrcRC:$src, DstRC)>; +def : BitconvertPat; +def : BitconvertPat; + +//===----------------------------------------------------------------------===// +// MOVrr +//===----------------------------------------------------------------------===// +let hasSideEffects = 0 in{ +def MOVww : EP2INST<(outs GPR32:$Rd),(ins GPR32:$Rn),"mov\t$Rd, $Rn",[(set GPR32:$Rd, GPR32:$Rn)],NoItinerary>; +def MOVss : EP2INST<(outs FPR32:$Rd),(ins FPR32:$Rn),"mov\t$Rd, $Rn",[(set FPR32:$Rd, FPR32:$Rn)],NoItinerary>; +// def FMOVws : EP3INST<(outs GPR32:$Rd),(ins FPR32:$Rn),"fmov\t$Rd, $Rn",[(set (i32 GPR32:$Rd), (i32 (bitconvert (f32 FPR32:$Rn))) )],NoItinerary>; +// def FMOVsw : EP3INST<(outs FPR32:$Rd),(ins GPR32:$Rn),"fmov\t$Rd, $Rn",[(set (f32 FPR32:$Rd), (f32 (bitconvert (i32 GPR32:$Rn))) )],NoItinerary>; +} + + +def cond_code_op : Operand { + let PrintMethod = "printCondCodeOperand"; +} + +// ins true, false, cond -> we prepend a "mov $Rd, $Rfalse" and make sure that the output ends up in the same $Rd +// this will create a redundant move in case $Rd already happens to be $Rfalse... but we only know this after RA, so we'll fix this in a post-RA pass + let Uses = [NZCV], Constraints = "$Rd = $Rm" in { + def MOVCCrr : EP4INST<(outs GPR32:$Rd), (ins GPR32:$Rn, GPR32:$Rm, cond_code_op:$Cond), "mov$Cond\t$Rd, $Rn", [], NoItinerary>; + def MOVCCss : EP4INST<(outs FPR32:$Rd), (ins FPR32:$Rn, FPR32:$Rm, cond_code_op:$Cond), "mov$Cond\t$Rd, $Rn", [], NoItinerary>; + } + def : Pat<(A64select_cc NZCV, GPR32:$Rn, GPR32:$Rm, (i32 imm:$Cond)), (MOVCCrr GPR32:$Rn, (MOVww GPR32:$Rm), (i32 imm:$Cond))>; + def : Pat<(A64select_cc NZCV, FPR32:$Rn, FPR32:$Rm, (i32 imm:$Cond)), (MOVCCss FPR32:$Rn, (MOVss FPR32:$Rm), (i32 imm:$Cond))>; + + + //===----------------------------------------------------------------------===// +// Compare and branch (immediate) +//===----------------------------------------------------------------------===// +def bcc_bimm_target : Operand { + // This label is a 19-bit offset from PC, scaled by the instruction-width: 4. + let PrintMethod = "printLabelOperand<24, 2>"; + let OperandType = "OPERAND_PCREL"; +} + +//===----------------------------------------------------------------------===// +// Conditional branch (immediate) instructions +//===----------------------------------------------------------------------===// +def cond_code : Operand, ImmLeaf= 0 && Imm <= 15;}]> { let PrintMethod = "printCondCodeOperand";} + +def Bcc : EP2INST<(outs),(ins cond_code:$Cond, bcc_bimm_target:$Label),"b$Cond $Label",[(A64br_cc NZCV, (i32 imm:$Cond), bb:$Label)],NoItinerary>{ + let Uses = [NZCV]; + let isBranch = 1; + let isTerminator = 1; +} + +//===----------------------------------------------------------------------===// +// Unconditional branch (immediate) instructions +//===----------------------------------------------------------------------===// +def blimm_target : Operand { + // This label is a 26-bit offset from PC, scaled by the instruction-width: 4. + let PrintMethod = "printLabelOperand<24, 2>"; + let OperandType = "OPERAND_PCREL"; +} + +let isBranch = 1 in { + def Bimm : EP1INST<(outs), (ins bcc_bimm_target:$Label),"b\t$Label", [(br bb:$Label)],NoItinerary> { + let isTerminator = 1; + let isBarrier = 1; + } + + def BLimm : EP1INST<(outs), (ins blimm_target:$Label),"bl\t$Label", [(EpiphanyCall tglobaladdr:$Label)],NoItinerary> { + let isCall = 1; + let Defs = [LR]; + } +} + +def : Pat<(EpiphanyCall texternalsym:$Label), (BLimm texternalsym:$Label)>; + +//===----------------------------------------------------------------------===// +// Unconditional branch (register) instructions +//===----------------------------------------------------------------------===// +// Contains: BR, BLR, RET, ERET, DRP. + +// Most of the notional opcode fields in the A64I_Breg format are fixed in A64 +// at the moment. +class A64I_BregImpl patterns, InstrItinClass itin = NoItinerary> + : EP1INST { + let isBranch = 1; + let isIndirectBranch = 1; +} + +// Note that these are not marked isCall or isReturn because as far as LLVM is +// concerned they're not. "ret" is just another jump unless it has been selected +// by LLVM as the function's return. + +let isBranch = 1 in { + def JRx : A64I_BregImpl<(outs), (ins GPR32:$Rn),"jr\t$Rn", [(brind GPR32:$Rn)]> { + let isBarrier = 1; + let isTerminator = 1; + } + + def JALRx : A64I_BregImpl<(outs), (ins GPR32:$Rn),"jalr\t$Rn", [(EpiphanyCall GPR32:$Rn)]> { + let isBarrier = 0; + let isCall = 1; + let Defs = [LR]; + } + + def RETx : A64I_BregImpl<(outs), (ins GPR32:$Rn),"rts\t$Rn", []> { + let isBarrier = 1; + let isTerminator = 1; + let isReturn = 1; + } + + // Create a separate pseudo-instruction for codegen to use so that we don't + // flag LR as used in every function. It'll be restored before the RET by the + // epilogue if it's legitimately used. + def RET : A64PseudoExpand<(outs), (ins), [(A64ret)], (RETx (ops LR))> { + let isTerminator = 1; + let isBarrier = 1; + let isReturn = 1; + } +} + +def RETAlias : InstAlias<"rts", (RETx LR)>; + + +//===----------------------------------------------------------------------===// +// Address generation patterns +//===----------------------------------------------------------------------===// + +class ADRP_ADD : Pat<(A64WrapperSmall addrop:$Hi16, addrop:$Lo16, (i32 imm)), (MOVTri_nopat (MOVri_nopat (addrop:$Lo16)),(addrop:$Hi16))>; + +def : ADRP_ADD; +def : ADRP_ADD; +def : ADRP_ADD; +def : ADRP_ADD; + +//===----------------------------------------------------------------------===// +// LS stuff +//===----------------------------------------------------------------------===// + +def byte_simm11 : Operand, ComplexPattern"> { let PrintMethod = "printOffsetUImm11Operand<1>"; } +def hword_simm11 : Operand, ComplexPattern"> { let PrintMethod = "printOffsetUImm11Operand<2>"; } +def word_simm11 : Operand, ComplexPattern"> { let PrintMethod = "printOffsetUImm11Operand<4>"; } +def dword_simm11 : Operand, ComplexPattern"> { let PrintMethod = "printOffsetUImm11Operand<8>"; } + +//===------------------------------ +// 2.1 Regular instructions +//===------------------------------ + +multiclass A64I_LDRSTR_unsigned size, bit v, + bit high_opc, string asmsuffix, + RegisterClass GPR, Operand params> { + // Unsigned immediate + def _STR : EP3INST<(outs), (ins GPR:$Rt, GPR32:$Rn, params:$UImm11),"str" # asmsuffix # "\t$Rt, [$Rn, $UImm11]",[], NoItinerary> { + let mayStore = 1; + } + def : InstAlias<"str" # asmsuffix # " $Rt, [$Rn]", (!cast(prefix # "_STR") GPR:$Rt, GPR32:$Rn, 0)>; + + def _LDR : EP3INST<(outs GPR:$Rt), (ins GPR32:$Rn, params:$UImm11),"ldr" # asmsuffix # "\t$Rt, [$Rn, $UImm11]",[], NoItinerary> { + let mayLoad = 1; + } + def : InstAlias<"ldr" # asmsuffix # " $Rt, [$Rn]", (!cast(prefix # "_LDR") GPR:$Rt, GPR32:$Rn, 0)>; + + // Register offset (four of these: load/store and Wm/Xm). + def _RO_LDR : EP3INST<(outs GPR:$Rt),(ins GPR32:$Rn, GPR32:$Rm),"ldr" # asmsuffix # "\t$Rt, [$Rn, $Rm]",[], NoItinerary>{ + let mayLoad = 1; + } + def : InstAlias<"ldr" # asmsuffix # " $Rt, [$Rn, $Rm]", (!cast(prefix # "_RO_LDR") GPR:$Rt, GPR32:$Rn, GPR32:$Rm)>; + + + def _RO_STR : EP3INST<(outs), (ins GPR:$Rt, GPR32:$Rn, GPR32:$Rm),"str" # asmsuffix # "\t$Rt, [$Rn, $Rm]",[], NoItinerary>{ + let mayStore = 1; + } + def : InstAlias<"str" # asmsuffix # " $Rt, [$Rn, $Rm]", (!cast(prefix # "_RO_STR") GPR:$Rt, GPR32:$Rn, GPR32:$Rm)>; + + // Post-indexed + def _PostInd_STR : EP3INST<(outs GPR32:$Rn_wb),(ins GPR:$Rt, GPR32:$Rn, params:$SImm11),"str" # asmsuffix # "\t$Rt, [$Rn], $SImm11",[], NoItinerary> { + let Constraints = "$Rn = $Rn_wb"; + let mayStore = 1; + + // Decoder only needed for unpredictability checking (FIXME). + } + + def _PostInd_LDR : EP3INST<(outs GPR:$Rt, GPR32:$Rn_wb),(ins GPR32:$Rn, params:$SImm11),"ldr" # asmsuffix # "\t$Rt, [$Rn], $SImm11",[], NoItinerary> { + let mayLoad = 1; + let Constraints = "$Rn = $Rn_wb"; + } + +} + + +defm LS8 : A64I_LDRSTR_unsigned<"LS8", 0b00, 0b0, 0b0, "b", GPR32, byte_simm11>; +defm LS16 : A64I_LDRSTR_unsigned<"LS16", 0b01, 0b0, 0b0, "h", GPR32, hword_simm11>; +defm LS32 : A64I_LDRSTR_unsigned<"LS32", 0b10, 0b0, 0b0, "w", GPR32, word_simm11>; +defm LSFP32 : A64I_LDRSTR_unsigned<"LSFP32", 0b10, 0b1, 0b0, "w", FPR32, word_simm11>; +defm LSFP64 : A64I_LDRSTR_unsigned<"LSFP64", 0b11, 0b1, 0b0, "d", DPR64, dword_simm11>; + + +//===----------------------------------------------------------------------===// +// LS patterns +//===----------------------------------------------------------------------===// + +//===------------------------------ +// 1. Basic infrastructural defs +//===------------------------------ + +// First, some simple classes for !foreach and !subst to use: +class Decls { + dag pattern; +} + +def decls : Decls; +def ALIGN; +def INST; +def OFFSET; +def SHIFT; + +// You can't use !subst on an actual immediate, but you *can* use it on an +// operand record that happens to match a single immediate. So we do. +def imm_eq0 : ImmLeaf; +def imm_eq1 : ImmLeaf; +def imm_eq2 : ImmLeaf; +def imm_eq3 : ImmLeaf; +def imm_eq4 : ImmLeaf; + +// If the low bits of a pointer are known to be 0 then an "or" is just as good +// as addition for computing an offset. This fragment forwards that check for +// TableGen's use. +def add_like_or : PatFrag<(ops node:$lhs, node:$rhs), (or node:$lhs, node:$rhs),[{ return CurDAG->isBaseWithConstantOffset(SDValue(N, 0));}]>; + +// Load/store (unsigned immediate) operations with relocations against global +// symbols (for lo12) are only valid if those symbols have correct alignment +// (since the immediate offset is divided by the access scale, it can't have a +// remainder). +// +// The guaranteed alignment is provided as part of the WrapperSmall +// operation, and checked against one of these. +def any_align : ImmLeaf; +def min_align2 : ImmLeaf= 2; }]>; +def min_align4 : ImmLeaf= 4; }]>; +def min_align8 : ImmLeaf= 8; }]>; +def min_align16 : ImmLeaf= 16; }]>; + +// "Normal" load/store instructions can be used on atomic operations, provided +// the ordering parameter is at most "monotonic". Anything above that needs +// special handling with acquire/release instructions. +class simple_load : PatFrag<(ops node:$ptr), (base node:$ptr), [{ return cast(N)->getOrdering() <= Monotonic;}]>; + +def atomic_load_simple_i8 : simple_load; +def atomic_load_simple_i16 : simple_load; +def atomic_load_simple_i32 : simple_load; + +class simple_store : PatFrag<(ops node:$ptr, node:$val), (base node:$ptr, node:$val), [{ return cast(N)->getOrdering() <= Monotonic;}]>; + +def atomic_store_simple_i8 : simple_store; +def atomic_store_simple_i16 : simple_store; +def atomic_store_simple_i32 : simple_store; + +//===------------------------------ +// 2. SImm11 and SImm9 +//===------------------------------ + +multiclass simm11_pats_byte { +def : Pat<(atomic_load_simple_i8 address), (LS8_LDR Base, Offset )>; +def : Pat<(atomic_store_simple_i8 address, GPR32:$Rt), (LS8_STR GPR32:$Rt, Base, Offset )>; +def : Pat<(zextloadi8 address), (LS8_LDR Base, Offset )>; +def : Pat<(extloadi8 address), (LS8_LDR Base, Offset )>; +def : Pat<(truncstorei8 GPR32:$Rt, address),(LS8_STR GPR32:$Rt, Base, Offset )>; +} + +multiclass simm11_pats_hword { +def : Pat<(atomic_load_simple_i16 address), (LS16_LDR Base, Offset )>; +def : Pat<(atomic_store_simple_i16 address, GPR32:$Rt), (LS16_STR GPR32:$Rt, Base, Offset )>; +def : Pat<(zextloadi16 address), (LS16_LDR Base, Offset )>; +def : Pat<(extloadi16 address), (LS16_LDR Base, Offset )>; +def : Pat<(truncstorei16 GPR32:$Rt, address),(LS16_STR GPR32:$Rt, Base, Offset )>; +} + +multiclass simm11_pats_word { +def : Pat<(atomic_load_simple_i32 address ), (LS32_LDR Base, Offset )>; +def : Pat<(atomic_store_simple_i32 address , GPR32:$Rt), (LS32_STR GPR32:$Rt, Base, Offset )>; +//32 native +def : Pat<(i32 (load address )), (LS32_LDR Base, Offset )>; +def : Pat<(f32 (load address )), (LSFP32_LDR Base, Offset )>; +def : Pat<(store (i32 GPR32:$Rt), address ), (LS32_STR GPR32:$Rt, Base, Offset )>; +def : Pat<(store (f32 FPR32:$Rt), address ), (LSFP32_STR FPR32:$Rt, Base, Offset )>; +} + +def tframeindex_XFORM : SDNodeXForm(N)->getIndex(); return CurDAG->getTargetFrameIndex(FI, MVT::i32);}]>; + +///############################################################################################### +// Straightforward patterns of last resort: a pointer with or without an +// appropriate offset. +defm : simm11_pats_byte<(i32 GPR32:$Rn), (i32 GPR32:$Rn), (i32 0)>; +defm : simm11_pats_hword<(i32 GPR32:$Rn), (i32 GPR32:$Rn), (i32 0)>; +defm : simm11_pats_word<(i32 GPR32:$Rn), (i32 GPR32:$Rn), (i32 0)>; + +defm : simm11_pats_byte<(add GPR32:$Rn, byte_simm11:$SImm11), (i32 GPR32:$Rn), (i32 byte_simm11:$SImm11)>; +defm : simm11_pats_hword<(add GPR32:$Rn, hword_simm11:$SImm11), (i32 GPR32:$Rn), (i32 hword_simm11:$SImm11)>; +defm : simm11_pats_word<(add GPR32:$Rn, word_simm11:$SImm11), (i32 GPR32:$Rn), (i32 word_simm11:$SImm11)>; +// The offset could be hidden behind an "or", of course: +defm : simm11_pats_byte<(add_like_or GPR32:$Rn, byte_simm11:$SImm11), (i32 GPR32:$Rn), (i32 byte_simm11:$SImm11)>; +defm : simm11_pats_hword<(add_like_or GPR32:$Rn, hword_simm11:$SImm11), (i32 GPR32:$Rn), (i32 hword_simm11:$SImm11)>; +defm : simm11_pats_word<(add_like_or GPR32:$Rn, word_simm11:$SImm11), (i32 GPR32:$Rn), (i32 word_simm11:$SImm11)>; +// Global addresses under the small-absolute model should use these +// instructions. There are ELF relocations specifically for it. +defm : simm11_pats_byte<(A64WrapperSmall tglobaladdr:$Hi, tglobaladdr:$Lo12, any_align), (MOVTri_nopat (MOVri_nopat (tglobaladdr:$Lo12)),(tglobaladdr:$Hi)), (i32 0)>; +defm : simm11_pats_hword<(A64WrapperSmall tglobaladdr:$Hi, tglobaladdr:$Lo12, min_align2), (MOVTri_nopat (MOVri_nopat (tglobaladdr:$Lo12)),(tglobaladdr:$Hi)), (i32 0)>; +defm : simm11_pats_word<(A64WrapperSmall tglobaladdr:$Hi, tglobaladdr:$Lo12, min_align4), (MOVTri_nopat (MOVri_nopat (tglobaladdr:$Lo12)),(tglobaladdr:$Hi)), (i32 0)>; + +// External symbols that make it this far should also get standard relocations. +defm : simm11_pats_byte<(A64WrapperSmall texternalsym:$Hi, texternalsym:$Lo12, any_align), (MOVTri_nopat (MOVri_nopat (texternalsym:$Lo12)),(texternalsym:$Hi)), (i32 0)>; +defm : simm11_pats_hword<(A64WrapperSmall texternalsym:$Hi, texternalsym:$Lo12, min_align2), (MOVTri_nopat (MOVri_nopat (texternalsym:$Lo12)),(texternalsym:$Hi)), (i32 0)>; +defm : simm11_pats_word<(A64WrapperSmall texternalsym:$Hi, texternalsym:$Lo12, min_align4), (MOVTri_nopat (MOVri_nopat (texternalsym:$Lo12)),(texternalsym:$Hi)), (i32 0)>; + +defm : simm11_pats_byte<(A64WrapperSmall tconstpool:$Hi, tconstpool:$Lo12, any_align), (MOVTri_nopat (MOVri_nopat (tconstpool:$Lo12)),(tconstpool:$Hi)), (i32 0)>; +defm : simm11_pats_hword<(A64WrapperSmall tconstpool:$Hi, tconstpool:$Lo12, min_align2), (MOVTri_nopat (MOVri_nopat (tconstpool:$Lo12)),(tconstpool:$Hi)), (i32 0)>; +defm : simm11_pats_word<(A64WrapperSmall tconstpool:$Hi, tconstpool:$Lo12, min_align4), (MOVTri_nopat (MOVri_nopat (tconstpool:$Lo12)),(tconstpool:$Hi)), (i32 0)>; + +// We also want to use simm11 instructions for local variables at the moment. +defm : simm11_pats_byte<(i32 frameindex:$Rn), (tframeindex_XFORM tframeindex:$Rn), (i32 0)>; +defm : simm11_pats_hword<(i32 frameindex:$Rn), (tframeindex_XFORM tframeindex:$Rn), (i32 0)>; +defm : simm11_pats_word<(i32 frameindex:$Rn), (tframeindex_XFORM tframeindex:$Rn), (i32 0)>; +///############################################################################################### + + +//===------------------------------ +// 3. Register offset patterns +//===------------------------------ + +multiclass regoff_pats { + +def : Pat<(atomic_load_simple_i8 address ), (LS8_RO_LDR Base, Offset)>; +def : Pat<(atomic_load_simple_i16 address ), (LS16_RO_LDR Base, Offset)>; +def : Pat<(atomic_load_simple_i32 address ), (LS32_RO_LDR Base, Offset)>; + +def : Pat<(atomic_store_simple_i8 address, GPR32:$Rt), (LS8_RO_STR GPR32:$Rt, Base, Offset)>; +def : Pat<(atomic_store_simple_i16 address, GPR32:$Rt), (LS16_RO_STR GPR32:$Rt, Base, Offset)>; +def : Pat<(atomic_store_simple_i32 address, GPR32:$Rt), (LS32_RO_STR GPR32:$Rt, Base, Offset)>; + +def : Pat<(zextloadi8 address ), (LS8_RO_LDR Base, Offset)>; +def : Pat<(zextloadi16 address ), (LS16_RO_LDR Base, Offset)>; + +def : Pat<(extloadi8 address ), (LS8_RO_LDR Base, Offset)>; +def : Pat<(extloadi16 address ), (LS16_RO_LDR Base, Offset)>; + +def : Pat<(truncstorei8 GPR32:$Rt, address ),(LS8_RO_STR GPR32:$Rt, Base, Offset)>; +def : Pat<(truncstorei16 GPR32:$Rt, address ),(LS16_RO_STR GPR32:$Rt, Base, Offset)>; + +//32 native +def : Pat<(i32 (load address) ), (LS32_RO_LDR Base, Offset)>; +def : Pat<(f32 (load address) ), (LSFP32_RO_LDR Base, Offset)>; + +def : Pat<(store (i32 GPR32:$Rt), address ), (LS32_RO_STR GPR32:$Rt, Base, Offset)>; +def : Pat<(store (f32 FPR32:$Rt), address ), (LSFP32_RO_STR FPR32:$Rt, Base, Offset)>; +} + +defm : regoff_pats<(add GPR32:$Rn, GPR32:$Rm), (i32 GPR32:$Rn), (i32 GPR32:$Rm)>; diff --git a/EpiphanyLSOptPass.cpp b/EpiphanyLSOptPass.cpp new file mode 100644 index 0000000..cbeb649 --- /dev/null +++ b/EpiphanyLSOptPass.cpp @@ -0,0 +1,535 @@ +//===-- EpiphanyLoadStoreOptimizer.cpp - Epiphany load / store opt. pass ------------===// +// +// 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 pass that performs load / store related peephole +// optimizations. This pass should be run after register allocation. +// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "epiphany-ldst-opt" +#include "Epiphany.h" +#include "EpiphanyInstrInfo.h" +#include "EpiphanyRegisterInfo.h" +#include "EpiphanyMachineFunctionInfo.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/RegisterScavenging.h" +#include "llvm/CodeGen/SelectionDAGNodes.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Function.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Target/TargetInstrInfo.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Target/TargetRegisterInfo.h" +using namespace llvm; + + +STATISTIC(NumLdStMoved, "Number of load / store instructions moved"); +STATISTIC(NumLDRDFormed,"Number of ldrd created before allocation"); +STATISTIC(NumSTRDFormed,"Number of strd created before allocation"); + + +/// isMemoryOp - Returns true if instruction is a memory operation that this +/// pass is capable of operating on. +static bool isMemoryOp(const MachineInstr *MI) { + // When no memory operands are present, conservatively assume unaligned, + // volatile, unfoldable. + if (!MI->hasOneMemOperand()) + return false; + + const MachineMemOperand *MMO = *MI->memoperands_begin(); + + // Don't touch volatile memory accesses - we may be changing their order. + if (MMO->isVolatile()) + return false; + + // Unaligned ldr/str is emulated by some kernels, but unaligned ldm/stm is + // not. + if (MMO->getAlignment() < 4) + return false; + + // str could probably be eliminated entirely, but for now we just want + // to avoid making a mess of it. + // FIXME: Use str as a wildcard to enable better stm folding. + if (MI->getNumOperands() > 0 && MI->getOperand(0).isReg() && + MI->getOperand(0).isUndef()) + return false; + + // Likewise don't mess with references to undefined addresses. + if (MI->getNumOperands() > 1 && MI->getOperand(1).isReg() && + MI->getOperand(1).isUndef()) + return false; + + int Opcode = MI->getOpcode(); + switch (Opcode) { + default: break; + case Epiphany::LS32_LDR: + case Epiphany::LSFP32_LDR: + case Epiphany::LS32_STR: + case Epiphany::LSFP32_STR: + return MI->getOperand(1).isReg(); + } + return false; +} + +static int getMemoryOpOffset(const MachineInstr *MI) { + int Opcode = MI->getOpcode(); + unsigned NumOperands = MI->getDesc().getNumOperands(); + return MI->getOperand(NumOperands-1).getImm() * 4;//only 32bit +} + +/// EpiphanyPreAllocLoadStoreOpt - Pre- register allocation pass that move +/// load / stores from consecutive locations close to make it more +/// likely they will be combined later. + +namespace { + struct EpiphanyPreAllocLoadStoreOpt : public MachineFunctionPass{ + static char ID; + EpiphanyPreAllocLoadStoreOpt() : MachineFunctionPass(ID) {} + + const DataLayout *TD; + const TargetInstrInfo *TII; + const TargetRegisterInfo *TRI; + MachineRegisterInfo *MRI; + MachineFunction *MF; + + virtual bool runOnMachineFunction(MachineFunction &Fn); + + virtual const char *getPassName() const { + return "Epiphany pre- register allocation load / store optimization pass"; + } + + private: + bool CanFormLdStDWord(MachineInstr *Op0, MachineInstr *Op1, DebugLoc &dl, + unsigned &NewOpc, unsigned &EvenReg, + unsigned &OddReg, unsigned &BaseReg, + int &Offset); + bool RescheduleOps(MachineBasicBlock *MBB, + SmallVector &Ops, + unsigned Base, bool isLd, + DenseMap &MI2LocMap); + bool RescheduleLoadStoreInstrs(MachineBasicBlock *MBB); + }; + char EpiphanyPreAllocLoadStoreOpt::ID = 0; +} + +bool EpiphanyPreAllocLoadStoreOpt::runOnMachineFunction(MachineFunction &Fn) { + TD = Fn.getTarget().getDataLayout(); + TII = Fn.getTarget().getInstrInfo(); + TRI = Fn.getTarget().getRegisterInfo(); + MRI = &Fn.getRegInfo(); + MF = &Fn; + + bool Modified = false; + for (MachineFunction::iterator MFI = Fn.begin(), E = Fn.end(); MFI != E; + ++MFI) + Modified |= RescheduleLoadStoreInstrs(MFI); + + return Modified; +} + +static bool IsSafeAndProfitableToMove(bool isLd, unsigned Base, + MachineBasicBlock::iterator I, + MachineBasicBlock::iterator E, + SmallPtrSet &MemOps, + SmallSet &MemRegs, + const TargetRegisterInfo *TRI) { + // Are there stores / loads / calls between them? + // FIXME: This is overly conservative. We should make use of alias information + // some day. + SmallSet AddedRegPressure; + while (++I != E) { + if (I->isDebugValue() || MemOps.count(&*I)) + continue; + if (I->isCall() || I->isTerminator() || I->hasUnmodeledSideEffects()) + return false; + if (isLd && I->mayStore()) + return false; + if (!isLd) { + if (I->mayLoad()) + return false; + // It's not safe to move the first 'str' down. + // str r1, [r0] + // strh r5, [r0] + // str r4, [r0, #+4] + if (I->mayStore()) + return false; + } + for (unsigned j = 0, NumOps = I->getNumOperands(); j != NumOps; ++j) { + MachineOperand &MO = I->getOperand(j); + if (!MO.isReg()) + continue; + unsigned Reg = MO.getReg(); + if (MO.isDef() && TRI->regsOverlap(Reg, Base)) + return false; + if (Reg != Base && !MemRegs.count(Reg)) + AddedRegPressure.insert(Reg); + } + } + + // Estimate register pressure increase due to the transformation. + if (MemRegs.size() <= 4) + // Ok if we are moving small number of instructions. + return true; + return AddedRegPressure.size() <= MemRegs.size() * 2; +} + + +/// Copy Op0 and Op1 operands into a new array assigned to MI. +static void concatenateMemOperands(MachineInstr *MI, MachineInstr *Op0, + MachineInstr *Op1) { + assert(MI->memoperands_empty() && "expected a new machineinstr"); + size_t numMemRefs = (Op0->memoperands_end() - Op0->memoperands_begin()) + + (Op1->memoperands_end() - Op1->memoperands_begin()); + + MachineFunction *MF = MI->getParent()->getParent(); + MachineSDNode::mmo_iterator MemBegin = MF->allocateMemRefsArray(numMemRefs); + MachineSDNode::mmo_iterator MemEnd = + std::copy(Op0->memoperands_begin(), Op0->memoperands_end(), MemBegin); + MemEnd = + std::copy(Op1->memoperands_begin(), Op1->memoperands_end(), MemEnd); + MI->setMemRefs(MemBegin, MemEnd); +} + +bool +EpiphanyPreAllocLoadStoreOpt::CanFormLdStDWord(MachineInstr *Op0, MachineInstr *Op1, + DebugLoc &dl, + unsigned &NewOpc, unsigned &EvenReg, + unsigned &OddReg, unsigned &BaseReg, + int &Offset) { + + unsigned Opcode = Op0->getOpcode(); + switch(Op0->getOpcode()){ + case Epiphany::LS32_LDR: + case Epiphany::LSFP32_LDR: NewOpc = Epiphany::LSFP64_LDR;break; + case Epiphany::LSFP32_STR: + case Epiphany::LS32_STR: NewOpc = Epiphany::LSFP64_STR;break; + default: return false; + } + + // Make sure the base address satisfies i64 ld / st alignment requirement. + if (!Op0->hasOneMemOperand() || !(*Op0->memoperands_begin())->getValue() || (*Op0->memoperands_begin())->isVolatile()) + return false; + + unsigned Align = (*Op0->memoperands_begin())->getAlignment(); + const Function *Func = MF->getFunction(); + if (Align < 8) + return false; + + // Then make sure the immediate offset fits. + Offset = getMemoryOpOffset(Op0); + + EvenReg = Op0->getOperand(0).getReg(); + OddReg = Op1->getOperand(0).getReg(); + if (EvenReg == OddReg) + return false; + BaseReg = Op0->getOperand(1).getReg(); + dl = Op0->getDebugLoc(); + return true; +} + +namespace { + struct OffsetCompare { + bool operator()(const MachineInstr *LHS, const MachineInstr *RHS) const { + int LOffset = getMemoryOpOffset(LHS); + int ROffset = getMemoryOpOffset(RHS); + assert(LHS == RHS || LOffset != ROffset); + return LOffset > ROffset; + } + }; +} + +bool EpiphanyPreAllocLoadStoreOpt::RescheduleOps(MachineBasicBlock *MBB, + SmallVector &Ops, + unsigned Base, bool isLd, + DenseMap &MI2LocMap) { + bool RetVal = false; + + // Sort by offset (in reverse order). + std::sort(Ops.begin(), Ops.end(), OffsetCompare()); + + // The loads / stores of the same base are in order. Scan them from first to + // last and check for the following: + // 1. Any def of base. + // 2. Any gaps. + while (Ops.size() > 1) { + unsigned FirstLoc = ~0U; + unsigned LastLoc = 0; + MachineInstr *FirstOp = 0; + MachineInstr *LastOp = 0; + int LastOffset = 0; + //unsigned LastOpcode = 0; + unsigned LastBytes = 0; + unsigned NumMove = 0; + for (int i = Ops.size() - 1; i >= 0; --i) { + MachineInstr *Op = Ops[i]; + unsigned Loc = MI2LocMap[Op]; + if (Loc <= FirstLoc) { + FirstLoc = Loc; + FirstOp = Op; + } + if (Loc >= LastLoc) { + LastLoc = Loc; + LastOp = Op; + } + + //unsigned LSMOpcode = 0;//getLoadStoreMultipleOpcode(Op->getOpcode(), Epiphany_AM::ia); + //if (LastOpcode && LSMOpcode != LastOpcode) + // break; + + int Offset = getMemoryOpOffset(Op); + unsigned Bytes = 4;//getLSMultipleTransferSize(Op); + if (LastBytes) { + if (Bytes != LastBytes || Offset != (LastOffset + (int)Bytes)) + break; + } + LastOffset = Offset; + LastBytes = Bytes; + //LastOpcode = LSMOpcode; + if (++NumMove == 8) // FIXME: Tune this limit. + break; + } + + if (NumMove <= 1) + Ops.pop_back(); + else { + SmallPtrSet MemOps; + SmallSet MemRegs; + for (int i = NumMove-1; i >= 0; --i) { + MemOps.insert(Ops[i]); + MemRegs.insert(Ops[i]->getOperand(0).getReg()); + } + + // Be conservative, if the instructions are too far apart, don't + // move them. We want to limit the increase of register pressure. + bool DoMove = (LastLoc - FirstLoc) <= NumMove*4; // FIXME: Tune this. + if (DoMove) + DoMove = IsSafeAndProfitableToMove(isLd, Base, FirstOp, LastOp, + MemOps, MemRegs, TRI); + if (!DoMove) { + for (unsigned i = 0; i != NumMove; ++i) + Ops.pop_back(); + } else { + // This is the new location for the loads / stores. + MachineBasicBlock::iterator InsertPos = isLd ? FirstOp : LastOp; + while (InsertPos != MBB->end() && (MemOps.count(InsertPos) || InsertPos->isDebugValue())) + ++InsertPos; + + // If we are moving a pair of loads / stores, see if it makes sense + // to try to allocate a pair of registers that can form register pairs. + MachineInstr *Op0 = Ops.back(); + MachineInstr *Op1 = Ops[Ops.size()-2]; + unsigned EvenReg = 0, OddReg = 0; + unsigned BaseReg = 0, DoubleReg = 0; + //bool isT2 = false; + unsigned NewOpc = 0; + int Offset = 0; + DebugLoc dl; + if (NumMove == 2 && CanFormLdStDWord(Op0, Op1, dl, NewOpc, + EvenReg, OddReg, BaseReg, + Offset)) { + Ops.pop_back(); + Ops.pop_back(); + + const MCInstrDesc &MCID = TII->get(NewOpc); + const TargetRegisterClass *TRC = TII->getRegClass(MCID, 0, TRI, *MF); + DoubleReg = MRI->createVirtualRegister(TRC); + + + // Form the pair instruction. + if (isLd) { + MachineInstrBuilder MIB = BuildMI(*MBB, InsertPos, dl, MCID) + .addReg(DoubleReg, RegState::Define) + .addReg(BaseReg); + MIB.addImm(Offset); + concatenateMemOperands(MIB, Op0, Op1); + DEBUG(dbgs() << "Formed " << *MIB << "\n"); + ++NumLDRDFormed; + + //FIXME: i think this is kinda bad, use EXTRACT_SUBREG instead? + + // Collect all the uses of this MI's DPR def for updating later. + SmallVector Uses0,Uses1; + + for (MachineRegisterInfo::use_iterator I = MRI->use_begin(Op0->getOperand(0).getReg()),E = MRI->use_end(); I != E; ++I) + Uses0.push_back(&I.getOperand()); + + for (MachineRegisterInfo::use_iterator I = MRI->use_begin(Op1->getOperand(0).getReg()),E = MRI->use_end(); I != E; ++I) + Uses1.push_back(&I.getOperand()); + + for (SmallVector::const_iterator I = Uses0.begin(), E = Uses0.end(); I != E; ++I) + (*I)->substVirtReg(DoubleReg, Epiphany::sub_even, *TRI); + + for (SmallVector::const_iterator I = Uses1.begin(), E = Uses1.end(); I != E; ++I) + (*I)->substVirtReg(DoubleReg, Epiphany::sub_odd, *TRI); + + } else {//store + + //build our dpr + BuildMI(*MBB, InsertPos, dl, TII->get(Epiphany::REG_SEQUENCE), DoubleReg) + .addReg(Op0->getOperand(0).getReg()) + .addImm(Epiphany::sub_even) + .addReg(Op1->getOperand(0).getReg()) + .addImm(Epiphany::sub_odd); + + + MachineInstrBuilder MIB = BuildMI(*MBB, InsertPos, dl, MCID) + .addReg(DoubleReg, RegState::Kill) + .addReg(BaseReg); + MIB.addImm(Offset); + concatenateMemOperands(MIB, Op0, Op1); + DEBUG(dbgs() << "Formed " << *MIB << "\n"); + ++NumSTRDFormed; + + } + MBB->erase(Op0); + MBB->erase(Op1); + + } else { + for (unsigned i = 0; i != NumMove; ++i) { + MachineInstr *Op = Ops.back(); + Ops.pop_back(); + MBB->splice(InsertPos, MBB, Op); + } + } + + NumLdStMoved += NumMove; + RetVal = true; + } + } + } + + return RetVal; +} + +bool +EpiphanyPreAllocLoadStoreOpt::RescheduleLoadStoreInstrs(MachineBasicBlock *MBB) { + bool RetVal = false; + + DenseMap MI2LocMap; + DenseMap > Base2LdsMap; + DenseMap > Base2StsMap; + SmallVector LdBases; + SmallVector StBases; + + unsigned Loc = 0; + MachineBasicBlock::iterator MBBI = MBB->begin(); + MachineBasicBlock::iterator E = MBB->end(); + while (MBBI != E) { + for (; MBBI != E; ++MBBI) { + MachineInstr *MI = MBBI; + if (MI->isCall() || MI->isTerminator()) { + // Stop at barriers. + ++MBBI; + break; + } + + if (!MI->isDebugValue()) + MI2LocMap[MI] = ++Loc; + + if (!isMemoryOp(MI)) + continue; + + int Opc = MI->getOpcode(); + bool isLd = (Opc == Epiphany::LS32_LDR || Opc == Epiphany::LSFP32_LDR); + unsigned Base = MI->getOperand(1).getReg(); + int Offset = getMemoryOpOffset(MI); + + bool StopHere = false; + if (isLd) { + DenseMap >::iterator BI = + Base2LdsMap.find(Base); + if (BI != Base2LdsMap.end()) { + for (unsigned i = 0, e = BI->second.size(); i != e; ++i) { + if (Offset == getMemoryOpOffset(BI->second[i])) { + StopHere = true; + break; + } + } + if (!StopHere) + BI->second.push_back(MI); + } else { + SmallVector MIs; + MIs.push_back(MI); + Base2LdsMap[Base] = MIs; + LdBases.push_back(Base); + } + } else { + DenseMap >::iterator BI = + Base2StsMap.find(Base); + if (BI != Base2StsMap.end()) { + for (unsigned i = 0, e = BI->second.size(); i != e; ++i) { + if (Offset == getMemoryOpOffset(BI->second[i])) { + StopHere = true; + break; + } + } + if (!StopHere) + BI->second.push_back(MI); + } else { + SmallVector MIs; + MIs.push_back(MI); + Base2StsMap[Base] = MIs; + StBases.push_back(Base); + } + } + + if (StopHere) { + // Found a duplicate (a base+offset combination that's seen earlier). + // Backtrack. + --Loc; + break; + } + } + + // Re-schedule loads. + for (unsigned i = 0, e = LdBases.size(); i != e; ++i) { + unsigned Base = LdBases[i]; + SmallVector &Lds = Base2LdsMap[Base]; + if (Lds.size() > 1) + RetVal |= RescheduleOps(MBB, Lds, Base, true, MI2LocMap); + } + + // Re-schedule stores. + for (unsigned i = 0, e = StBases.size(); i != e; ++i) { + unsigned Base = StBases[i]; + SmallVector &Sts = Base2StsMap[Base]; + if (Sts.size() > 1) + RetVal |= RescheduleOps(MBB, Sts, Base, false, MI2LocMap); + } + + if (MBBI != E) { + Base2LdsMap.clear(); + Base2StsMap.clear(); + LdBases.clear(); + StBases.clear(); + } + } + + return RetVal; +} + + +/// createEpiphanyLoadStoreOptimizationPass - returns an instance of the load / store +/// optimization pass. +FunctionPass *llvm::createEpiphanyLSOptPass() { + return new EpiphanyPreAllocLoadStoreOpt(); +} diff --git a/EpiphanyMCInstLower.cpp b/EpiphanyMCInstLower.cpp new file mode 100644 index 0000000..3ff6c4a --- /dev/null +++ b/EpiphanyMCInstLower.cpp @@ -0,0 +1,120 @@ +//===-- EpiphanyMCInstLower.cpp - Convert Epiphany MachineInstr to an 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 Epiphany MachineInstrs to their corresponding +// MCInst records. +// +//===----------------------------------------------------------------------===// + +#include "EpiphanyAsmPrinter.h" +#include "EpiphanyTargetMachine.h" +#include "MCTargetDesc/EpiphanyMCExpr.h" +#include "Utils/EpiphanyBaseInfo.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCInst.h" +#include "llvm/Target/Mangler.h" + +using namespace llvm; + +MCOperand +EpiphanyAsmPrinter::lowerSymbolOperand(const MachineOperand &MO, + const MCSymbol *Sym) const { + const MCExpr *Expr = 0; + + Expr = MCSymbolRefExpr::Create(Sym, MCSymbolRefExpr::VK_None, OutContext); + + switch (MO.getTargetFlags()) { + case EpiphanyII::MO_LO16: + Expr = EpiphanyMCExpr::CreateLo16(Expr, OutContext); + break; + case EpiphanyII::MO_HI16: + Expr = EpiphanyMCExpr::CreateHi16(Expr, OutContext); + break; + case EpiphanyII::MO_NO_FLAG: + // Expr is already correct + break; + default: + llvm_unreachable("Unexpected MachineOperand flag - lowersymboloperand"); + } + + if (!MO.isJTI() && MO.getOffset()) + Expr = MCBinaryExpr::CreateAdd(Expr, + MCConstantExpr::Create(MO.getOffset(), + OutContext), + OutContext); + + return MCOperand::CreateExpr(Expr); +} + +bool EpiphanyAsmPrinter::lowerOperand(const MachineOperand &MO, + MCOperand &MCOp) const { + switch (MO.getType()) { + default: llvm_unreachable("unknown operand type"); + case MachineOperand::MO_Register: + if (MO.isImplicit()) + return false; + assert(!MO.getSubReg() && "Subregs should be eliminated!"); + MCOp = MCOperand::CreateReg(MO.getReg()); + break; + case MachineOperand::MO_Immediate: + MCOp = MCOperand::CreateImm(MO.getImm()); + break; + case MachineOperand::MO_FPImmediate: {// a bit hacky, see arm + APFloat Val = MO.getFPImm()->getValueAPF(); + bool ignored; + Val.convert(APFloat::IEEEdouble, APFloat::rmTowardZero, &ignored); + MCOp = MCOperand::CreateFPImm(Val.convertToDouble()); + break; + } + case MachineOperand::MO_BlockAddress: + MCOp = lowerSymbolOperand(MO, GetBlockAddressSymbol(MO.getBlockAddress())); + break; + case MachineOperand::MO_ExternalSymbol: + MCOp = lowerSymbolOperand(MO, GetExternalSymbolSymbol(MO.getSymbolName())); + break; + case MachineOperand::MO_GlobalAddress: + MCOp = lowerSymbolOperand(MO, Mang->getSymbol(MO.getGlobal())); + break; + case MachineOperand::MO_MachineBasicBlock: + MCOp = MCOperand::CreateExpr(MCSymbolRefExpr::Create( + MO.getMBB()->getSymbol(), OutContext)); + break; + case MachineOperand::MO_JumpTableIndex: + MCOp = lowerSymbolOperand(MO, GetJTISymbol(MO.getIndex())); + break; + case MachineOperand::MO_ConstantPoolIndex: + MCOp = lowerSymbolOperand(MO, GetCPISymbol(MO.getIndex())); + break; + case MachineOperand::MO_RegisterMask: + // Ignore call clobbers + return false; + + } + + return true; +} + +void llvm::LowerEpiphanyMachineInstrToMCInst(const MachineInstr *MI, + MCInst &OutMI, + EpiphanyAsmPrinter &AP) { + OutMI.setOpcode(MI->getOpcode()); + + for (unsigned i = 0, e = MI->getNumOperands(); i != e; ++i) { + const MachineOperand &MO = MI->getOperand(i); + + MCOperand MCOp; + if (AP.lowerOperand(MO, MCOp)) + OutMI.addOperand(MCOp); + } +} diff --git a/EpiphanyMachineFunctionInfo.cpp b/EpiphanyMachineFunctionInfo.cpp new file mode 100644 index 0000000..d2bff76 --- /dev/null +++ b/EpiphanyMachineFunctionInfo.cpp @@ -0,0 +1,18 @@ +//===-- EpiphanyMachineFuctionInfo.cpp - Epiphany machine function info -----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file just contains the anchor for the EpiphanyMachineFunctionInfo to +// force vtable emission. +// +//===----------------------------------------------------------------------===// +#include "EpiphanyMachineFunctionInfo.h" + +using namespace llvm; + +void EpiphanyMachineFunctionInfo::anchor() { } diff --git a/EpiphanyMachineFunctionInfo.h b/EpiphanyMachineFunctionInfo.h new file mode 100644 index 0000000..1ef316e --- /dev/null +++ b/EpiphanyMachineFunctionInfo.h @@ -0,0 +1,149 @@ +//=- EpiphanyMachineFuctionInfo.h - Epiphany machine function 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 declares Epiphany-specific per-machine-function information. +// +//===----------------------------------------------------------------------===// + +#ifndef EPIPHANYMACHINEFUNCTIONINFO_H +#define EPIPHANYMACHINEFUNCTIONINFO_H + +#include "llvm/CodeGen/MachineFunction.h" + +namespace llvm { + +/// This class is derived from MachineFunctionInfo and contains private Epiphany +/// target-specific information for each MachineFunction. +class EpiphanyMachineFunctionInfo : public MachineFunctionInfo { + virtual void anchor(); + + /// Number of bytes of arguments this function has on the stack. If the callee + /// is expected to restore the argument stack this should be a multiple of 16, + /// all usable during a tail call. + /// + /// The alternative would forbid tail call optimisation in some cases: if we + /// want to transfer control from a function with 8-bytes of stack-argument + /// space to a function with 16-bytes then misalignment of this value would + /// make a stack adjustment necessary, which could not be undone by the + /// callee. + unsigned BytesInStackArgArea; + + /// The number of bytes to restore to deallocate space for incoming + /// arguments. Canonically 0 in the C calling convention, but non-zero when + /// callee is expected to pop the args. + unsigned ArgumentStackToRestore; + + /// If the stack needs to be adjusted on frame entry in two stages, this + /// records the size of the first adjustment just prior to storing + /// callee-saved registers. The callee-saved slots are addressed assuming + /// SP == - InitialStackAdjust. + unsigned InitialStackAdjust; + + /// Number of local-dynamic TLS accesses. + unsigned NumLocalDynamics; + + /// @see Epiphany Procedure Call Standard, B.3 + /// + /// The Frame index of the area where LowerFormalArguments puts the + /// general-purpose registers that might contain variadic parameters. + int VariadicGPRIdx; + + /// @see Epiphany Procedure Call Standard, B.3 + /// + /// The size of the frame object used to store the general-purpose registers + /// which might contain variadic arguments. This is the offset from + /// VariadicGPRIdx to what's stored in __gr_top. + unsigned VariadicGPRSize; + + /// @see Epiphany Procedure Call Standard, B.3 + /// + /// The Frame index of the area where LowerFormalArguments puts the + /// floating-point registers that might contain variadic parameters. + int VariadicFPRIdx; + + /// @see Epiphany Procedure Call Standard, B.3 + /// + /// The size of the frame object used to store the floating-point registers + /// which might contain variadic arguments. This is the offset from + /// VariadicFPRIdx to what's stored in __vr_top. + unsigned VariadicFPRSize; + + /// @see Epiphany Procedure Call Standard, B.3 + /// + /// The Frame index of an object pointing just past the last known stacked + /// argument on entry to a variadic function. This goes into the __stack field + /// of the va_list type. + int VariadicStackIdx; + + /// The offset of the frame pointer from the stack pointer on function + /// entry. This is expected to be negative. + int FramePointerOffset; + +public: + EpiphanyMachineFunctionInfo() + : BytesInStackArgArea(0), + ArgumentStackToRestore(0), + InitialStackAdjust(0), + NumLocalDynamics(0), + VariadicGPRIdx(0), + VariadicGPRSize(0), + VariadicFPRIdx(0), + VariadicFPRSize(0), + VariadicStackIdx(0), + FramePointerOffset(0) {} + + explicit EpiphanyMachineFunctionInfo(MachineFunction &MF) + : BytesInStackArgArea(0), + ArgumentStackToRestore(0), + InitialStackAdjust(0), + NumLocalDynamics(0), + VariadicGPRIdx(0), + VariadicGPRSize(0), + VariadicFPRIdx(0), + VariadicFPRSize(0), + VariadicStackIdx(0), + FramePointerOffset(0) {} + + unsigned getBytesInStackArgArea() const { return BytesInStackArgArea; } + void setBytesInStackArgArea (unsigned bytes) { BytesInStackArgArea = bytes;} + + unsigned getArgumentStackToRestore() const { return ArgumentStackToRestore; } + void setArgumentStackToRestore(unsigned bytes) { + ArgumentStackToRestore = bytes; + } + + unsigned getInitialStackAdjust() const { return InitialStackAdjust; } + void setInitialStackAdjust(unsigned bytes) { InitialStackAdjust = bytes; } + + unsigned getNumLocalDynamicTLSAccesses() const { return NumLocalDynamics; } + void incNumLocalDynamicTLSAccesses() { ++NumLocalDynamics; } + + int getVariadicGPRIdx() const { return VariadicGPRIdx; } + void setVariadicGPRIdx(int Idx) { VariadicGPRIdx = Idx; } + + unsigned getVariadicGPRSize() const { return VariadicGPRSize; } + void setVariadicGPRSize(unsigned Size) { VariadicGPRSize = Size; } + + int getVariadicFPRIdx() const { return VariadicFPRIdx; } + void setVariadicFPRIdx(int Idx) { VariadicFPRIdx = Idx; } + + unsigned getVariadicFPRSize() const { return VariadicFPRSize; } + void setVariadicFPRSize(unsigned Size) { VariadicFPRSize = Size; } + + int getVariadicStackIdx() const { return VariadicStackIdx; } + void setVariadicStackIdx(int Idx) { VariadicStackIdx = Idx; } + + int getFramePointerOffset() const { return FramePointerOffset; } + void setFramePointerOffset(int Idx) { FramePointerOffset = Idx; } + +}; + +} // End llvm namespace + +#endif diff --git a/EpiphanyRegisterInfo.cpp b/EpiphanyRegisterInfo.cpp new file mode 100644 index 0000000..45fec03 --- /dev/null +++ b/EpiphanyRegisterInfo.cpp @@ -0,0 +1,166 @@ +//===- EpiphanyRegisterInfo.cpp - Epiphany 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 Epiphany implementation of the TargetRegisterInfo +// class. +// +//===----------------------------------------------------------------------===// + + +#include "EpiphanyRegisterInfo.h" +#include "EpiphanyFrameLowering.h" +#include "EpiphanyMachineFunctionInfo.h" +#include "EpiphanyTargetMachine.h" +#include "MCTargetDesc/EpiphanyMCTargetDesc.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/RegisterScavenging.h" +#include "llvm/CodeGen/VirtRegMap.h" +#include "llvm/ADT/BitVector.h" + +#define GET_REGINFO_TARGET_DESC +#include "EpiphanyGenRegisterInfo.inc" + +using namespace llvm; + +EpiphanyRegisterInfo::EpiphanyRegisterInfo(const EpiphanyInstrInfo &tii, + const EpiphanySubtarget &sti) + : EpiphanyGenRegisterInfo(Epiphany::LR), TII(tii) { +} + +const uint16_t * +EpiphanyRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const { + return CSR_PCS_SaveList; +} + +const uint32_t* +EpiphanyRegisterInfo::getCallPreservedMask(CallingConv::ID) const { + return CSR_PCS_RegMask; +} + +const TargetRegisterClass * +EpiphanyRegisterInfo::getCrossCopyRegClass(const TargetRegisterClass *RC) const { + //if (RC == &Epiphany::FlagClassRegClass) + // return &Epiphany::GPR32RegClass; + + return RC; +} + + + +BitVector +EpiphanyRegisterInfo::getReservedRegs(const MachineFunction &MF) const { + BitVector Reserved(getNumRegs()); + const TargetFrameLowering *TFI = MF.getTarget().getFrameLowering(); + + Reserved.set(Epiphany::SP); + Reserved.set(Epiphany::R63);// hack: dst for cmp + //constants + Reserved.set(Epiphany::R28); + Reserved.set(Epiphany::R29); + Reserved.set(Epiphany::R30); + Reserved.set(Epiphany::R31); + + Reserved.set(Epiphany::NZCV); + + if (TFI->hasFP(MF)) { + Reserved.set(Epiphany::R11); + } + + return Reserved; +} + +void +EpiphanyRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator MBBI, + int SPAdj, + unsigned FIOperandNum, + RegScavenger *RS) const { + assert(SPAdj == 0 && "Cannot deal with nonzero SPAdj yet"); + MachineInstr &MI = *MBBI; + MachineBasicBlock &MBB = *MI.getParent(); + MachineFunction &MF = *MBB.getParent(); + MachineFrameInfo *MFI = MF.getFrameInfo(); + const EpiphanyFrameLowering *TFI = static_cast(MF.getTarget().getFrameLowering()); + + // In order to work out the base and offset for addressing, the FrameLowering + // code needs to know (sometimes) whether the instruction is storing/loading a + // callee-saved register, or whether it's a more generic + // operation. Fortunately the frame indices are used *only* for that purpose + // and are contiguous, so we can check here. + const std::vector &CSI = MFI->getCalleeSavedInfo(); + int MinCSFI = 0; + int MaxCSFI = -1; + + if (CSI.size()) { + MinCSFI = CSI[0].getFrameIdx(); + MaxCSFI = CSI[CSI.size() - 1].getFrameIdx(); + } + + int FrameIndex = MI.getOperand(FIOperandNum).getIndex(); + bool IsCalleeSaveOp = FrameIndex >= MinCSFI && FrameIndex <= MaxCSFI; + + unsigned FrameReg; + int64_t Offset; + Offset = TFI->resolveFrameIndexReference(MF, FrameIndex, FrameReg, SPAdj, IsCalleeSaveOp); + + Offset += MI.getOperand(FIOperandNum + 1).getImm(); + + // DBG_VALUE instructions have no real restrictions so they can be handled + // easily. + if (MI.isDebugValue()) { + MI.getOperand(FIOperandNum).ChangeToRegister(FrameReg, /*isDef=*/ false); + MI.getOperand(FIOperandNum + 1).ChangeToImmediate(Offset); + return; + } + + int MinOffset, MaxOffset, OffsetScale; + if (MI.getOpcode() == Epiphany::ADDri) { + MinOffset = -(0x3FF); + MaxOffset = 0x3FF; + OffsetScale = 1; + } else { + // Load/store of a stack object + TII.getAddressConstraints(MI, OffsetScale, MinOffset, MaxOffset); + } + + // The frame lowering has told us a base and offset it thinks we should use to + // access this variable, but it's still up to us to make sure the values are + // legal for the instruction in question. + if (Offset % OffsetScale != 0 || Offset < MinOffset || Offset > MaxOffset) { + unsigned BaseReg = MF.getRegInfo().createVirtualRegister(&Epiphany::GPR32RegClass); + EPIPHemitRegUpdate(MBB, MBBI, MBBI->getDebugLoc(), TII, BaseReg, FrameReg, BaseReg, Offset); + FrameReg = BaseReg; + Offset = 0; + } + + // Negative offsets are expected if we address from FP, but for + // now this checks nothing has gone horribly wrong. + assert(Offset >= 0 && "Unexpected negative offset from SP"); + + MI.getOperand(FIOperandNum).ChangeToRegister(FrameReg, false, false, true); + MI.getOperand(FIOperandNum + 1).ChangeToImmediate(Offset / OffsetScale); +} + +unsigned +EpiphanyRegisterInfo::getFrameRegister(const MachineFunction &MF) const { + const TargetFrameLowering *TFI = MF.getTarget().getFrameLowering(); + + if (TFI->hasFP(MF)) + return Epiphany::R11; + else + return Epiphany::SP; +} + +bool +EpiphanyRegisterInfo::useFPForScavengingIndex(const MachineFunction &MF) const { + const TargetFrameLowering *TFI = MF.getTarget().getFrameLowering(); + const EpiphanyFrameLowering *AFI = static_cast(TFI); + return AFI->useFPForAddressing(MF); +} diff --git a/EpiphanyRegisterInfo.h b/EpiphanyRegisterInfo.h new file mode 100644 index 0000000..c626653 --- /dev/null +++ b/EpiphanyRegisterInfo.h @@ -0,0 +1,84 @@ +//==- EpiphanyRegisterInfo.h - Epiphany 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 Epiphany implementation of the MCRegisterInfo class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TARGET_EPIPHANYREGISTERINFO_H +#define LLVM_TARGET_EPIPHANYREGISTERINFO_H + +#include "llvm/Target/TargetRegisterInfo.h" + +#define GET_REGINFO_HEADER +#include "EpiphanyGenRegisterInfo.inc" + +namespace llvm { + +class EpiphanyInstrInfo; +class EpiphanySubtarget; + +/// Register allocation hints. +namespace EPIPHANYRI { + enum { + RegPairOdd = 1, + RegPairEven = 2 + }; +} + +struct EpiphanyRegisterInfo : public EpiphanyGenRegisterInfo { +private: + const EpiphanyInstrInfo &TII; + +public: + EpiphanyRegisterInfo(const EpiphanyInstrInfo &tii, + const EpiphanySubtarget &sti); + + const uint16_t *getCalleeSavedRegs(const MachineFunction *MF = 0) const; + const uint32_t *getCallPreservedMask(CallingConv::ID) const; + + const uint32_t *getTLSDescCallPreservedMask() const; + + BitVector getReservedRegs(const MachineFunction &MF) const; + unsigned getFrameRegister(const MachineFunction &MF) const; + + void eliminateFrameIndex(MachineBasicBlock::iterator II, int SPAdj, + unsigned FIOperandNum, + RegScavenger *Rs = NULL) const; + + /// getCrossCopyRegClass - Returns a legal register class to copy a register + /// in the specified class to or from. Returns original class if it is + /// possible to copy between a two registers of the specified class. + const TargetRegisterClass * + getCrossCopyRegClass(const TargetRegisterClass *RC) const; + + /// getLargestLegalSuperClass - Returns the largest super class of RC that is + /// legal to use in the current sub-target and has the same spill size. + const TargetRegisterClass* + getLargestLegalSuperClass(const TargetRegisterClass *RC) const { + //if (RC == &Epiphany::tcGPR32RegClass) + // return &Epiphany::GPR32RegClass; + + return RC; + } + + bool requiresRegisterScavenging(const MachineFunction &MF) const { + return true; + } + + bool requiresFrameIndexScavenging(const MachineFunction &MF) const { + return true; + } + + bool useFPForScavengingIndex(const MachineFunction &MF) const; +}; + +} // end namespace llvm + +#endif // LLVM_TARGET_EPIPHANYREGISTERINFO_H diff --git a/EpiphanyRegisterInfo.td b/EpiphanyRegisterInfo.td new file mode 100644 index 0000000..75e2916 --- /dev/null +++ b/EpiphanyRegisterInfo.td @@ -0,0 +1,102 @@ +//===- EpiphanyRegisterInfo.td - ARM Register defs ----------*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains declarations that describe the Epiphany register file +// +//===----------------------------------------------------------------------===// + +let Namespace = "Epiphany" in { +def sub_even : SubRegIndex; +def sub_odd : SubRegIndex; +} + +// Registers are identified with 5-bit ID numbers. +class EpiphanyReg enc, string n> : Register { + let HWEncoding = enc; + let Namespace = "Epiphany"; +} + +class EpiphanyRegWithSubs enc, string n, list subregs = []> : EpiphanyReg { + let SubRegs = subregs; + let CoveredBySubRegs = 1; +} + +//===----------------------------------------------------------------------===// +// Integer registers: w0-w30, wzr, wsp, x0-x30, xzr, sp +//===----------------------------------------------------------------------===// + +foreach Index = 0-12 in { + def R#Index : EpiphanyReg< Index, "r"#Index>, DwarfRegNum<[Index]>; +} + +def SP : EpiphanyReg<13, "sp">, DwarfRegNum<[13]>; +def LR : EpiphanyReg<14, "lr">, DwarfRegNum<[14]>; + +foreach Index = 15-63 in { + def R#Index : EpiphanyReg< Index, "r"#Index>, DwarfRegNum<[Index]>; +} + +let SubRegIndices = [sub_even, sub_odd] in { +def D0 : EpiphanyRegWithSubs<0, "d0", [R0, R1]>, DwarfRegNum<[0,1]>; +def D1 : EpiphanyRegWithSubs<2, "d1", [R2, R3]>, DwarfRegNum<[2,3]>; +def D2 : EpiphanyRegWithSubs<4, "d2", [R4, R5]>, DwarfRegNum<[4,5]>; +def D3 : EpiphanyRegWithSubs<6, "d3", [R6, R7]>, DwarfRegNum<[6,7]>; +def D4 : EpiphanyRegWithSubs<8, "d4", [R8, R9]>, DwarfRegNum<[8,9]>; +def D5 : EpiphanyRegWithSubs<10, "d5", [R10, R11]>, DwarfRegNum<[10,11]>; + +def D6 : EpiphanyRegWithSubs<12, "d6", [R12, SP]>, DwarfRegNum<[12,13]>; +def D7 : EpiphanyRegWithSubs<14, "d7", [LR, R15]>, DwarfRegNum<[14,15]>; + +def D8 : EpiphanyRegWithSubs<16, "d8", [R16, R17]>, DwarfRegNum<[16,17]>; +def D9 : EpiphanyRegWithSubs<18, "d9", [R18, R19]>, DwarfRegNum<[18,19]>; +def D10 : EpiphanyRegWithSubs<20, "d10", [R20, R21]>, DwarfRegNum<[20,21]>; +def D11 : EpiphanyRegWithSubs<22, "d11", [R22, R23]>, DwarfRegNum<[22,23]>; +def D12 : EpiphanyRegWithSubs<24, "d12", [R24, R25]>, DwarfRegNum<[24,25]>; +def D13 : EpiphanyRegWithSubs<26, "d13", [R26, R27]>, DwarfRegNum<[26,27]>; +def D14 : EpiphanyRegWithSubs<28, "d14", [R28, R29]>, DwarfRegNum<[28,29]>; +def D15 : EpiphanyRegWithSubs<30, "d15", [R30, R31]>, DwarfRegNum<[30,31]>; +def D16 : EpiphanyRegWithSubs<32, "d16", [R32, R33]>, DwarfRegNum<[32,33]>; +def D17 : EpiphanyRegWithSubs<34, "d17", [R34, R35]>, DwarfRegNum<[34,35]>; +def D18 : EpiphanyRegWithSubs<36, "d18", [R36, R37]>, DwarfRegNum<[36,37]>; +def D19 : EpiphanyRegWithSubs<38, "d19", [R38, R39]>, DwarfRegNum<[38,39]>; +def D20 : EpiphanyRegWithSubs<40, "d20", [R40, R41]>, DwarfRegNum<[40,41]>; +def D21 : EpiphanyRegWithSubs<42, "d21", [R42, R43]>, DwarfRegNum<[42,43]>; +def D22 : EpiphanyRegWithSubs<44, "d22", [R44, R45]>, DwarfRegNum<[44,45]>; +def D23 : EpiphanyRegWithSubs<46, "d23", [R46, R47]>, DwarfRegNum<[46,47]>; +def D24 : EpiphanyRegWithSubs<48, "d24", [R48, R49]>, DwarfRegNum<[48,49]>; +def D25 : EpiphanyRegWithSubs<50, "d25", [R50, R51]>, DwarfRegNum<[50,51]>; +def D26 : EpiphanyRegWithSubs<52, "d26", [R52, R53]>, DwarfRegNum<[52,53]>; +def D27 : EpiphanyRegWithSubs<54, "d27", [R54, R55]>, DwarfRegNum<[54,55]>; +def D28 : EpiphanyRegWithSubs<56, "d28", [R56, R57]>, DwarfRegNum<[56,57]>; +def D29 : EpiphanyRegWithSubs<58, "d29", [R58, R59]>, DwarfRegNum<[58,59]>; +def D30 : EpiphanyRegWithSubs<60, "d30", [R60, R61]>, DwarfRegNum<[60,61]>; +// def D31 : EpiphanyRegWithSubs<62, "d31", [R62, R63]>, DwarfRegNum<[62,63]>; +} + +// Note that the order of registers is important for the Disassembler here: +// tablegen uses it to form MCRegisterClass::getRegister, which we assume can +// take an encoding value. +def GPR32 : RegisterClass<"Epiphany", [i32], 32, (add (sequence "R%u", 0, 12), LR, SP, (sequence "R%u", 15, 63))> { +} + +def FPR32 : RegisterClass<"Epiphany", [f32], 32, (add GPR32)> { +} + +def DPR64 : RegisterClass<"Epiphany", [i64,f64], 64, (sequence "D%u", 0, 30)> { +} + +// Flags register +def NZCV : Register<"nzcv"> { + let Namespace = "Epiphany"; +} + +def FlagClass : RegisterClass<"Epiphany", [i32], 32, (add NZCV)> { + let CopyCost = -1; + let isAllocatable = 0; +} diff --git a/EpiphanySchedule.td b/EpiphanySchedule.td new file mode 100644 index 0000000..0988acb --- /dev/null +++ b/EpiphanySchedule.td @@ -0,0 +1,10 @@ +//===- EpiphanySchedule.td - Epiphany Scheduling Definitions -*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +def GenericItineraries : ProcessorItineraries<[], [], []>; diff --git a/EpiphanySelectionDAGInfo.cpp b/EpiphanySelectionDAGInfo.cpp new file mode 100644 index 0000000..52493ac --- /dev/null +++ b/EpiphanySelectionDAGInfo.cpp @@ -0,0 +1,25 @@ +//===-- EpiphanySelectionDAGInfo.cpp - Epiphany SelectionDAG Info -----------===// +// +// 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 EpiphanySelectionDAGInfo class. +// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "arm-selectiondag-info" +#include "EpiphanyTargetMachine.h" +#include "llvm/CodeGen/SelectionDAG.h" +using namespace llvm; + +EpiphanySelectionDAGInfo::EpiphanySelectionDAGInfo(const EpiphanyTargetMachine &TM) + : TargetSelectionDAGInfo(TM), + Subtarget(&TM.getSubtarget()) { +} + +EpiphanySelectionDAGInfo::~EpiphanySelectionDAGInfo() { +} diff --git a/EpiphanySelectionDAGInfo.h b/EpiphanySelectionDAGInfo.h new file mode 100644 index 0000000..4f4a6a6 --- /dev/null +++ b/EpiphanySelectionDAGInfo.h @@ -0,0 +1,32 @@ +//===-- EpiphanySelectionDAGInfo.h - Epiphany SelectionDAG 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 defines the Epiphany subclass for TargetSelectionDAGInfo. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EPIPHANYSELECTIONDAGINFO_H +#define LLVM_EPIPHANYSELECTIONDAGINFO_H + +#include "llvm/Target/TargetSelectionDAGInfo.h" + +namespace llvm { + +class EpiphanyTargetMachine; + +class EpiphanySelectionDAGInfo : public TargetSelectionDAGInfo { + const EpiphanySubtarget *Subtarget; +public: + explicit EpiphanySelectionDAGInfo(const EpiphanyTargetMachine &TM); + ~EpiphanySelectionDAGInfo(); +}; + +} + +#endif diff --git a/EpiphanySubtarget.cpp b/EpiphanySubtarget.cpp new file mode 100644 index 0000000..9315fb0 --- /dev/null +++ b/EpiphanySubtarget.cpp @@ -0,0 +1,41 @@ +//===-- EpiphanySubtarget.cpp - Epiphany 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 Epiphany specific subclass of TargetSubtargetInfo. +// +//===----------------------------------------------------------------------===// + +#include "EpiphanySubtarget.h" +#include "EpiphanyRegisterInfo.h" +#include "MCTargetDesc/EpiphanyMCTargetDesc.h" +#include "llvm/IR/GlobalValue.h" +#include "llvm/Target/TargetSubtargetInfo.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/ADT/SmallVector.h" + +#define GET_SUBTARGETINFO_TARGET_DESC +#define GET_SUBTARGETINFO_CTOR +#include "EpiphanyGenSubtargetInfo.inc" + +using namespace llvm; + +EpiphanySubtarget::EpiphanySubtarget(StringRef TT, StringRef CPU, StringRef FS) + : EpiphanyGenSubtargetInfo(TT, CPU, FS) + , TargetTriple(TT) { + + ParseSubtargetFeatures(CPU, FS); +} + +bool EpiphanySubtarget::GVIsIndirectSymbol(const GlobalValue *GV, + Reloc::Model RelocM) const { + if (RelocM == Reloc::Static) + return false; + + return !GV->hasLocalLinkage() && !GV->hasHiddenVisibility(); +} diff --git a/EpiphanySubtarget.h b/EpiphanySubtarget.h new file mode 100644 index 0000000..85d9139 --- /dev/null +++ b/EpiphanySubtarget.h @@ -0,0 +1,51 @@ +//==-- EpiphanySubtarget.h - Define Subtarget for the Epiphany ---*- 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 Epiphany specific subclass of TargetSubtargetInfo. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TARGET_EPIPHANY_SUBTARGET_H +#define LLVM_TARGET_EPIPHANY_SUBTARGET_H + +#include "llvm/ADT/Triple.h" +#include "llvm/Target/TargetSubtargetInfo.h" + +#define GET_SUBTARGETINFO_HEADER +#include "EpiphanyGenSubtargetInfo.inc" + +#include + +namespace llvm { +class StringRef; +class GlobalValue; + +class EpiphanySubtarget : public EpiphanyGenSubtargetInfo { +protected: + /// TargetTriple - What processor and OS we're targeting. + Triple TargetTriple; +public: + /// This constructor initializes the data members to match that + /// of the specified triple. + /// + EpiphanySubtarget(StringRef TT, StringRef CPU, StringRef FS); + + /// ParseSubtargetFeatures - Parses features string setting specified + /// subtarget options. Definition of function is auto generated by tblgen. + void ParseSubtargetFeatures(StringRef CPU, StringRef FS); + + bool GVIsIndirectSymbol(const GlobalValue *GV, Reloc::Model RelocM) const; + + bool isTargetELF() const { return TargetTriple.isOSBinFormatELF(); } + bool isTargetLinux() const { return TargetTriple.getOS() == Triple::Linux; } + +}; +} // End llvm namespace + +#endif // LLVM_TARGET_EPIPHANY_SUBTARGET_H diff --git a/EpiphanyTargetMachine.cpp b/EpiphanyTargetMachine.cpp new file mode 100644 index 0000000..bbe7e80 --- /dev/null +++ b/EpiphanyTargetMachine.cpp @@ -0,0 +1,95 @@ +//===-- EpiphanyTargetMachine.cpp - Define TargetMachine for Epiphany -------===// +// +// 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 implementation of the EpiphanyTargetMachine +// methods. Principally just setting up the passes needed to generate correct +// code on this architecture. +// +//===----------------------------------------------------------------------===// + +#include "Epiphany.h" +#include "EpiphanyTargetMachine.h" +#include "MCTargetDesc/EpiphanyMCTargetDesc.h" +#include "llvm/PassManager.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/CommandLine.h" + +using namespace llvm; + +static cl::opt +EnableLSD("double-ls", cl::Hidden, + cl::desc("Enable double loads and stores"), + cl::init(false)); + + +extern "C" void LLVMInitializeEpiphanyTarget() { + RegisterTargetMachine X(TheEpiphanyTarget); +} + +EpiphanyTargetMachine::EpiphanyTargetMachine(const Target &T, StringRef TT, + StringRef CPU, StringRef FS, + const TargetOptions &Options, + Reloc::Model RM, CodeModel::Model CM, + CodeGenOpt::Level OL) + : LLVMTargetMachine(T, TT, CPU, FS, Options, RM, CM, OL), + Subtarget(TT, CPU, FS), + InstrInfo(Subtarget), + DL("e-p:32:32-i8:8:8-i16:16:16-i32:32:32-f32:32:32-i64:64:64-f64:64:64-s64:64:64-S64:64:64-a0:32:32"), + TLInfo(*this), + TSInfo(*this), + FrameLowering(Subtarget) { +} + +namespace { +/// Epiphany Code Generator Pass Configuration Options. +class EpiphanyPassConfig : public TargetPassConfig { +public: + EpiphanyPassConfig(EpiphanyTargetMachine *TM, PassManagerBase &PM) + : TargetPassConfig(TM, PM) {} + + EpiphanyTargetMachine &getEpiphanyTargetMachine() const { + return getTM(); + } + + const EpiphanySubtarget &getEpiphanySubtarget() const { + return *getEpiphanyTargetMachine().getSubtargetImpl(); + } + + virtual bool addInstSelector(); + virtual bool addPreEmitPass(); + virtual bool addPreRegAlloc(); + virtual bool addPostRegAlloc(); +}; +} // namespace + +TargetPassConfig *EpiphanyTargetMachine::createPassConfig(PassManagerBase &PM) { + return new EpiphanyPassConfig(this, PM); +} + +bool EpiphanyPassConfig::addPreEmitPass() { + addPass(&UnpackMachineBundlesID); + return true; +} + +bool EpiphanyPassConfig::addInstSelector() { + addPass(createEpiphanyISelDAG(getEpiphanyTargetMachine(), getOptLevel())); + return false; +} + +bool EpiphanyPassConfig::addPreRegAlloc() { + if (EnableLSD) + addPass(createEpiphanyLSOptPass()); + return true; +} + +bool EpiphanyPassConfig::addPostRegAlloc() { + addPass(createEpiphanyCondMovPass(getEpiphanyTargetMachine())); + return true; +} \ No newline at end of file diff --git a/EpiphanyTargetMachine.h b/EpiphanyTargetMachine.h new file mode 100644 index 0000000..c9e2741 --- /dev/null +++ b/EpiphanyTargetMachine.h @@ -0,0 +1,69 @@ +//=== EpiphanyTargetMachine.h - Define TargetMachine for Epiphany -*- 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 Epiphany specific subclass of TargetMachine. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EPIPHANYTARGETMACHINE_H +#define LLVM_EPIPHANYTARGETMACHINE_H + +#include "EpiphanyFrameLowering.h" +#include "EpiphanyISelLowering.h" +#include "EpiphanyInstrInfo.h" +#include "EpiphanySelectionDAGInfo.h" +#include "EpiphanySubtarget.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/Target/TargetMachine.h" + +namespace llvm { + +class EpiphanyTargetMachine : public LLVMTargetMachine { + EpiphanySubtarget Subtarget; + EpiphanyInstrInfo InstrInfo; + const DataLayout DL; + EpiphanyTargetLowering TLInfo; + EpiphanySelectionDAGInfo TSInfo; + EpiphanyFrameLowering FrameLowering; + +public: + EpiphanyTargetMachine(const Target &T, StringRef TT, StringRef CPU, + StringRef FS, const TargetOptions &Options, + Reloc::Model RM, CodeModel::Model CM, + CodeGenOpt::Level OL); + + const EpiphanyInstrInfo *getInstrInfo() const { + return &InstrInfo; + } + + const EpiphanyFrameLowering *getFrameLowering() const { + return &FrameLowering; + } + + const EpiphanyTargetLowering *getTargetLowering() const { + return &TLInfo; + } + + const EpiphanySelectionDAGInfo *getSelectionDAGInfo() const { + return &TSInfo; + } + + const EpiphanySubtarget *getSubtargetImpl() const { return &Subtarget; } + + const DataLayout *getDataLayout() const { return &DL; } + + const TargetRegisterInfo *getRegisterInfo() const { + return &InstrInfo.getRegisterInfo(); + } + TargetPassConfig *createPassConfig(PassManagerBase &PM); +}; + +} + +#endif diff --git a/EpiphanyTargetObjectFile.cpp b/EpiphanyTargetObjectFile.cpp new file mode 100644 index 0000000..1edba97 --- /dev/null +++ b/EpiphanyTargetObjectFile.cpp @@ -0,0 +1,24 @@ +//===-- EpiphanyTargetObjectFile.cpp - Epiphany Object Info -----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file deals with any Epiphany specific requirements on object files. +// +//===----------------------------------------------------------------------===// + + +#include "EpiphanyTargetObjectFile.h" + +using namespace llvm; + +void +EpiphanyLinuxTargetObjectFile::Initialize(MCContext &Ctx, + const TargetMachine &TM) { + TargetLoweringObjectFileELF::Initialize(Ctx, TM); + InitializeELF(TM.Options.UseInitArray); +} diff --git a/EpiphanyTargetObjectFile.h b/EpiphanyTargetObjectFile.h new file mode 100644 index 0000000..c632a41 --- /dev/null +++ b/EpiphanyTargetObjectFile.h @@ -0,0 +1,31 @@ +//===-- EpiphanyTargetObjectFile.h - Epiphany Object 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 deals with any Epiphany specific requirements on object files. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TARGET_EPIPHANY_TARGETOBJECTFILE_H +#define LLVM_TARGET_EPIPHANY_TARGETOBJECTFILE_H + +#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Target/TargetLoweringObjectFile.h" + +namespace llvm { + + /// EpiphanyLinuxTargetObjectFile - This implementation is used for linux + /// Epiphany. + class EpiphanyLinuxTargetObjectFile : public TargetLoweringObjectFileELF { + virtual void Initialize(MCContext &Ctx, const TargetMachine &TM); + }; + +} // end namespace llvm + +#endif diff --git a/InstPrinter/CMakeLists.txt b/InstPrinter/CMakeLists.txt new file mode 100644 index 0000000..0194547 --- /dev/null +++ b/InstPrinter/CMakeLists.txt @@ -0,0 +1,8 @@ +include_directories( ${CMAKE_CURRENT_BINARY_DIR}/.. ${CMAKE_CURRENT_SOURCE_DIR}/.. ) + +add_llvm_library(LLVMEpiphanyAsmPrinter + EpiphanyInstPrinter.cpp + ) + +add_dependencies(LLVMEpiphanyAsmPrinter EpiphanyCommonTableGen) + diff --git a/InstPrinter/EpiphanyInstPrinter.cpp b/InstPrinter/EpiphanyInstPrinter.cpp new file mode 100644 index 0000000..d8a6f75 --- /dev/null +++ b/InstPrinter/EpiphanyInstPrinter.cpp @@ -0,0 +1,118 @@ +//==-- EpiphanyInstPrinter.cpp - Convert Epiphany 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 Epiphany MCInst to a .s file. +// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "asm-printer" +#include "EpiphanyInstPrinter.h" +#include "MCTargetDesc/EpiphanyMCTargetDesc.h" +#include "Utils/EpiphanyBaseInfo.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +#define GET_INSTRUCTION_NAME +#define PRINT_ALIAS_INSTR +#include "EpiphanyGenAsmWriter.inc" + +EpiphanyInstPrinter::EpiphanyInstPrinter(const MCAsmInfo &MAI, + const MCInstrInfo &MII, + const MCRegisterInfo &MRI, + const MCSubtargetInfo &STI) : + MCInstPrinter(MAI, MII, MRI) { + // Initialize the set of available features. + setAvailableFeatures(STI.getFeatureBits()); +} + +void EpiphanyInstPrinter::printAddSubImmOperand(const MCInst *MI, unsigned OpNum, raw_ostream &O) { + const MCOperand &Imm11Op = MI->getOperand(OpNum); + int64_t Imm11 = Imm11Op.getImm(); + assert((Imm11 <= 1023 && Imm11 >= -1024) && "Invalid immediate for add/sub imm"); + + O << "#" << Imm11; +} + +void EpiphanyInstPrinter::printBareImmOperand(const MCInst *MI, unsigned OpNum, raw_ostream &O) { + const MCOperand &MO = MI->getOperand(OpNum); + O << MO.getImm(); +} + + +void EpiphanyInstPrinter::printCondCodeOperand(const MCInst *MI, unsigned OpNum, raw_ostream &O) { + const MCOperand &MO = MI->getOperand(OpNum); + + O << A64CondCodeToString(static_cast(MO.getImm())); +} + +template void +EpiphanyInstPrinter::printLabelOperand(const MCInst *MI, unsigned OpNum, raw_ostream &O) { + const MCOperand &MO = MI->getOperand(OpNum); + + if (!MO.isImm()) { + printOperand(MI, OpNum, O); + return; + } + +//we do currently not support branches to immediates that are not labels +assert(MO.isImm() && "unknown operand kind in printLabelOperand (imm?)"); +} + +void EpiphanyInstPrinter::printOffsetUImm11Operand(const MCInst *MI, unsigned OpNum, raw_ostream &O, int MemSize) { + const MCOperand &MOImm = MI->getOperand(OpNum); + int32_t Imm = MOImm.getImm();// * MemSize; + + O << "#" << Imm; +} + +void EpiphanyInstPrinter::printOperand(const MCInst *MI, unsigned OpNo, + raw_ostream &O) { + const MCOperand &Op = MI->getOperand(OpNo); + if (Op.isReg()) { + unsigned Reg = Op.getReg(); + O << getRegisterName(Reg); + } else if (Op.isImm()) { + O << '#' << Op.getImm(); + } else { + assert(Op.isExpr() && "unknown operand kind in printOperand"); + // If a symbolic branch target was added as a constant expression then print + // that address in hex. + const MCConstantExpr *BranchTarget = dyn_cast(Op.getExpr()); + int64_t Address; + if (BranchTarget && BranchTarget->EvaluateAsAbsolute(Address)) { + O << "0x"; + O.write_hex(Address); + } + else { + // Otherwise, just print the expression. + O << *Op.getExpr(); + } + } +} + +void EpiphanyInstPrinter::printFPImmOperand(const MCInst *MI, unsigned OpNum, + raw_ostream &o) { + const MCOperand &MOImm = MI->getOperand(OpNum); + assert(MOImm.isFPImm() && "not float operand in printFPImmOperand"); + o << '#' << format("%.8f", MOImm.getFPImm()); +} + +void EpiphanyInstPrinter::printInst(const MCInst *MI, raw_ostream &O, + StringRef Annot) { +if (!printAliasInstr(MI, O)) + printInstruction(MI, O); + + printAnnotation(O, Annot); +} diff --git a/InstPrinter/EpiphanyInstPrinter.h b/InstPrinter/EpiphanyInstPrinter.h new file mode 100644 index 0000000..c69b4e5 --- /dev/null +++ b/InstPrinter/EpiphanyInstPrinter.h @@ -0,0 +1,72 @@ +//===-- EpiphanyInstPrinter.h - Convert Epiphany 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 Epiphany MCInst to a .s file. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EPIPHANYINSTPRINTER_H +#define LLVM_EPIPHANYINSTPRINTER_H + +#include "MCTargetDesc/EpiphanyMCTargetDesc.h" +#include "Utils/EpiphanyBaseInfo.h" +#include "llvm/MC/MCInstPrinter.h" +#include "llvm/MC/MCSubtargetInfo.h" + +namespace llvm { + + class MCOperand; + + class EpiphanyInstPrinter : public MCInstPrinter { + public: + EpiphanyInstPrinter(const MCAsmInfo &MAI, const MCInstrInfo &MII, + const MCRegisterInfo &MRI, const MCSubtargetInfo &STI); + + // Autogenerated by tblgen + void printInstruction(const MCInst *MI, raw_ostream &O); + bool printAliasInstr(const MCInst *MI, raw_ostream &O); + static const char *getRegisterName(unsigned RegNo); + static const char *getInstructionName(unsigned Opcode); + + void printAddSubImmOperand(const MCInst *MI, unsigned OpNum, raw_ostream &O); + + void printBareImmOperand(const MCInst *MI, unsigned OpNum, raw_ostream &O); + + void printCondCodeOperand(const MCInst *MI, unsigned OpNum, raw_ostream &O); + + template + void printOffsetUImm11Operand(const MCInst *MI, unsigned OpNum, raw_ostream &o) { printOffsetUImm11Operand(MI, OpNum, o, MemScale); } + void printOffsetUImm11Operand(const MCInst *MI, unsigned OpNum, raw_ostream &o, int MemScale); + + template + void printLabelOperand(const MCInst *MI, unsigned OpNum, raw_ostream &O); + + template + void printLogicalImmOperand(const MCInst *MI, unsigned OpNum, raw_ostream &O); + + template void + printSImm7ScaledOperand(const MCInst *MI, unsigned OpNum, raw_ostream &O); + + void printOffsetSImm9Operand(const MCInst *MI, unsigned OpNum, raw_ostream &O); + + void printFPImmOperand(const MCInst *MI, unsigned OpNum, raw_ostream &o); + + void printOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O); + virtual void printInst(const MCInst *MI, raw_ostream &O, StringRef Annot); + + bool isStackReg(unsigned RegNo) { + return RegNo == Epiphany::SP; + } + + + }; + +} + +#endif diff --git a/InstPrinter/LLVMBuild.txt b/InstPrinter/LLVMBuild.txt new file mode 100644 index 0000000..ede51e8 --- /dev/null +++ b/InstPrinter/LLVMBuild.txt @@ -0,0 +1,24 @@ +;===- ./lib/Target/Epiphany/InstPrinter/LLVMBuild.txt -----------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Library +name = EpiphanyAsmPrinter +parent = Epiphany +required_libraries = EpiphanyUtils MC Support +add_to_library_groups = Epiphany + diff --git a/InstPrinter/Makefile b/InstPrinter/Makefile new file mode 100644 index 0000000..c740e18 --- /dev/null +++ b/InstPrinter/Makefile @@ -0,0 +1,15 @@ +##===- lib/Target/Epiphany/AsmPrinter/Makefile --------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +LEVEL = ../../../.. +LIBRARYNAME = LLVMEpiphanyAsmPrinter + +# Hack: we need to include 'main' target directory to grab private headers +CPP.Flags += -I$(PROJ_OBJ_DIR)/.. -I$(PROJ_SRC_DIR)/.. + +include $(LEVEL)/Makefile.common diff --git a/LLVMBuild.txt b/LLVMBuild.txt new file mode 100644 index 0000000..385b4c7 --- /dev/null +++ b/LLVMBuild.txt @@ -0,0 +1,36 @@ +;===- ./lib/Target/Epiphany/LLVMBuild.txt -----------------------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[common] +subdirectories = InstPrinter MCTargetDesc TargetInfo Utils + +[component_0] +type = TargetGroup +name = Epiphany +parent = Target +has_asmparser = 0 +has_asmprinter = 1 +has_disassembler = 0 +;has_jit = 1 + +[component_1] +type = Library +name = EpiphanyCodeGen +parent = Epiphany +required_libraries = EpiphanyAsmPrinter EpiphanyDesc EpiphanyInfo AsmPrinter CodeGen Core MC SelectionDAG Support Target +add_to_library_groups = Epiphany + diff --git a/MCTargetDesc/CMakeLists.txt b/MCTargetDesc/CMakeLists.txt new file mode 100644 index 0000000..0a591e4 --- /dev/null +++ b/MCTargetDesc/CMakeLists.txt @@ -0,0 +1,9 @@ +add_llvm_library(LLVMEpiphanyDesc + EpiphanyMCAsmInfo.cpp + EpiphanyMCExpr.cpp + EpiphanyMCTargetDesc.cpp + ) +add_dependencies(LLVMEpiphanyDesc EpiphanyCommonTableGen) + +# Hack: we need to include 'main' target directory to grab private headers +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/.. ${CMAKE_CURRENT_BINARY_DIR}/..) diff --git a/MCTargetDesc/EpiphanyMCAsmInfo.cpp b/MCTargetDesc/EpiphanyMCAsmInfo.cpp new file mode 100644 index 0000000..09fa663 --- /dev/null +++ b/MCTargetDesc/EpiphanyMCAsmInfo.cpp @@ -0,0 +1,41 @@ +//===-- EpiphanyMCAsmInfo.cpp - Epiphany 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 EpiphanyMCAsmInfo properties. +// +//===----------------------------------------------------------------------===// + +#include "EpiphanyMCAsmInfo.h" + +using namespace llvm; + +EpiphanyELFMCAsmInfo::EpiphanyELFMCAsmInfo() { + PointerSize = 8; + + // ".comm align is in bytes but .align is pow-2." + AlignmentIsInBytes = false; + + CommentString = "//"; + PrivateGlobalPrefix = ".L"; + Code32Directive = ".code\t32"; + + Data16bitsDirective = "\t.hword\t"; + Data32bitsDirective = "\t.word\t"; + Data64bitsDirective = "\t.xword\t"; + + UseDataRegionDirectives = true; + + WeakRefDirective = "\t.weak\t"; + + HasLEB128 = true; + SupportsDebugInformation = true; + + // Exceptions handling + ExceptionsType = ExceptionHandling::DwarfCFI; +} diff --git a/MCTargetDesc/EpiphanyMCAsmInfo.h b/MCTargetDesc/EpiphanyMCAsmInfo.h new file mode 100644 index 0000000..579886d --- /dev/null +++ b/MCTargetDesc/EpiphanyMCAsmInfo.h @@ -0,0 +1,27 @@ +//==-- EpiphanyMCAsmInfo.h - Epiphany asm properties -------------*- 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 EpiphanyMCAsmInfo class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EPIPHANYTARGETASMINFO_H +#define LLVM_EPIPHANYTARGETASMINFO_H + +#include "llvm/MC/MCAsmInfo.h" + +namespace llvm { + + struct EpiphanyELFMCAsmInfo : public MCAsmInfo { + explicit EpiphanyELFMCAsmInfo(); + }; + +} // namespace llvm + +#endif diff --git a/MCTargetDesc/EpiphanyMCExpr.cpp b/MCTargetDesc/EpiphanyMCExpr.cpp new file mode 100644 index 0000000..eb2da8f --- /dev/null +++ b/MCTargetDesc/EpiphanyMCExpr.cpp @@ -0,0 +1,95 @@ +//===-- EpiphanyMCExpr.cpp - Epiphany specific MC expression classes --------===// +// +// 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 implementation of the assembly expression modifiers +// accepted by the Epiphany architecture (e.g. ":lo12:", ":gottprel_g1:", ...). +// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "epiphanymcexpr" +#include "EpiphanyMCExpr.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCAssembler.h" +#include "llvm/MC/MCELF.h" +#include "llvm/Object/ELF.h" + +using namespace llvm; + +const EpiphanyMCExpr* +EpiphanyMCExpr::Create(VariantKind Kind, const MCExpr *Expr, + MCContext &Ctx) { + return new (Ctx) EpiphanyMCExpr(Kind, Expr); +} + +void EpiphanyMCExpr::PrintImpl(raw_ostream &OS) const { + switch (Kind) { + default: llvm_unreachable("Invalid kind!"); + case VK_EPIPHANY_LO16: OS << "%low("; break; + case VK_EPIPHANY_HI16: OS << "%high("; break; + } + + const MCExpr *Expr = getSubExpr(); + if (Expr->getKind() != MCExpr::SymbolRef) + OS << '('; + Expr->print(OS); + if (Expr->getKind() != MCExpr::SymbolRef) + OS << ')'; + + switch (Kind) { + case VK_EPIPHANY_LO16: + case VK_EPIPHANY_HI16: OS << ")"; + default: break; + } +} + +bool +EpiphanyMCExpr::EvaluateAsRelocatableImpl(MCValue &Res, + const MCAsmLayout *Layout) const { + return getSubExpr()->EvaluateAsRelocatable(Res, *Layout); +} + +static void fixELFSymbolsInTLSFixupsImpl(const MCExpr *Expr, MCAssembler &Asm) { + llvm_unreachable("Can't handle nested target expression"); +} + +void EpiphanyMCExpr::fixELFSymbolsInTLSFixups(MCAssembler &Asm) const { +} + +// FIXME: This basically copies MCObjectStreamer::AddValueSymbols. Perhaps +// that method should be made public? +// FIXME: really do above: now that two backends are using it. +static void AddValueSymbolsImpl(const MCExpr *Value, MCAssembler *Asm) { + switch (Value->getKind()) { + case MCExpr::Target: + llvm_unreachable("Can't handle nested target expr!"); + break; + + case MCExpr::Constant: + break; + + case MCExpr::Binary: { + const MCBinaryExpr *BE = cast(Value); + AddValueSymbolsImpl(BE->getLHS(), Asm); + AddValueSymbolsImpl(BE->getRHS(), Asm); + break; + } + + case MCExpr::SymbolRef: + Asm->getOrCreateSymbolData(cast(Value)->getSymbol()); + break; + + case MCExpr::Unary: + AddValueSymbolsImpl(cast(Value)->getSubExpr(), Asm); + break; + } +} + +void EpiphanyMCExpr::AddValueSymbols(MCAssembler *Asm) const { + AddValueSymbolsImpl(getSubExpr(), Asm); +} diff --git a/MCTargetDesc/EpiphanyMCExpr.h b/MCTargetDesc/EpiphanyMCExpr.h new file mode 100644 index 0000000..d68f520 --- /dev/null +++ b/MCTargetDesc/EpiphanyMCExpr.h @@ -0,0 +1,82 @@ +//==- EpiphanyMCExpr.h - Epiphany specific MC expression classes --*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file describes Epiphany-specific MCExprs, used for modifiers like +// ":lo12:" or ":gottprel_g1:". +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EPIPHANYMCEXPR_H +#define LLVM_EPIPHANYMCEXPR_H + +#include "llvm/MC/MCExpr.h" + +namespace llvm { + +class EpiphanyMCExpr : public MCTargetExpr { +public: + enum VariantKind { + VK_EPIPHANY_None, + VK_EPIPHANY_LO16, // :lo12: + VK_EPIPHANY_HI16, // :lo12: + }; + +private: + const VariantKind Kind; + const MCExpr *Expr; + + explicit EpiphanyMCExpr(VariantKind _Kind, const MCExpr *_Expr) + : Kind(_Kind), Expr(_Expr) {} + +public: + /// @name Construction + /// @{ + + static const EpiphanyMCExpr *Create(VariantKind Kind, const MCExpr *Expr, + MCContext &Ctx); + + static const EpiphanyMCExpr *CreateLo16(const MCExpr *Expr, MCContext &Ctx) { + return Create(VK_EPIPHANY_LO16, Expr, Ctx); + } + static const EpiphanyMCExpr *CreateHi16(const MCExpr *Expr, MCContext &Ctx) { + return Create(VK_EPIPHANY_HI16, Expr, Ctx); + } + + /// @} + /// @name Accessors + /// @{ + + /// getOpcode - Get the kind of this expression. + VariantKind getKind() const { return Kind; } + + /// getSubExpr - Get the child of this expression. + const MCExpr *getSubExpr() const { return Expr; } + + /// @} + + void PrintImpl(raw_ostream &OS) const; + bool EvaluateAsRelocatableImpl(MCValue &Res, + const MCAsmLayout *Layout) const; + void AddValueSymbols(MCAssembler *) const; + const MCSection *FindAssociatedSection() const { + return getSubExpr()->FindAssociatedSection(); + } + + void fixELFSymbolsInTLSFixups(MCAssembler &Asm) const; + + static bool classof(const MCExpr *E) { + return E->getKind() == MCExpr::Target; + } + + static bool classof(const EpiphanyMCExpr *) { return true; } + +}; +} // end namespace llvm + +#endif diff --git a/MCTargetDesc/EpiphanyMCTargetDesc.cpp b/MCTargetDesc/EpiphanyMCTargetDesc.cpp new file mode 100644 index 0000000..6b8bc0b --- /dev/null +++ b/MCTargetDesc/EpiphanyMCTargetDesc.cpp @@ -0,0 +1,175 @@ +//===-- EpiphanyMCTargetDesc.cpp - Epiphany Target Descriptions -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file provides Epiphany specific target descriptions. +// +//===----------------------------------------------------------------------===// + +#include "EpiphanyMCTargetDesc.h" +//#include "EpiphanyELFStreamer.h" +#include "EpiphanyMCAsmInfo.h" +#include "InstPrinter/EpiphanyInstPrinter.h" +#include "llvm/ADT/APInt.h" +#include "llvm/MC/MCCodeGenInfo.h" +#include "llvm/MC/MCInstrAnalysis.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/ErrorHandling.h" + +#define GET_REGINFO_MC_DESC +#include "EpiphanyGenRegisterInfo.inc" + +#define GET_INSTRINFO_MC_DESC +#include "EpiphanyGenInstrInfo.inc" + +#define GET_SUBTARGETINFO_MC_DESC +#include "EpiphanyGenSubtargetInfo.inc" + +using namespace llvm; + +MCSubtargetInfo *Epiphany_MC::createEpiphanyMCSubtargetInfo(StringRef TT, + StringRef CPU, + StringRef FS) { + MCSubtargetInfo *X = new MCSubtargetInfo(); + InitEpiphanyMCSubtargetInfo(X, TT, CPU, ""); + return X; +} + + +static MCInstrInfo *createEpiphanyMCInstrInfo() { + MCInstrInfo *X = new MCInstrInfo(); + InitEpiphanyMCInstrInfo(X); + return X; +} + +static MCRegisterInfo *createEpiphanyMCRegisterInfo(StringRef Triple) { + MCRegisterInfo *X = new MCRegisterInfo(); + InitEpiphanyMCRegisterInfo(X, Epiphany::LR); + return X; +} + +static MCAsmInfo *createEpiphanyMCAsmInfo(const Target &T, StringRef TT) { + Triple TheTriple(TT); + + MCAsmInfo *MAI = new EpiphanyELFMCAsmInfo(); + MachineLocation Dst(MachineLocation::VirtualFP); + MachineLocation Src(Epiphany::SP, 0); + MAI->addInitialFrameState(0, Dst, Src); + + return MAI; +} + +static MCCodeGenInfo *createEpiphanyMCCodeGenInfo(StringRef TT, Reloc::Model RM, + CodeModel::Model CM, + CodeGenOpt::Level OL) { + MCCodeGenInfo *X = new MCCodeGenInfo(); + RM = Reloc::Static; + CM = CodeModel::Small; + X->InitMCCodeGenInfo(RM, CM, OL); + return X; +} + + +static MCInstPrinter *createEpiphanyMCInstPrinter(const Target &T, + unsigned SyntaxVariant, + const MCAsmInfo &MAI, + const MCInstrInfo &MII, + const MCRegisterInfo &MRI, + const MCSubtargetInfo &STI) { + if (SyntaxVariant == 0) + return new EpiphanyInstPrinter(MAI, MII, MRI, STI); + return 0; +} + +namespace { + +class EpiphanyMCInstrAnalysis : public MCInstrAnalysis { +public: + EpiphanyMCInstrAnalysis(const MCInstrInfo *Info) : MCInstrAnalysis(Info) {} + + virtual bool isUnconditionalBranch(const MCInst &Inst) const { + if (Inst.getOpcode() == Epiphany::Bcc + && Inst.getOperand(0).getImm() == EpiphanyCC::AL) + return true; + return MCInstrAnalysis::isUnconditionalBranch(Inst); + } + + virtual bool isConditionalBranch(const MCInst &Inst) const { + if (Inst.getOpcode() == Epiphany::Bcc + && Inst.getOperand(0).getImm() == EpiphanyCC::AL) + return false; + return MCInstrAnalysis::isConditionalBranch(Inst); + } + + uint64_t evaluateBranch(const MCInst &Inst, uint64_t Addr, + uint64_t Size) const { + unsigned LblOperand = Inst.getOpcode() == Epiphany::Bcc ? 1 : 0; + // FIXME: We only handle PCRel branches for now. + if (Info->get(Inst.getOpcode()).OpInfo[LblOperand].OperandType + != MCOI::OPERAND_PCREL) + return -1ULL; + + int64_t Imm = Inst.getOperand(LblOperand).getImm(); + + return Addr + Imm; + } +}; + +} + +static MCInstrAnalysis *createEpiphanyMCInstrAnalysis(const MCInstrInfo *Info) { + return new EpiphanyMCInstrAnalysis(Info); +} + + + +extern "C" void LLVMInitializeEpiphanyTargetMC() { + // Register the MC asm info. + RegisterMCAsmInfoFn A(TheEpiphanyTarget, createEpiphanyMCAsmInfo); + + // Register the MC codegen info. + TargetRegistry::RegisterMCCodeGenInfo(TheEpiphanyTarget, + createEpiphanyMCCodeGenInfo); + + // Register the MC instruction info. + TargetRegistry::RegisterMCInstrInfo(TheEpiphanyTarget, + createEpiphanyMCInstrInfo); + + // Register the MC register info. + TargetRegistry::RegisterMCRegInfo(TheEpiphanyTarget, + createEpiphanyMCRegisterInfo); + + // Register the MC subtarget info. + using Epiphany_MC::createEpiphanyMCSubtargetInfo; + TargetRegistry::RegisterMCSubtargetInfo(TheEpiphanyTarget, + createEpiphanyMCSubtargetInfo); + + // Register the MC instruction analyzer. + TargetRegistry::RegisterMCInstrAnalysis(TheEpiphanyTarget, + createEpiphanyMCInstrAnalysis); + + //// Register the MC Code Emitter + //TargetRegistry::RegisterMCCodeEmitter(TheEpiphanyTarget, + // createEpiphanyMCCodeEmitter); + + //// Register the asm backend. + //TargetRegistry::RegisterMCAsmBackend(TheEpiphanyTarget, + // createEpiphanyAsmBackend); + + //// Register the object streamer. + //TargetRegistry::RegisterMCObjectStreamer(TheEpiphanyTarget, + // createMCStreamer); + + // Register the MCInstPrinter. + TargetRegistry::RegisterMCInstPrinter(TheEpiphanyTarget, + createEpiphanyMCInstPrinter); +} diff --git a/MCTargetDesc/EpiphanyMCTargetDesc.h b/MCTargetDesc/EpiphanyMCTargetDesc.h new file mode 100644 index 0000000..ce84098 --- /dev/null +++ b/MCTargetDesc/EpiphanyMCTargetDesc.h @@ -0,0 +1,65 @@ +//===-- EpiphanyMCTargetDesc.h - Epiphany Target Descriptions -----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file provides Epiphany specific target descriptions. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EPIPHANYMCTARGETDESC_H +#define LLVM_EPIPHANYMCTARGETDESC_H + +#include "llvm/Support/DataTypes.h" + +namespace llvm { +class MCAsmBackend; +class MCCodeEmitter; +class MCContext; +class MCInstrInfo; +class MCObjectWriter; +class MCRegisterInfo; +class MCSubtargetInfo; +class StringRef; +class Target; +class raw_ostream; + +extern Target TheEpiphanyTarget; + +namespace Epiphany_MC { + MCSubtargetInfo *createEpiphanyMCSubtargetInfo(StringRef TT, StringRef CPU, + StringRef FS); +} + +//MCCodeEmitter *createEpiphanyMCCodeEmitter(const MCInstrInfo &MCII, +// const MCRegisterInfo &MRI, +// const MCSubtargetInfo &STI, +// MCContext &Ctx); + +//MCObjectWriter *createEpiphanyELFObjectWriter(raw_ostream &OS, +// uint8_t OSABI); + +//MCAsmBackend *createEpiphanyAsmBackend(const Target &T, StringRef TT, +// StringRef CPU); + +} // End llvm namespace + +// Defines symbolic names for Epiphany registers. This defines a mapping from +// register name to register number. +// +#define GET_REGINFO_ENUM +#include "EpiphanyGenRegisterInfo.inc" + +// Defines symbolic names for the Epiphany instructions. +// +#define GET_INSTRINFO_ENUM +#include "EpiphanyGenInstrInfo.inc" + +#define GET_SUBTARGETINFO_ENUM +#include "EpiphanyGenSubtargetInfo.inc" + +#endif diff --git a/MCTargetDesc/LLVMBuild.txt b/MCTargetDesc/LLVMBuild.txt new file mode 100644 index 0000000..ce34d13 --- /dev/null +++ b/MCTargetDesc/LLVMBuild.txt @@ -0,0 +1,24 @@ +;===- ./lib/Target/Epiphany/MCTargetDesc/LLVMBuild.txt ----------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Library +name = EpiphanyDesc +parent = Epiphany +required_libraries = EpiphanyAsmPrinter EpiphanyInfo MC Support +add_to_library_groups = Epiphany + diff --git a/MCTargetDesc/Makefile b/MCTargetDesc/Makefile new file mode 100644 index 0000000..dcd532e --- /dev/null +++ b/MCTargetDesc/Makefile @@ -0,0 +1,16 @@ +##===- lib/Target/Epiphany/TargetDesc/Makefile --------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL = ../../../.. +LIBRARYNAME = LLVMEpiphanyDesc + +# Hack: we need to include 'main' target directory to grab private headers +CPP.Flags += -I$(PROJ_OBJ_DIR)/.. -I$(PROJ_SRC_DIR)/.. + +include $(LEVEL)/Makefile.common diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a4c1bd0 --- /dev/null +++ b/Makefile @@ -0,0 +1,30 @@ +##===- lib/Target/Epiphany/Makefile -------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL = ../../.. +LIBRARYNAME = LLVMEpiphanyCodeGen +TARGET = Epiphany + +# Make sure that tblgen is run, first thing. +BUILT_SOURCES = EpiphanyGenAsmMatcher.inc \ + EpiphanyGenAsmWriter.inc \ + EpiphanyGenCallingConv.inc \ + EpiphanyGenDAGISel.inc \ + EpiphanyGenDisassemblerTables.inc \ + EpiphanyGenInstrInfo.inc \ + EpiphanyGenMCCodeEmitter.inc \ + EpiphanyGenMCPseudoLowering.inc \ + EpiphanyGenRegisterInfo.inc \ + EpiphanyGenSubtargetInfo.inc + +DIRS = InstPrinter AsmParser Disassembler TargetInfo MCTargetDesc Utils + +include $(LEVEL)/Makefile.common + + diff --git a/README.md b/README.md index 126232e..0527a80 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ Epiphany ======== -the actual epiphany backend +the actual epiphany backend, based on the AArch64 backend diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..050aeb8 --- /dev/null +++ b/README.txt @@ -0,0 +1,2 @@ +This file will contain changes that need to be made before Epiphany can become an +officially supported target. Currently a placeholder. diff --git a/TargetInfo/CMakeLists.txt b/TargetInfo/CMakeLists.txt new file mode 100644 index 0000000..35f0b2b --- /dev/null +++ b/TargetInfo/CMakeLists.txt @@ -0,0 +1,7 @@ +include_directories( ${CMAKE_CURRENT_BINARY_DIR}/.. ${CMAKE_CURRENT_SOURCE_DIR}/.. ) + +add_llvm_library(LLVMEpiphanyInfo + EpiphanyTargetInfo.cpp + ) + +add_dependencies(LLVMEpiphanyInfo EpiphanyCommonTableGen) diff --git a/TargetInfo/EpiphanyTargetInfo.cpp b/TargetInfo/EpiphanyTargetInfo.cpp new file mode 100644 index 0000000..5b7aded --- /dev/null +++ b/TargetInfo/EpiphanyTargetInfo.cpp @@ -0,0 +1,24 @@ +//===-- EpiphanyTargetInfo.cpp - Epiphany Target Implementation -------------===// +// +// 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 key registration step for the architecture. +// +//===----------------------------------------------------------------------===// + +#include "Epiphany.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/TargetRegistry.h" +using namespace llvm; + +Target llvm::TheEpiphanyTarget; + +extern "C" void LLVMInitializeEpiphanyTargetInfo() { + RegisterTarget + X(TheEpiphanyTarget, "epiphany", "Epiphany"); +} diff --git a/TargetInfo/LLVMBuild.txt b/TargetInfo/LLVMBuild.txt new file mode 100644 index 0000000..75f222d --- /dev/null +++ b/TargetInfo/LLVMBuild.txt @@ -0,0 +1,24 @@ +;===- ./lib/Target/Epiphany/TargetInfo/LLVMBuild.txt ------------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Library +name = EpiphanyInfo +parent = Epiphany +required_libraries = MC Support Target +add_to_library_groups = Epiphany + diff --git a/TargetInfo/Makefile b/TargetInfo/Makefile new file mode 100644 index 0000000..8a8da98 --- /dev/null +++ b/TargetInfo/Makefile @@ -0,0 +1,15 @@ +##===- lib/Target/Epiphany/TargetInfo/Makefile --------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +LEVEL = ../../../.. +LIBRARYNAME = LLVMEpiphanyInfo + +# Hack: we need to include 'main' target directory to grab private headers +CPPFLAGS = -I$(PROJ_OBJ_DIR)/.. -I$(PROJ_SRC_DIR)/.. + +include $(LEVEL)/Makefile.common diff --git a/Utils/CMakeLists.txt b/Utils/CMakeLists.txt new file mode 100644 index 0000000..707b23c --- /dev/null +++ b/Utils/CMakeLists.txt @@ -0,0 +1,5 @@ +include_directories( ${CMAKE_CURRENT_BINARY_DIR}/.. ${CMAKE_CURRENT_SOURCE_DIR}/.. ) + +add_llvm_library(LLVMEpiphanyUtils + EpiphanyBaseInfo.cpp + ) diff --git a/Utils/EpiphanyBaseInfo.cpp b/Utils/EpiphanyBaseInfo.cpp new file mode 100644 index 0000000..4dfc3bc --- /dev/null +++ b/Utils/EpiphanyBaseInfo.cpp @@ -0,0 +1,71 @@ +//===-- EpiphanyBaseInfo.cpp - Epiphany Base encoding information------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file provides basic encoding and assembly information for Epiphany. +// +//===----------------------------------------------------------------------===// +#include "EpiphanyBaseInfo.h" +#include "llvm/ADT/APFloat.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Regex.h" + +using namespace llvm; + +bool EpiphanyImms::isFPImm(const APFloat &Val, uint32_t &Imm8Bits) { + const fltSemantics &Sem = Val.getSemantics(); + unsigned FracBits = APFloat::semanticsPrecision(Sem) - 1; + + uint32_t ExpMask; + switch (FracBits) { + case 10: // IEEE half-precision + ExpMask = 0x1f; + break; + case 23: // IEEE single-precision + ExpMask = 0xff; + break; + case 52: // IEEE double-precision + ExpMask = 0x7ff; + break; + case 112: // IEEE quad-precision + // No immediates are valid for double precision. + return false; + default: + llvm_unreachable("Only half, single and double precision supported"); + } + + uint32_t ExpStart = FracBits; + uint64_t FracMask = (1ULL << FracBits) - 1; + + uint32_t Sign = Val.isNegative(); + + uint64_t Bits= Val.bitcastToAPInt().getLimitedValue(); + uint64_t Fraction = Bits & FracMask; + int32_t Exponent = ((Bits >> ExpStart) & ExpMask); + Exponent -= ExpMask >> 1; + + // S[d] = imm8<7>:NOT(imm8<6>):Replicate(imm8<6>, 5):imm8<5:0>:Zeros(19) + // D[d] = imm8<7>:NOT(imm8<6>):Replicate(imm8<6>, 8):imm8<5:0>:Zeros(48) + // This translates to: only 4 bits of fraction; -3 <= exp <= 4. + uint64_t A64FracStart = FracBits - 4; + uint64_t A64FracMask = 0xf; + + // Are there too many fraction bits? + if (Fraction & ~(A64FracMask << A64FracStart)) + return false; + + if (Exponent < -3 || Exponent > 4) + return false; + + uint32_t PackedFraction = (Fraction >> A64FracStart) & A64FracMask; + uint32_t PackedExp = (Exponent + 7) & 0x7; + + Imm8Bits = (Sign << 7) | (PackedExp << 4) | PackedFraction; + return true; +} diff --git a/Utils/EpiphanyBaseInfo.h b/Utils/EpiphanyBaseInfo.h new file mode 100644 index 0000000..0a2c192 --- /dev/null +++ b/Utils/EpiphanyBaseInfo.h @@ -0,0 +1,102 @@ +//===-- EpiphanyBaseInfo.h - Top level definitions for Epiphany- --*- 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 Epiphany target useful for the compiler back-end and the MC libraries. +// As such, it deliberately does not include references to LLVM core +// code gen types, passes, etc.. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EPIPHANY_BASEINFO_H +#define LLVM_EPIPHANY_BASEINFO_H + +#include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/ErrorHandling.h" + +namespace llvm { + +// // Enums corresponding to Epiphany condition codes +namespace EpiphanyCC { + // The CondCodes constants map directly to the 4-bit encoding of the + // condition field for predicated instructions. + enum CondCodes { + EQ = 0, + NE, + GTU, + GTEU, + LTEU, + LTU, + GT, + GTE, + LT, + LTE, + BEQ, + BNE, + BLT, + BLTE, + AL, // unconditional, always + L, // link + Invalid + }; + +} // namespace EpiphanyCC + +inline static const char *A64CondCodeToString(EpiphanyCC::CondCodes CC) { + switch (CC) { + default: llvm_unreachable("Unknown condition code"); + case EpiphanyCC::EQ: return "eq"; + case EpiphanyCC::NE: return "ne"; + case EpiphanyCC::GTU: return "gtu"; + case EpiphanyCC::GTEU: return "gteu"; + case EpiphanyCC::LTEU: return "lteu"; + case EpiphanyCC::LTU: return "ltu"; + case EpiphanyCC::GT: return "gt"; + case EpiphanyCC::GTE: return "gte"; + case EpiphanyCC::LT: return "lt"; + case EpiphanyCC::LTE: return "lte"; + case EpiphanyCC::BEQ: return "beq"; + case EpiphanyCC::BNE: return "bne"; + case EpiphanyCC::BLT: return "blt"; + case EpiphanyCC::BLTE: return "blte"; + case EpiphanyCC::AL: return "al"; // unconditional, always + case EpiphanyCC::L: return "l"; // link + + } +} +namespace EpiphanyII { + + enum TOF { + //===--------------------------------------------------------------===// + // Epiphany Specific MachineOperand flags. + + MO_NO_FLAG, + + // LO/HI reloc, only for symbols + MO_LO16, + MO_HI16 + }; +} + +class APFloat; + +namespace EpiphanyImms { + bool isFPImm(const APFloat &Val, uint32_t &Imm8Bits); + + inline bool isFPImm(const APFloat &Val) { + uint32_t Imm8; + return isFPImm(Val, Imm8); + } + +} + +} // end namespace llvm; + +#endif diff --git a/Utils/LLVMBuild.txt b/Utils/LLVMBuild.txt new file mode 100644 index 0000000..f06add2 --- /dev/null +++ b/Utils/LLVMBuild.txt @@ -0,0 +1,23 @@ +;===- ./lib/Target/Epiphany6/Utils/LLVMBuild.txt ----------------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Library +name = EpiphanyUtils +parent = Epiphany +required_libraries = Core Support +add_to_library_groups = Epiphany diff --git a/Utils/Makefile b/Utils/Makefile new file mode 100644 index 0000000..b90a4b6 --- /dev/null +++ b/Utils/Makefile @@ -0,0 +1,15 @@ +##===- lib/Target/Epiphany/Utils/Makefile -------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +LEVEL = ../../../.. +LIBRARYNAME = LLVMEpiphanyUtils + +# Hack: we need to include 'main' Epiphany target directory to grab private headers +#CPP.Flags += -I$(PROJ_OBJ_DIR)/.. -I$(PROJ_SRC_DIR)/.. + +include $(LEVEL)/Makefile.common