diff --git a/CODE_OWNERS.TXT b/CODE_OWNERS.TXT index c0d272f1e7c..f2b8477a27c 100644 --- a/CODE_OWNERS.TXT +++ b/CODE_OWNERS.TXT @@ -44,6 +44,10 @@ N: Greg Clayton E: clayborg@gmail.com D: LLDB +N: Pete Couperus +E: petecoup@synopsys.com +D: ARC backend (lib/Target/ARC/*) + N: Sanjoy Das E: sanjoy@playingwithpointers.com D: IndVar Simplify, Scalar Evolution diff --git a/include/llvm/ADT/Triple.h b/include/llvm/ADT/Triple.h index a4cdeb89458..91ab8554d13 100644 --- a/include/llvm/ADT/Triple.h +++ b/include/llvm/ADT/Triple.h @@ -50,6 +50,7 @@ public: armeb, // ARM (big endian): armeb aarch64, // AArch64 (little endian): aarch64 aarch64_be, // AArch64 (big endian): aarch64_be + arc, // ARC: Synopsys ARC avr, // AVR: Atmel AVR microcontroller bpfel, // eBPF or extended BPF or 64-bit BPF (little endian) bpfeb, // eBPF or extended BPF or 64-bit BPF (big endian) diff --git a/lib/Support/Triple.cpp b/lib/Support/Triple.cpp index b2d2d43e400..73dc59ce41b 100644 --- a/lib/Support/Triple.cpp +++ b/lib/Support/Triple.cpp @@ -25,6 +25,7 @@ StringRef Triple::getArchTypeName(ArchType Kind) { case aarch64_be: return "aarch64_be"; case arm: return "arm"; case armeb: return "armeb"; + case arc: return "arc"; case avr: return "avr"; case bpfel: return "bpfel"; case bpfeb: return "bpfeb"; @@ -83,6 +84,8 @@ StringRef Triple::getArchTypePrefix(ArchType Kind) { case aarch64: case aarch64_be: return "aarch64"; + case arc: return "arc"; + case arm: case armeb: case thumb: @@ -255,6 +258,7 @@ Triple::ArchType Triple::getArchTypeForLLVMName(StringRef Name) { return StringSwitch(Name) .Case("aarch64", aarch64) .Case("aarch64_be", aarch64_be) + .Case("arc", arc) .Case("arm64", aarch64) // "arm64" is an alias for "aarch64" .Case("arm", arm) .Case("armeb", armeb) @@ -384,6 +388,7 @@ static Triple::ArchType parseArch(StringRef ArchName) { .Case("xscaleeb", Triple::armeb) .Case("aarch64", Triple::aarch64) .Case("aarch64_be", Triple::aarch64_be) + .Case("arc", Triple::arc) .Case("arm64", Triple::aarch64) .Case("arm", Triple::arm) .Case("armeb", Triple::armeb) @@ -620,6 +625,7 @@ static Triple::ObjectFormatType getDefaultFormat(const Triple &T) { return Triple::ELF; case Triple::aarch64_be: + case Triple::arc: case Triple::amdgcn: case Triple::amdil: case Triple::amdil64: @@ -1173,6 +1179,7 @@ static unsigned getArchPointerBitWidth(llvm::Triple::ArchType Arch) { case llvm::Triple::msp430: return 16; + case llvm::Triple::arc: case llvm::Triple::arm: case llvm::Triple::armeb: case llvm::Triple::hexagon: @@ -1256,6 +1263,7 @@ Triple Triple::get32BitArchVariant() const { case Triple::amdil: case Triple::hsail: case Triple::spir: + case Triple::arc: case Triple::arm: case Triple::armeb: case Triple::hexagon: @@ -1306,6 +1314,7 @@ Triple Triple::get64BitArchVariant() const { Triple T(*this); switch (getArch()) { case Triple::UnknownArch: + case Triple::arc: case Triple::avr: case Triple::hexagon: case Triple::kalimba: diff --git a/lib/Target/ARC/ARC.h b/lib/Target/ARC/ARC.h new file mode 100644 index 00000000000..65f6ed67eb5 --- /dev/null +++ b/lib/Target/ARC/ARC.h @@ -0,0 +1,33 @@ +//===- ARC.h - Top-level interface for ARC 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 +// ARC back-end. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_ARC_ARC_H +#define LLVM_LIB_TARGET_ARC_ARC_H + +#include "MCTargetDesc/ARCMCTargetDesc.h" +#include "llvm/Target/TargetMachine.h" + +namespace llvm { + +class FunctionPass; +class ARCTargetMachine; + +FunctionPass *createARCISelDag(ARCTargetMachine &TM, + CodeGenOpt::Level OptLevel); +FunctionPass *createARCExpandPseudosPass(); +FunctionPass *createARCBranchFinalizePass(); + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_ARC_ARC_H diff --git a/lib/Target/ARC/ARC.td b/lib/Target/ARC/ARC.td new file mode 100644 index 00000000000..6635630c62a --- /dev/null +++ b/lib/Target/ARC/ARC.td @@ -0,0 +1,25 @@ +//===- ARC.td - Describe the ARC Target Machine ------------*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +include "llvm/Target/Target.td" + +include "ARCRegisterInfo.td" +include "ARCInstrInfo.td" +include "ARCCallingConv.td" + +def ARCInstrInfo : InstrInfo; + +class Proc Features> + : Processor; + +def : Proc<"generic", []>; + +def ARC : Target { + let InstructionSet = ARCInstrInfo; +} diff --git a/lib/Target/ARC/ARCAsmPrinter.cpp b/lib/Target/ARC/ARCAsmPrinter.cpp new file mode 100644 index 00000000000..8c13da0484f --- /dev/null +++ b/lib/Target/ARC/ARCAsmPrinter.cpp @@ -0,0 +1,83 @@ +//===- ARCAsmPrinter.cpp - ARC LLVM assembly writer -------------*- 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 a printer that converts from our internal representation +// of machine-dependent LLVM code to GNU format ARC assembly language. +// +//===----------------------------------------------------------------------===// + +#include "ARC.h" +#include "ARCInstrInfo.h" +#include "ARCMCInstLower.h" +#include "ARCSubtarget.h" +#include "ARCTargetMachine.h" +#include "ARCTargetStreamer.h" +#include "InstPrinter/ARCInstPrinter.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSymbolELF.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Target/TargetLoweringObjectFile.h" +#include + +using namespace llvm; + +#define DEBUG_TYPE "asm-printer" + +namespace { + +class ARCAsmPrinter : public AsmPrinter { + ARCMCInstLower MCInstLowering; + ARCTargetStreamer &getTargetStreamer(); + +public: + explicit ARCAsmPrinter(TargetMachine &TM, + std::unique_ptr Streamer) + : AsmPrinter(TM, std::move(Streamer)), + MCInstLowering(&OutContext, *this) {} + + StringRef getPassName() const override { return "ARC Assembly Printer"; } + void EmitInstruction(const MachineInstr *MI) override; +}; + +} // end anonymous namespace + +ARCTargetStreamer &ARCAsmPrinter::getTargetStreamer() { + return static_cast(*OutStreamer->getTargetStreamer()); +} + +void ARCAsmPrinter::EmitInstruction(const MachineInstr *MI) { + SmallString<128> Str; + raw_svector_ostream O(Str); + + switch (MI->getOpcode()) { + case ARC::DBG_VALUE: + llvm_unreachable("Should be handled target independently"); + break; + } + + MCInst TmpInst; + MCInstLowering.Lower(MI, TmpInst); + EmitToStreamer(*OutStreamer, TmpInst); +} + +// Force static initialization. +extern "C" void LLVMInitializeARCAsmPrinter() { + RegisterAsmPrinter X(getTheARCTarget()); +} diff --git a/lib/Target/ARC/ARCBranchFinalize.cpp b/lib/Target/ARC/ARCBranchFinalize.cpp new file mode 100644 index 00000000000..0fb8a420d86 --- /dev/null +++ b/lib/Target/ARC/ARCBranchFinalize.cpp @@ -0,0 +1,183 @@ +//===- ARCBranchFinalize.cpp - ARC conditional branches ---------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This pass takes existing conditional branches and expands them into longer +// range conditional branches. +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "arc-branch-finalize" + +#include "ARCInstrInfo.h" +#include "ARCTargetMachine.h" +#include "MCTargetDesc/ARCInfo.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/Support/Debug.h" +#include "llvm/Target/TargetInstrInfo.h" +#include + +using namespace llvm; + +namespace llvm { + +void initializeARCBranchFinalizePass(PassRegistry &Registry); +FunctionPass *createARCBranchFinalizePass(); + +} // end namespace llvm + +namespace { + +class ARCBranchFinalize : public MachineFunctionPass { +public: + static char ID; + + ARCBranchFinalize() : MachineFunctionPass(ID) { + initializeARCBranchFinalizePass(*PassRegistry::getPassRegistry()); + } + + StringRef getPassName() const override { + return "ARC Branch Finalization Pass"; + } + + bool runOnMachineFunction(MachineFunction &MF) override; + void replaceWithBRcc(MachineInstr *MI) const; + void replaceWithCmpBcc(MachineInstr *MI) const; + +private: + const ARCInstrInfo *TII{nullptr}; +}; + +char ARCBranchFinalize::ID = 0; + +} // end anonymous namespace + +INITIALIZE_PASS_BEGIN(ARCBranchFinalize, "arc-branch-finalize", + "ARC finalize branches", false, false) +INITIALIZE_PASS_DEPENDENCY(MachineDominatorTree) +INITIALIZE_PASS_END(ARCBranchFinalize, "arc-branch-finalize", + "ARC finalize branches", false, false) + +// BRcc has 6 supported condition codes, which differ from the 16 +// condition codes supported in the predicated instructions: +// EQ -- 000 +// NE -- 001 +// LT -- 010 +// GE -- 011 +// LO -- 100 +// HS -- 101 +static unsigned getCCForBRcc(unsigned CC) { + switch (CC) { + case ARCCC::EQ: + return 0; + case ARCCC::NE: + return 1; + case ARCCC::LT: + return 2; + case ARCCC::GE: + return 3; + case ARCCC::LO: + return 4; + case ARCCC::HS: + return 5; + default: + return -1U; + } +} + +static bool isBRccPseudo(MachineInstr *MI) { + return !(MI->getOpcode() != ARC::BRcc_rr_p && + MI->getOpcode() != ARC::BRcc_ru6_p); +} + +static unsigned getBRccForPseudo(MachineInstr *MI) { + assert(isBRccPseudo(MI) && "Can't get BRcc for wrong instruction."); + if (MI->getOpcode() == ARC::BRcc_rr_p) + return ARC::BRcc_rr; + return ARC::BRcc_ru6; +} + +static unsigned getCmpForPseudo(MachineInstr *MI) { + assert(isBRccPseudo(MI) && "Can't get BRcc for wrong instruction."); + if (MI->getOpcode() == ARC::BRcc_rr_p) + return ARC::CMP_rr; + return ARC::CMP_ru6; +} + +void ARCBranchFinalize::replaceWithBRcc(MachineInstr *MI) const { + DEBUG(dbgs() << "Replacing pseudo branch with BRcc\n"); + unsigned CC = getCCForBRcc(MI->getOperand(3).getImm()); + if (CC != -1U) { + BuildMI(*MI->getParent(), MI, MI->getDebugLoc(), + TII->get(getBRccForPseudo(MI))) + .addMBB(MI->getOperand(0).getMBB()) + .addReg(MI->getOperand(1).getReg()) + .add(MI->getOperand(2)) + .addImm(getCCForBRcc(MI->getOperand(3).getImm())); + MI->eraseFromParent(); + } else { + replaceWithCmpBcc(MI); + } +} + +void ARCBranchFinalize::replaceWithCmpBcc(MachineInstr *MI) const { + DEBUG(dbgs() << "Branch: " << *MI << "\n"); + DEBUG(dbgs() << "Replacing pseudo branch with Cmp + Bcc\n"); + BuildMI(*MI->getParent(), MI, MI->getDebugLoc(), + TII->get(getCmpForPseudo(MI))) + .addReg(MI->getOperand(1).getReg()) + .add(MI->getOperand(2)); + BuildMI(*MI->getParent(), MI, MI->getDebugLoc(), TII->get(ARC::Bcc)) + .addMBB(MI->getOperand(0).getMBB()) + .addImm(MI->getOperand(3).getImm()); + MI->eraseFromParent(); +} + +bool ARCBranchFinalize::runOnMachineFunction(MachineFunction &MF) { + DEBUG(dbgs() << "Running ARC Branch Finalize on " + << MF.getFunction()->getName() << "\n"); + std::vector Branches; + bool Changed = false; + unsigned MaxSize = 0; + TII = MF.getSubtarget().getInstrInfo(); + std::map BlockToPCMap; + std::vector> BranchToPCList; + unsigned PC = 0; + + for (auto &MBB : MF) { + BlockToPCMap.insert(std::make_pair(&MBB, PC)); + for (auto &MI : MBB) { + unsigned Size = TII->getInstSizeInBytes(MI); + if (Size > 8 || Size == 0) { + DEBUG(dbgs() << "Unknown (or size 0) size for: " << MI << "\n"); + } else { + MaxSize += Size; + } + if (MI.isBranch()) { + Branches.push_back(&MI); + BranchToPCList.emplace_back(&MI, PC); + } + PC += Size; + } + } + for (auto P : BranchToPCList) { + if (isBRccPseudo(P.first)) + isInt<9>(MaxSize) ? replaceWithBRcc(P.first) : replaceWithCmpBcc(P.first); + } + + DEBUG(dbgs() << "Estimated function size for " << MF.getFunction()->getName() + << ": " << MaxSize << "\n"); + + return Changed; +} + +FunctionPass *llvm::createARCBranchFinalizePass() { + return new ARCBranchFinalize(); +} diff --git a/lib/Target/ARC/ARCCallingConv.td b/lib/Target/ARC/ARCCallingConv.td new file mode 100644 index 00000000000..b7d37bc2a41 --- /dev/null +++ b/lib/Target/ARC/ARCCallingConv.td @@ -0,0 +1,41 @@ +//===- ARCCallingConv.td - Calling Conventions for ARC -----*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// This describes the calling conventions for ARC architecture. +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// ARC Return Value Calling Convention +//===----------------------------------------------------------------------===// +def RetCC_ARC : CallingConv<[ + // i32 are returned in registers R0, R1, R2, R3 + CCIfType<[i32, i64], CCAssignToReg<[R0, R1, R2, R3]>>, + + // Integer values get stored in stack slots that are 4 bytes in + // size and 4-byte aligned. + CCIfType<[i64], CCAssignToStack<8, 4>>, + CCIfType<[i32], CCAssignToStack<4, 4>> +]>; + +//===----------------------------------------------------------------------===// +// ARC Argument Calling Conventions +//===----------------------------------------------------------------------===// +def CC_ARC : CallingConv<[ + // Promote i8/i16 arguments to i32. + CCIfType<[i8, i16], CCPromoteToType>, + + // The first 8 integer arguments are passed in integer registers. + CCIfType<[i32, i64], CCAssignToReg<[R0, R1, R2, R3, R4, R5, R6, R7]>>, + + // Integer values get stored in stack slots that are 4 bytes in + // size and 4-byte aligned. + CCIfType<[i64], CCAssignToStack<8, 4>>, + CCIfType<[i32], CCAssignToStack<4, 4>> +]>; + +def CSR_ARC : CalleeSavedRegs<(add (sequence "R%u", 13, 25), GP, FP)>; diff --git a/lib/Target/ARC/ARCExpandPseudos.cpp b/lib/Target/ARC/ARCExpandPseudos.cpp new file mode 100644 index 00000000000..3177735c052 --- /dev/null +++ b/lib/Target/ARC/ARCExpandPseudos.cpp @@ -0,0 +1,103 @@ +//===- ARCExpandPseudosPass - ARC expand pseudo loads -----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This pass expands stores with large offsets into an appropriate sequence. +//===----------------------------------------------------------------------===// + +#include "ARC.h" +#include "ARCInstrInfo.h" +#include "ARCRegisterInfo.h" +#include "ARCSubtarget.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" + +using namespace llvm; + +#define DEBUG_TYPE "arc-expand-pseudos" + +namespace { + +class ARCExpandPseudos : public MachineFunctionPass { +public: + static char ID; + ARCExpandPseudos() : MachineFunctionPass(ID) {} + + bool runOnMachineFunction(MachineFunction &Fn) override; + + StringRef getPassName() const override { return "ARC Expand Pseudos"; } + +private: + void ExpandStore(MachineFunction &, MachineBasicBlock::iterator); + + const ARCInstrInfo *TII; +}; + +char ARCExpandPseudos::ID = 0; + +} // end anonymous namespace + +static unsigned getMappedOp(unsigned PseudoOp) { + switch (PseudoOp) { + case ARC::ST_FAR: + return ARC::ST_rs9; + case ARC::STH_FAR: + return ARC::STH_rs9; + case ARC::STB_FAR: + return ARC::STB_rs9; + default: + llvm_unreachable("Unhandled pseudo op."); + } +} + +void ARCExpandPseudos::ExpandStore(MachineFunction &MF, + MachineBasicBlock::iterator SII) { + MachineInstr &SI = *SII; + unsigned AddrReg = MF.getRegInfo().createVirtualRegister(&ARC::GPR32RegClass); + unsigned AddOpc = + isUInt<6>(SI.getOperand(2).getImm()) ? ARC::ADD_rru6 : ARC::ADD_rrlimm; + BuildMI(*SI.getParent(), SI, SI.getDebugLoc(), TII->get(AddOpc), AddrReg) + .addReg(SI.getOperand(1).getReg()) + .addImm(SI.getOperand(2).getImm()); + BuildMI(*SI.getParent(), SI, SI.getDebugLoc(), + TII->get(getMappedOp(SI.getOpcode()))) + .addReg(SI.getOperand(0).getReg()) + .addReg(AddrReg) + .addImm(0); + SI.eraseFromParent(); +} + +bool ARCExpandPseudos::runOnMachineFunction(MachineFunction &MF) { + const ARCSubtarget *STI = &MF.getSubtarget(); + TII = STI->getInstrInfo(); + bool ExpandedStore = false; + for (auto &MBB : MF) { + MachineBasicBlock::iterator MBBI = MBB.begin(), E = MBB.end(); + while (MBBI != E) { + MachineBasicBlock::iterator NMBBI = std::next(MBBI); + switch (MBBI->getOpcode()) { + case ARC::ST_FAR: + case ARC::STH_FAR: + case ARC::STB_FAR: + ExpandStore(MF, MBBI); + ExpandedStore = true; + break; + default: + break; + } + MBBI = NMBBI; + } + } + return ExpandedStore; +} + +FunctionPass *llvm::createARCExpandPseudosPass() { + return new ARCExpandPseudos(); +} diff --git a/lib/Target/ARC/ARCFrameLowering.cpp b/lib/Target/ARC/ARCFrameLowering.cpp new file mode 100644 index 00000000000..225b0a1dd86 --- /dev/null +++ b/lib/Target/ARC/ARCFrameLowering.cpp @@ -0,0 +1,472 @@ +//===- ARCFrameLowering.cpp - ARC Frame 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 ARC implementation of the TargetFrameLowering class. +// +//===----------------------------------------------------------------------===// + +#include "ARCFrameLowering.h" +#include "ARCMachineFunctionInfo.h" +#include "ARCSubtarget.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/CodeGen/RegisterScavenging.h" +#include "llvm/IR/Function.h" +#include "llvm/Support/Debug.h" +#include "llvm/Target/TargetRegisterInfo.h" + +#define DEBUG_TYPE "arc-frame-lowering" + +using namespace llvm; + +static cl::opt + UseSaveRestoreFunclet("arc-save-restore-funclet", cl::Hidden, + cl::desc("Use arc callee save/restore functions"), + cl::init(true)); + +static const char *store_funclet_name[] = { + "__st_r13_to_r15", "__st_r13_to_r16", "__st_r13_to_r17", "__st_r13_to_r18", + "__st_r13_to_r19", "__st_r13_to_r20", "__st_r13_to_r21", "__st_r13_to_r22", + "__st_r13_to_r23", "__st_r13_to_r24", "__st_r13_to_r25", +}; + +static const char *load_funclet_name[] = { + "__ld_r13_to_r15", "__ld_r13_to_r16", "__ld_r13_to_r17", "__ld_r13_to_r18", + "__ld_r13_to_r19", "__ld_r13_to_r20", "__ld_r13_to_r21", "__ld_r13_to_r22", + "__ld_r13_to_r23", "__ld_r13_to_r24", "__ld_r13_to_r25", +}; + +static void generateStackAdjustment(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, + const ARCInstrInfo &TII, DebugLoc dl, + int Amount, int StackPtr) { + unsigned AdjOp; + if (!Amount) + return; + bool Positive; + unsigned AbsAmount; + if (Amount < 0) { + AbsAmount = -Amount; + Positive = false; + } else { + AbsAmount = Amount; + Positive = true; + } + + DEBUG(dbgs() << "Internal: adjust stack by: " << Amount << "," << AbsAmount + << "\n"); + + assert((AbsAmount % 4 == 0) && "Stack adjustments must be 4-byte aligned."); + if (isUInt<6>(AbsAmount)) + AdjOp = Positive ? ARC::ADD_rru6 : ARC::SUB_rru6; + else + AdjOp = Positive ? ARC::ADD_rrlimm : ARC::SUB_rrlimm; + + BuildMI(MBB, MBBI, dl, TII.get(AdjOp), StackPtr) + .addReg(StackPtr) + .addImm(AbsAmount); +} + +static unsigned +determineLastCalleeSave(const std::vector &CSI) { + unsigned Last = 0; + for (auto Reg : CSI) { + assert(Reg.getReg() >= ARC::R13 && Reg.getReg() <= ARC::R25 && + "Unexpected callee saved reg."); + if (Reg.getReg() > Last) + Last = Reg.getReg(); + } + return Last; +} + +void ARCFrameLowering::determineCalleeSaves(MachineFunction &MF, + BitVector &SavedRegs, + RegScavenger *RS) const { + DEBUG(dbgs() << "Determine Callee Saves: " << MF.getFunction()->getName() + << "\n"); + TargetFrameLowering::determineCalleeSaves(MF, SavedRegs, RS); + SavedRegs.set(ARC::BLINK); +} + +void ARCFrameLowering::adjustStackToMatchRecords( + MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, + bool Allocate) const { + MachineFunction &MF = *MBB.getParent(); + int ScalarAlloc = MF.getFrameInfo().getStackSize(); + + if (Allocate) { + // Allocate by adjusting by the negative of what the record holder tracked + // it tracked a positive offset in a downward growing stack. + ScalarAlloc = -ScalarAlloc; + } + + generateStackAdjustment(MBB, MBBI, *ST.getInstrInfo(), DebugLoc(), + ScalarAlloc, ARC::SP); +} + +/// Insert prolog code into the function. +/// For ARC, this inserts a call to a function that puts required callee saved +/// registers onto the stack, when enough callee saved registers are required. +void ARCFrameLowering::emitPrologue(MachineFunction &MF, + MachineBasicBlock &MBB) const { + DEBUG(dbgs() << "Emit Prologue: " << MF.getFunction()->getName() << "\n"); + auto *AFI = MF.getInfo(); + MachineModuleInfo &MMI = MF.getMMI(); + MCContext &Context = MMI.getContext(); + const MCRegisterInfo *MRI = Context.getRegisterInfo(); + const ARCInstrInfo *TII = MF.getSubtarget().getInstrInfo(); + MachineBasicBlock::iterator MBBI = MBB.begin(); + // Debug location must be unknown since the first debug location is used + // to determine the end of the prologue. + DebugLoc dl; + MachineFrameInfo &MFI = MF.getFrameInfo(); + const std::vector &CSI = MFI.getCalleeSavedInfo(); + unsigned Last = determineLastCalleeSave(CSI); + unsigned StackSlotsUsedByFunclet = 0; + bool SavedBlink = false; + unsigned AlreadyAdjusted = 0; + if (MF.getFunction()->isVarArg()) { + // Add in the varargs area here first. + DEBUG(dbgs() << "Varargs\n"); + unsigned VarArgsBytes = MFI.getObjectSize(AFI->getVarArgsFrameIndex()); + BuildMI(MBB, MBBI, dl, TII->get(ARC::SUB_rru6)) + .addReg(ARC::SP) + .addReg(ARC::SP) + .addImm(VarArgsBytes); + } + if (hasFP(MF)) { + DEBUG(dbgs() << "Saving FP\n"); + BuildMI(MBB, MBBI, dl, TII->get(ARC::ST_AW_rs9)) + .addReg(ARC::SP, RegState::Define) + .addReg(ARC::FP) + .addReg(ARC::SP) + .addImm(-4); + AlreadyAdjusted += 4; + } + if (UseSaveRestoreFunclet && Last > ARC::R14) { + DEBUG(dbgs() << "Creating store funclet.\n"); + // BL to __save_r13_to_getRegAsmName()> + StackSlotsUsedByFunclet = Last - ARC::R12; + BuildMI(MBB, MBBI, dl, TII->get(ARC::PUSH_S_BLINK)); + BuildMI(MBB, MBBI, dl, TII->get(ARC::SUB_rru6)) + .addReg(ARC::SP) + .addReg(ARC::SP) + .addImm(4 * StackSlotsUsedByFunclet); + BuildMI(MBB, MBBI, dl, TII->get(ARC::BL)) + .addExternalSymbol(store_funclet_name[Last - ARC::R15]) + .addReg(ARC::BLINK, RegState::Implicit | RegState::Kill); + AlreadyAdjusted += 4 * (StackSlotsUsedByFunclet + 1); + SavedBlink = true; + } + // If we haven't saved BLINK, but we need to...do that now. + if (MFI.hasCalls() && !SavedBlink) { + DEBUG(dbgs() << "Creating save blink.\n"); + BuildMI(MBB, MBBI, dl, TII->get(ARC::PUSH_S_BLINK)); + AlreadyAdjusted += 4; + } + if (AFI->MaxCallStackReq > 0) + MFI.setStackSize(MFI.getStackSize() + AFI->MaxCallStackReq); + // We have already saved some of the stack... + DEBUG(dbgs() << "Adjusting stack by: " + << (MFI.getStackSize() - AlreadyAdjusted) << "\n"); + generateStackAdjustment(MBB, MBBI, *ST.getInstrInfo(), dl, + -(MFI.getStackSize() - AlreadyAdjusted), ARC::SP); + + if (hasFP(MF)) { + DEBUG(dbgs() << "Setting FP from SP.\n"); + BuildMI(MBB, MBBI, dl, + TII->get(isUInt<6>(MFI.getStackSize()) ? ARC::ADD_rru6 + : ARC::ADD_rrlimm), + ARC::FP) + .addReg(ARC::SP) + .addImm(MFI.getStackSize()); + } + + // Emit CFI records: + // .cfi_def_cfa_offset StackSize + // .cfi_offset fp, -StackSize + // .cfi_offset blink, -StackSize+4 + unsigned CFIIndex = MF.addFrameInst( + MCCFIInstruction::createDefCfaOffset(nullptr, -MFI.getStackSize())); + BuildMI(MBB, MBBI, dl, TII->get(TargetOpcode::CFI_INSTRUCTION)) + .addCFIIndex(CFIIndex) + .setMIFlags(MachineInstr::FrameSetup); + + int CurOffset = -4; + if (hasFP(MF)) { + CFIIndex = MF.addFrameInst(MCCFIInstruction::createOffset( + nullptr, MRI->getDwarfRegNum(ARC::FP, true), CurOffset)); + BuildMI(MBB, MBBI, dl, TII->get(TargetOpcode::CFI_INSTRUCTION)) + .addCFIIndex(CFIIndex) + .setMIFlags(MachineInstr::FrameSetup); + CurOffset -= 4; + } + + if (MFI.hasCalls()) { + CFIIndex = MF.addFrameInst(MCCFIInstruction::createOffset( + nullptr, MRI->getDwarfRegNum(ARC::BLINK, true), CurOffset)); + BuildMI(MBB, MBBI, dl, TII->get(TargetOpcode::CFI_INSTRUCTION)) + .addCFIIndex(CFIIndex) + .setMIFlags(MachineInstr::FrameSetup); + } + // CFI for the rest of the registers. + for (const auto &Entry : CSI) { + unsigned Reg = Entry.getReg(); + int FI = Entry.getFrameIdx(); + // Skip BLINK and FP. + if ((hasFP(MF) && Reg == ARC::FP) || (MFI.hasCalls() && Reg == ARC::BLINK)) + continue; + CFIIndex = MF.addFrameInst(MCCFIInstruction::createOffset( + nullptr, MRI->getDwarfRegNum(Reg, true), MFI.getObjectOffset(FI))); + BuildMI(MBB, MBBI, dl, TII->get(TargetOpcode::CFI_INSTRUCTION)) + .addCFIIndex(CFIIndex) + .setMIFlags(MachineInstr::FrameSetup); + } +} + +/// Insert epilog code into the function. +/// For ARC, this inserts a call to a function that restores callee saved +/// registers onto the stack, when enough callee saved registers are required. +void ARCFrameLowering::emitEpilogue(MachineFunction &MF, + MachineBasicBlock &MBB) const { + DEBUG(dbgs() << "Emit Epilogue: " << MF.getFunction()->getName() << "\n"); + auto *AFI = MF.getInfo(); + const ARCInstrInfo *TII = MF.getSubtarget().getInstrInfo(); + MachineBasicBlock::iterator MBBI = MBB.getFirstTerminator(); + MachineFrameInfo &MFI = MF.getFrameInfo(); + uint64_t StackSize = MF.getFrameInfo().getStackSize(); + bool SavedBlink = false; + unsigned AmountAboveFunclet = 0; + // If we have variable sized frame objects, then we have to move + // the stack pointer to a known spot (fp - StackSize). + // Then, replace the frame pointer by (new) [sp,StackSize-4]. + // Then, move the stack pointer the rest of the way (sp = sp + StackSize). + if (hasFP(MF)) { + BuildMI(MBB, MBBI, DebugLoc(), TII->get(ARC::SUB_rru6), ARC::SP) + .addReg(ARC::FP) + .addImm(StackSize); + AmountAboveFunclet += 4; + } + + // Now, move the stack pointer to the bottom of the save area for the funclet. + const std::vector &CSI = MFI.getCalleeSavedInfo(); + unsigned Last = determineLastCalleeSave(CSI); + unsigned StackSlotsUsedByFunclet = 0; + // Now, restore the callee save registers. + if (UseSaveRestoreFunclet && Last > ARC::R14) { + // BL to __ld_r13_to_getRegAsmName()> + StackSlotsUsedByFunclet = Last - ARC::R12; + AmountAboveFunclet += 4 * (StackSlotsUsedByFunclet + 1); + SavedBlink = true; + } + + if (MFI.hasCalls() && !SavedBlink) { + AmountAboveFunclet += 4; + SavedBlink = true; + } + + // Move the stack pointer up to the point of the funclet. + if (StackSize - AmountAboveFunclet) { + BuildMI(MBB, MBBI, MBB.findDebugLoc(MBBI), TII->get(ARC::ADD_rru6)) + .addReg(ARC::SP) + .addReg(ARC::SP) + .addImm(StackSize - AmountAboveFunclet); + } + + if (StackSlotsUsedByFunclet) { + BuildMI(MBB, MBBI, MBB.findDebugLoc(MBBI), TII->get(ARC::BL)) + .addExternalSymbol(load_funclet_name[Last - ARC::R15]) + .addReg(ARC::BLINK, RegState::Implicit | RegState::Kill); + BuildMI(MBB, MBBI, MBB.findDebugLoc(MBBI), TII->get(ARC::ADD_rru6)) + .addReg(ARC::SP) + .addReg(ARC::SP) + .addImm(4 * (StackSlotsUsedByFunclet)); + } + // Now, pop blink if necessary. + if (SavedBlink) { + BuildMI(MBB, MBBI, MBB.findDebugLoc(MBBI), TII->get(ARC::POP_S_BLINK)); + } + // Now, pop fp if necessary. + if (hasFP(MF)) { + BuildMI(MBB, MBBI, MBB.findDebugLoc(MBBI), TII->get(ARC::LD_AB_rs9)) + .addReg(ARC::SP, RegState::Define) + .addReg(ARC::FP, RegState::Define) + .addReg(ARC::SP) + .addImm(4); + } + + // Relieve the varargs area if necessary. + if (MF.getFunction()->isVarArg()) { + // Add in the varargs area here first. + DEBUG(dbgs() << "Varargs\n"); + unsigned VarArgsBytes = MFI.getObjectSize(AFI->getVarArgsFrameIndex()); + BuildMI(MBB, MBBI, MBB.findDebugLoc(MBBI), TII->get(ARC::ADD_rru6)) + .addReg(ARC::SP) + .addReg(ARC::SP) + .addImm(VarArgsBytes); + } +} + +static std::vector::iterator +getSavedReg(std::vector &V, unsigned reg) { + for (auto I = V.begin(), E = V.end(); I != E; ++I) { + if (reg == I->getReg()) + return I; + } + return V.end(); +} + +bool ARCFrameLowering::assignCalleeSavedSpillSlots( + MachineFunction &MF, const TargetRegisterInfo *TRI, + std::vector &CSI) const { + // Use this opportunity to assign the spill slots for all of the potential + // callee save registers (blink, fp, r13->r25) that we care about the + // placement for. We can calculate all of that data here. + int CurOffset = -4; + unsigned Last = determineLastCalleeSave(CSI); + MachineFrameInfo &MFI = MF.getFrameInfo(); + if (hasFP(MF)) { + // Create a fixed slot at for FP + int StackObj = MFI.CreateFixedSpillStackObject(4, CurOffset, true); + DEBUG(dbgs() << "Creating fixed object (" << StackObj << ") for FP at " + << CurOffset << "\n"); + (void)StackObj; + CurOffset -= 4; + } + if (MFI.hasCalls() || (UseSaveRestoreFunclet && Last > ARC::R14)) { + // Create a fixed slot for BLINK. + int StackObj = MFI.CreateFixedSpillStackObject(4, CurOffset, true); + DEBUG(dbgs() << "Creating fixed object (" << StackObj << ") for BLINK at " + << CurOffset << "\n"); + (void)StackObj; + CurOffset -= 4; + } + + // Create slots for last down to r13. + for (unsigned Which = Last; Which > ARC::R12; Which--) { + auto RegI = getSavedReg(CSI, Which); + if (RegI == CSI.end() || RegI->getFrameIdx() == 0) { + // Always create the stack slot. If for some reason the register isn't in + // the save list, then don't worry about it. + int FI = MFI.CreateFixedSpillStackObject(4, CurOffset, true); + if (RegI != CSI.end()) + RegI->setFrameIdx(FI); + } else + MFI.setObjectOffset(RegI->getFrameIdx(), CurOffset); + CurOffset -= 4; + } + for (auto &I : CSI) { + if (I.getReg() > ARC::R12) + continue; + if (I.getFrameIdx() == 0) { + I.setFrameIdx(MFI.CreateFixedSpillStackObject(4, CurOffset, true)); + DEBUG(dbgs() << "Creating fixed object (" << I.getFrameIdx() + << ") for other register at " << CurOffset << "\n"); + } else { + MFI.setObjectOffset(I.getFrameIdx(), CurOffset); + DEBUG(dbgs() << "Updating fixed object (" << I.getFrameIdx() + << ") for other register at " << CurOffset << "\n"); + } + CurOffset -= 4; + } + return true; +} + +bool ARCFrameLowering::spillCalleeSavedRegisters( + MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, + const std::vector &CSI, + const TargetRegisterInfo *TRI) const { + DEBUG(dbgs() << "Spill callee saved registers: " + << MBB.getParent()->getFunction()->getName() << "\n"); + // There are routines for saving at least 3 registers (r13 to r15, etc.) + unsigned Last = determineLastCalleeSave(CSI); + if (UseSaveRestoreFunclet && Last > ARC::R14) { + // Use setObjectOffset for these registers. + // Needs to be in or before processFunctionBeforeFrameFinalized. + // Or, do assignCalleeSaveSpillSlots? + // Will be handled in prolog. + return true; + } + return false; +} + +bool ARCFrameLowering::restoreCalleeSavedRegisters( + MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, + std::vector &CSI, const TargetRegisterInfo *TRI) const { + DEBUG(dbgs() << "Restore callee saved registers: " + << MBB.getParent()->getFunction()->getName() << "\n"); + // There are routines for saving at least 3 registers (r13 to r15, etc.) + unsigned Last = determineLastCalleeSave(CSI); + if (UseSaveRestoreFunclet && Last > ARC::R14) { + // Will be handled in epilog. + return true; + } + return false; +} + +// Adjust local variables that are 4-bytes or larger to 4-byte boundary +void ARCFrameLowering::processFunctionBeforeFrameFinalized( + MachineFunction &MF, RegScavenger *RS) const { + const TargetRegisterInfo *RegInfo = MF.getSubtarget().getRegisterInfo(); + DEBUG(dbgs() << "Process function before frame finalized: " + << MF.getFunction()->getName() << "\n"); + MachineFrameInfo &MFI = MF.getFrameInfo(); + DEBUG(dbgs() << "Current stack size: " << MFI.getStackSize() << "\n"); + const TargetRegisterClass *RC = &ARC::GPR32RegClass; + if (MFI.hasStackObjects()) { + int RegScavFI = MFI.CreateStackObject( + RegInfo->getSpillSize(*RC), RegInfo->getSpillAlignment(*RC), false); + RS->addScavengingFrameIndex(RegScavFI); + DEBUG(dbgs() << "Created scavenging index RegScavFI=" << RegScavFI << "\n"); + } +} + +static void emitRegUpdate(MachineBasicBlock &MBB, + MachineBasicBlock::iterator &MBBI, DebugLoc dl, + unsigned Reg, int NumBytes, bool IsAdd, + const ARCInstrInfo *TII) { + unsigned Opc = IsAdd ? ARC::ADD_rru6 : ARC::SUB_rru6; + BuildMI(MBB, MBBI, dl, TII->get(Opc), Reg) + .addReg(Reg, RegState::Kill) + .addImm(NumBytes); +} + +MachineBasicBlock::iterator ARCFrameLowering::eliminateCallFramePseudoInstr( + MachineFunction &MF, MachineBasicBlock &MBB, + MachineBasicBlock::iterator I) const { + DEBUG(dbgs() << "EmitCallFramePseudo: " << MF.getFunction()->getName() + << "\n"); + const ARCInstrInfo *TII = MF.getSubtarget().getInstrInfo(); + MachineInstr &Old = *I; + DebugLoc dl = Old.getDebugLoc(); + unsigned Amt = Old.getOperand(0).getImm(); + auto *AFI = MF.getInfo(); + if (!hasFP(MF)) { + if (Amt > AFI->MaxCallStackReq && Old.getOpcode() == ARC::ADJCALLSTACKDOWN) + AFI->MaxCallStackReq = Amt; + } else { + if (Amt != 0) { + assert((Old.getOpcode() == ARC::ADJCALLSTACKDOWN || + Old.getOpcode() == ARC::ADJCALLSTACKUP) && + "Unknown Frame Pseudo."); + bool IsAdd = (Old.getOpcode() == ARC::ADJCALLSTACKUP); + emitRegUpdate(MBB, I, dl, ARC::SP, Amt, IsAdd, TII); + } + } + return MBB.erase(I); +} + +bool ARCFrameLowering::hasFP(const MachineFunction &MF) const { + const TargetRegisterInfo *RegInfo = MF.getSubtarget().getRegisterInfo(); + bool HasFP = MF.getTarget().Options.DisableFramePointerElim(MF) || + MF.getFrameInfo().hasVarSizedObjects() || + MF.getFrameInfo().isFrameAddressTaken() || + RegInfo->needsStackRealignment(MF); + return HasFP; +} diff --git a/lib/Target/ARC/ARCFrameLowering.h b/lib/Target/ARC/ARCFrameLowering.h new file mode 100644 index 00000000000..ac5378adbd8 --- /dev/null +++ b/lib/Target/ARC/ARCFrameLowering.h @@ -0,0 +1,78 @@ +//===- ARCFrameLowering.h - Define frame lowering for ARC -------*- 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 ARC specific frame lowering. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_ARC_ARCFRAMELOWERING_H +#define LLVM_LIB_TARGET_ARC_ARCFRAMELOWERING_H + +#include "ARC.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/Target/TargetFrameLowering.h" + +namespace llvm { + +class MachineFunction; +class ARCSubtarget; +class ARCInstrInfo; + +class ARCFrameLowering : public TargetFrameLowering { +public: + ARCFrameLowering(const ARCSubtarget &st) + : TargetFrameLowering(TargetFrameLowering::StackGrowsDown, 4, 0), ST(st) { + } + + /// Insert Prologue into the function. + void emitPrologue(MachineFunction &MF, MachineBasicBlock &MBB) const override; + + /// Insert Epilogue into the function. + void emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const override; + + /// Add explicit callee save registers. + void determineCalleeSaves(MachineFunction &MF, BitVector &SavedRegs, + RegScavenger *RS) const override; + + bool spillCalleeSavedRegisters(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + const std::vector &CSI, + const TargetRegisterInfo *TRI) const override; + + bool + restoreCalleeSavedRegisters(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + std::vector &CSI, + const TargetRegisterInfo *TRI) const override; + + void processFunctionBeforeFrameFinalized(MachineFunction &MF, + RegScavenger *RS) const override; + + bool hasFP(const MachineFunction &MF) const override; + + MachineBasicBlock::iterator + eliminateCallFramePseudoInstr(MachineFunction &MF, MachineBasicBlock &MBB, + MachineBasicBlock::iterator I) const override; + + bool assignCalleeSavedSpillSlots( + llvm::MachineFunction &, const llvm::TargetRegisterInfo *, + std::vector &) const override; + +private: + void adjustStackToMatchRecords(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + bool allocate) const; + + const ARCSubtarget &ST; +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_ARC_ARCFRAMELOWERING_H diff --git a/lib/Target/ARC/ARCISelDAGToDAG.cpp b/lib/Target/ARC/ARCISelDAGToDAG.cpp new file mode 100644 index 00000000000..a87710d32e0 --- /dev/null +++ b/lib/Target/ARC/ARCISelDAGToDAG.cpp @@ -0,0 +1,182 @@ +//===- ARCISelDAGToDAG.cpp - ARC dag to dag inst selector -------*- 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 an instruction selector for the ARC target. +// +//===----------------------------------------------------------------------===// + +#include "ARC.h" +#include "ARCTargetMachine.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/SelectionDAG.h" +#include "llvm/CodeGen/SelectionDAGISel.h" +#include "llvm/IR/CallingConv.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Target/TargetLowering.h" + +using namespace llvm; + +/// ARCDAGToDAGISel - ARC specific code to select ARC machine +/// instructions for SelectionDAG operations. +namespace { + +class ARCDAGToDAGISel : public SelectionDAGISel { +public: + ARCDAGToDAGISel(ARCTargetMachine &TM, CodeGenOpt::Level OptLevel) + : SelectionDAGISel(TM, OptLevel) {} + + void Select(SDNode *N) override; + + // Complex Pattern Selectors. + bool SelectFrameADDR_ri(SDValue Addr, SDValue &Base, SDValue &Offset); + bool SelectAddrModeS9(SDValue Addr, SDValue &Base, SDValue &Offset); + bool SelectAddrModeImm(SDValue Addr, SDValue &Base, SDValue &Offset); + bool SelectAddrModeFar(SDValue Addr, SDValue &Base, SDValue &Offset); + bool SelectCMOVPred(SDValue N, SDValue &Pred, SDValue &Reg) { + const ConstantSDNode *CN = cast(N); + Pred = CurDAG->getTargetConstant(CN->getZExtValue(), SDLoc(N), MVT::i32); + Reg = CurDAG->getRegister(ARC::STATUS32, MVT::i32); + return true; + } + + StringRef getPassName() const override { + return "ARC DAG->DAG Pattern Instruction Selection"; + } + +// Include the pieces autogenerated from the target description. +#include "ARCGenDAGISel.inc" +}; + +} // end anonymous namespace + +/// This pass converts a legalized DAG into a ARC-specific DAG, ready for +/// instruction scheduling. +FunctionPass *llvm::createARCISelDag(ARCTargetMachine &TM, + CodeGenOpt::Level OptLevel) { + return new ARCDAGToDAGISel(TM, OptLevel); +} + +bool ARCDAGToDAGISel::SelectAddrModeImm(SDValue Addr, SDValue &Base, + SDValue &Offset) { + if (Addr.getOpcode() == ARCISD::GAWRAPPER) { + Base = Addr.getOperand(0); + Offset = CurDAG->getTargetConstant(0, SDLoc(Addr), MVT::i32); + return true; + } + return false; +} + +bool ARCDAGToDAGISel::SelectAddrModeS9(SDValue Addr, SDValue &Base, + SDValue &Offset) { + if (Addr.getOpcode() == ARCISD::GAWRAPPER) { + return false; + } + + if (Addr.getOpcode() != ISD::ADD && Addr.getOpcode() != ISD::SUB && + !CurDAG->isBaseWithConstantOffset(Addr)) { + if (Addr.getOpcode() == ISD::FrameIndex) { + // Match frame index. + int FI = cast(Addr)->getIndex(); + Base = CurDAG->getTargetFrameIndex( + FI, TLI->getPointerTy(CurDAG->getDataLayout())); + } else { + Base = Addr; + } + Offset = CurDAG->getTargetConstant(0, SDLoc(Addr), MVT::i32); + return true; + } + + if (ConstantSDNode *RHS = dyn_cast(Addr.getOperand(1))) { + int32_t RHSC = RHS->getSExtValue(); + if (Addr.getOpcode() == ISD::SUB) + RHSC = -RHSC; + + // Do we need more than 9 bits to encode? + if (!isInt<9>(RHSC)) + return false; + Base = Addr.getOperand(0); + if (Base.getOpcode() == ISD::FrameIndex) { + int FI = cast(Base)->getIndex(); + Base = CurDAG->getTargetFrameIndex( + FI, TLI->getPointerTy(CurDAG->getDataLayout())); + } + Offset = CurDAG->getTargetConstant(RHSC, SDLoc(Addr), MVT::i32); + return true; + } + Base = Addr; + Offset = CurDAG->getTargetConstant(0, SDLoc(Addr), MVT::i32); + return true; +} + +bool ARCDAGToDAGISel::SelectAddrModeFar(SDValue Addr, SDValue &Base, + SDValue &Offset) { + if (SelectAddrModeS9(Addr, Base, Offset)) + return false; + if (Addr.getOpcode() == ARCISD::GAWRAPPER) { + return false; + } + if (ConstantSDNode *RHS = dyn_cast(Addr.getOperand(1))) { + int32_t RHSC = RHS->getSExtValue(); + if (Addr.getOpcode() == ISD::SUB) + RHSC = -RHSC; + Base = Addr.getOperand(0); + Offset = CurDAG->getTargetConstant(RHSC, SDLoc(Addr), MVT::i32); + return true; + } + return false; +} + +// Is this a legal frame index addressing expression. +bool ARCDAGToDAGISel::SelectFrameADDR_ri(SDValue Addr, SDValue &Base, + SDValue &Offset) { + FrameIndexSDNode *FIN = nullptr; + if ((FIN = dyn_cast(Addr))) { + Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), MVT::i32); + Offset = CurDAG->getTargetConstant(0, SDLoc(Addr), MVT::i32); + return true; + } + if (Addr.getOpcode() == ISD::ADD) { + ConstantSDNode *CN = nullptr; + if ((FIN = dyn_cast(Addr.getOperand(0))) && + (CN = dyn_cast(Addr.getOperand(1))) && + (CN->getSExtValue() % 4 == 0 && CN->getSExtValue() >= 0)) { + // Constant positive word offset from frame index + Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), MVT::i32); + Offset = + CurDAG->getTargetConstant(CN->getSExtValue(), SDLoc(Addr), MVT::i32); + return true; + } + } + return false; +} + +void ARCDAGToDAGISel::Select(SDNode *N) { + switch (N->getOpcode()) { + case ISD::Constant: { + uint64_t CVal = cast(N)->getZExtValue(); + ReplaceNode(N, CurDAG->getMachineNode( + isInt<12>(CVal) ? ARC::MOV_rs12 : ARC::MOV_rlimm, + SDLoc(N), MVT::i32, + CurDAG->getTargetConstant(CVal, SDLoc(N), MVT::i32))); + return; + } + } + SelectCode(N); +} diff --git a/lib/Target/ARC/ARCISelLowering.cpp b/lib/Target/ARC/ARCISelLowering.cpp new file mode 100644 index 00000000000..6b921cab950 --- /dev/null +++ b/lib/Target/ARC/ARCISelLowering.cpp @@ -0,0 +1,767 @@ +//===- ARCISelLowering.cpp - ARC DAG Lowering 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 implements the ARCTargetLowering class. +// +//===----------------------------------------------------------------------===// + +#include "ARCISelLowering.h" +#include "ARC.h" +#include "ARCMachineFunctionInfo.h" +#include "ARCSubtarget.h" +#include "ARCTargetMachine.h" +#include "MCTargetDesc/ARCInfo.h" +#include "llvm/CodeGen/CallingConvLower.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineJumpTableInfo.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/SelectionDAGISel.h" +#include "llvm/CodeGen/ValueTypes.h" +#include "llvm/IR/CallingConv.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/Support/Debug.h" +#include + +#define DEBUG_TYPE "arc-lower" + +using namespace llvm; + +static SDValue lowerCallResult(SDValue Chain, SDValue InFlag, + const SmallVectorImpl &RVLocs, + SDLoc dl, SelectionDAG &DAG, + SmallVectorImpl &InVals); + +static ARCCC::CondCode ISDCCtoARCCC(ISD::CondCode isdCC) { + switch (isdCC) { + case ISD::SETUEQ: + return ARCCC::EQ; + case ISD::SETUGT: + return ARCCC::HI; + case ISD::SETUGE: + return ARCCC::HS; + case ISD::SETULT: + return ARCCC::LO; + case ISD::SETULE: + return ARCCC::LS; + case ISD::SETUNE: + return ARCCC::NE; + case ISD::SETEQ: + return ARCCC::EQ; + case ISD::SETGT: + return ARCCC::GT; + case ISD::SETGE: + return ARCCC::GE; + case ISD::SETLT: + return ARCCC::LT; + case ISD::SETLE: + return ARCCC::LE; + case ISD::SETNE: + return ARCCC::NE; + default: + llvm_unreachable("Unhandled ISDCC code."); + } +} + +ARCTargetLowering::ARCTargetLowering(const TargetMachine &TM, + const ARCSubtarget &Subtarget) + : TargetLowering(TM), TM(TM), Subtarget(Subtarget) { + // Set up the register classes. + addRegisterClass(MVT::i32, &ARC::GPR32RegClass); + + // Compute derived properties from the register classes + computeRegisterProperties(Subtarget.getRegisterInfo()); + + setStackPointerRegisterToSaveRestore(ARC::SP); + + setSchedulingPreference(Sched::Source); + + // Use i32 for setcc operations results (slt, sgt, ...). + setBooleanContents(ZeroOrOneBooleanContent); + setBooleanVectorContents(ZeroOrOneBooleanContent); + + for (unsigned Opc = 0; Opc < ISD::BUILTIN_OP_END; ++Opc) + setOperationAction(Opc, MVT::i32, Expand); + + // Operations to get us off of the ground. + // Basic. + setOperationAction(ISD::ADD, MVT::i32, Legal); + setOperationAction(ISD::SUB, MVT::i32, Legal); + setOperationAction(ISD::AND, MVT::i32, Legal); + setOperationAction(ISD::SMAX, MVT::i32, Legal); + setOperationAction(ISD::SMIN, MVT::i32, Legal); + + // Need barrel shifter. + setOperationAction(ISD::SHL, MVT::i32, Legal); + setOperationAction(ISD::SRA, MVT::i32, Legal); + setOperationAction(ISD::SRL, MVT::i32, Legal); + setOperationAction(ISD::ROTR, MVT::i32, Legal); + + setOperationAction(ISD::Constant, MVT::i32, Legal); + setOperationAction(ISD::UNDEF, MVT::i32, Legal); + + // Need multiplier + setOperationAction(ISD::MUL, MVT::i32, Legal); + setOperationAction(ISD::MULHS, MVT::i32, Legal); + setOperationAction(ISD::MULHU, MVT::i32, Legal); + setOperationAction(ISD::LOAD, MVT::i32, Legal); + setOperationAction(ISD::STORE, MVT::i32, Legal); + + setOperationAction(ISD::SELECT_CC, MVT::i32, Custom); + setOperationAction(ISD::BR_CC, MVT::i32, Custom); + setOperationAction(ISD::BRCOND, MVT::Other, Expand); + setOperationAction(ISD::BR_JT, MVT::Other, Expand); + setOperationAction(ISD::JumpTable, MVT::i32, Custom); + + // Have psuedo instruction for frame addresses. + setOperationAction(ISD::FRAMEADDR, MVT::i32, Legal); + // Custom lower global addresses. + setOperationAction(ISD::GlobalAddress, MVT::i32, Custom); + + // Expand var-args ops. + setOperationAction(ISD::VASTART, MVT::Other, Custom); + setOperationAction(ISD::VAEND, MVT::Other, Expand); + setOperationAction(ISD::VAARG, MVT::Other, Expand); + setOperationAction(ISD::VACOPY, MVT::Other, Expand); + + // Other expansions + setOperationAction(ISD::STACKSAVE, MVT::Other, Expand); + setOperationAction(ISD::STACKRESTORE, MVT::Other, Expand); + + // Sign extend inreg + setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i1, Custom); +} + +const char *ARCTargetLowering::getTargetNodeName(unsigned Opcode) const { + switch (Opcode) { + case ARCISD::BL: + return "ARCISD::BL"; + case ARCISD::CMOV: + return "ARCISD::CMOV"; + case ARCISD::CMP: + return "ARCISD::CMP"; + case ARCISD::BRcc: + return "ARCISD::BRcc"; + case ARCISD::RET: + return "ARCISD::RET"; + case ARCISD::GAWRAPPER: + return "ARCISD::GAWRAPPER"; + } + return nullptr; +} + +//===----------------------------------------------------------------------===// +// Misc Lower Operation implementation +//===----------------------------------------------------------------------===// + +SDValue ARCTargetLowering::LowerSELECT_CC(SDValue Op, SelectionDAG &DAG) const { + SDValue LHS = Op.getOperand(0); + SDValue RHS = Op.getOperand(1); + ISD::CondCode CC = cast(Op.getOperand(4))->get(); + SDValue TVal = Op.getOperand(2); + SDValue FVal = Op.getOperand(3); + SDLoc dl(Op); + ARCCC::CondCode ArcCC = ISDCCtoARCCC(CC); + assert(LHS.getValueType() == MVT::i32 && "Only know how to SELECT_CC i32"); + SDValue Cmp = DAG.getNode(ARCISD::CMP, dl, MVT::Glue, LHS, RHS); + return DAG.getNode(ARCISD::CMOV, dl, TVal.getValueType(), TVal, FVal, + DAG.getConstant(ArcCC, dl, MVT::i32), Cmp); +} + +SDValue ARCTargetLowering::LowerSIGN_EXTEND_INREG(SDValue Op, + SelectionDAG &DAG) const { + SDValue Op0 = Op.getOperand(0); + SDLoc dl(Op); + assert(Op.getValueType() == MVT::i32 && + "Unhandled target sign_extend_inreg."); + // These are legal + unsigned Width = cast(Op.getOperand(1))->getVT().getSizeInBits(); + if (Width == 16 || Width == 8) + return Op; + if (Width >= 32) { + return {}; + } + SDValue LS = DAG.getNode(ISD::SHL, dl, MVT::i32, Op0, + DAG.getConstant(32 - Width, dl, MVT::i32)); + SDValue SR = DAG.getNode(ISD::SRA, dl, MVT::i32, LS, + DAG.getConstant(32 - Width, dl, MVT::i32)); + return SR; +} + +SDValue ARCTargetLowering::LowerBR_CC(SDValue Op, SelectionDAG &DAG) const { + SDValue Chain = Op.getOperand(0); + ISD::CondCode CC = cast(Op.getOperand(1))->get(); + SDValue LHS = Op.getOperand(2); + SDValue RHS = Op.getOperand(3); + SDValue Dest = Op.getOperand(4); + SDLoc dl(Op); + ARCCC::CondCode arcCC = ISDCCtoARCCC(CC); + assert(LHS.getValueType() == MVT::i32 && "Only know how to BR_CC i32"); + return DAG.getNode(ARCISD::BRcc, dl, MVT::Other, Chain, Dest, LHS, RHS, + DAG.getConstant(arcCC, dl, MVT::i32)); +} + +SDValue ARCTargetLowering::LowerJumpTable(SDValue Op, SelectionDAG &DAG) const { + auto *N = cast(Op); + SDValue GA = DAG.getTargetJumpTable(N->getIndex(), MVT::i32); + return DAG.getNode(ARCISD::GAWRAPPER, SDLoc(N), MVT::i32, GA); +} + +#include "ARCGenCallingConv.inc" + +//===----------------------------------------------------------------------===// +// Call Calling Convention Implementation +//===----------------------------------------------------------------------===// + +/// ARC call implementation +SDValue ARCTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, + SmallVectorImpl &InVals) const { + SelectionDAG &DAG = CLI.DAG; + SDLoc &dl = CLI.DL; + SmallVectorImpl &Outs = CLI.Outs; + SmallVectorImpl &OutVals = CLI.OutVals; + SmallVectorImpl &Ins = CLI.Ins; + SDValue Chain = CLI.Chain; + SDValue Callee = CLI.Callee; + CallingConv::ID CallConv = CLI.CallConv; + bool IsVarArg = CLI.IsVarArg; + bool &IsTailCall = CLI.IsTailCall; + + IsTailCall = false; // Do not support tail calls yet. + + SmallVector ArgLocs; + CCState CCInfo(CallConv, IsVarArg, DAG.getMachineFunction(), ArgLocs, + *DAG.getContext()); + + CCInfo.AnalyzeCallOperands(Outs, CC_ARC); + + SmallVector RVLocs; + // Analyze return values to determine the number of bytes of stack required. + CCState RetCCInfo(CallConv, IsVarArg, DAG.getMachineFunction(), RVLocs, + *DAG.getContext()); + RetCCInfo.AllocateStack(CCInfo.getNextStackOffset(), 4); + RetCCInfo.AnalyzeCallResult(Ins, RetCC_ARC); + + // Get a count of how many bytes are to be pushed on the stack. + unsigned NumBytes = RetCCInfo.getNextStackOffset(); + auto PtrVT = getPointerTy(DAG.getDataLayout()); + + Chain = DAG.getCALLSEQ_START(Chain, NumBytes, 0, dl); + + SmallVector, 4> RegsToPass; + SmallVector MemOpChains; + + SDValue StackPtr; + // Walk the register/memloc assignments, inserting copies/loads. + for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) { + CCValAssign &VA = ArgLocs[i]; + SDValue Arg = OutVals[i]; + + // Promote the value if needed. + switch (VA.getLocInfo()) { + default: + llvm_unreachable("Unknown loc info!"); + case CCValAssign::Full: + break; + case CCValAssign::SExt: + Arg = DAG.getNode(ISD::SIGN_EXTEND, dl, VA.getLocVT(), Arg); + break; + case CCValAssign::ZExt: + Arg = DAG.getNode(ISD::ZERO_EXTEND, dl, VA.getLocVT(), Arg); + break; + case CCValAssign::AExt: + Arg = DAG.getNode(ISD::ANY_EXTEND, dl, VA.getLocVT(), Arg); + break; + } + + // Arguments that can be passed on register must be kept at + // RegsToPass vector + if (VA.isRegLoc()) { + RegsToPass.push_back(std::make_pair(VA.getLocReg(), Arg)); + } else { + assert(VA.isMemLoc() && "Must be register or memory argument."); + if (!StackPtr.getNode()) + StackPtr = DAG.getCopyFromReg(Chain, dl, ARC::SP, + getPointerTy(DAG.getDataLayout())); + // Calculate the stack position. + SDValue SOffset = DAG.getIntPtrConstant(VA.getLocMemOffset(), dl); + SDValue PtrOff = DAG.getNode( + ISD::ADD, dl, getPointerTy(DAG.getDataLayout()), StackPtr, SOffset); + + SDValue Store = + DAG.getStore(Chain, dl, Arg, PtrOff, MachinePointerInfo()); + MemOpChains.push_back(Store); + IsTailCall = false; + } + } + + // Transform all store nodes into one single node because + // all store nodes are independent of each other. + if (!MemOpChains.empty()) + Chain = DAG.getNode(ISD::TokenFactor, dl, MVT::Other, MemOpChains); + + // Build a sequence of copy-to-reg nodes chained together with token + // chain and flag operands which copy the outgoing args into registers. + // The InFlag in necessary since all emitted instructions must be + // stuck together. + SDValue Glue; + for (unsigned i = 0, e = RegsToPass.size(); i != e; ++i) { + Chain = DAG.getCopyToReg(Chain, dl, RegsToPass[i].first, + RegsToPass[i].second, Glue); + Glue = Chain.getValue(1); + } + + // If the callee is a GlobalAddress node (quite common, every direct call is) + // turn it into a TargetGlobalAddress node so that legalize doesn't hack it. + // Likewise ExternalSymbol -> TargetExternalSymbol. + bool IsDirect = true; + if (auto *G = dyn_cast(Callee)) + Callee = DAG.getTargetGlobalAddress(G->getGlobal(), dl, MVT::i32); + else if (auto *E = dyn_cast(Callee)) + Callee = DAG.getTargetExternalSymbol(E->getSymbol(), MVT::i32); + else + IsDirect = false; + // Branch + Link = #chain, #target_address, #opt_in_flags... + // = Chain, Callee, Reg#1, Reg#2, ... + // + // Returns a chain & a flag for retval copy to use. + SDVTList NodeTys = DAG.getVTList(MVT::Other, MVT::Glue); + SmallVector 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. + const TargetRegisterInfo *TRI = Subtarget.getRegisterInfo(); + const uint32_t *Mask = + TRI->getCallPreservedMask(DAG.getMachineFunction(), CallConv); + assert(Mask && "Missing call preserved mask for calling convention"); + Ops.push_back(DAG.getRegisterMask(Mask)); + + if (Glue.getNode()) + Ops.push_back(Glue); + + Chain = DAG.getNode(IsDirect ? ARCISD::BL : ARCISD::JL, dl, NodeTys, Ops); + Glue = Chain.getValue(1); + + // Create the CALLSEQ_END node. + Chain = DAG.getCALLSEQ_END(Chain, DAG.getConstant(NumBytes, dl, PtrVT, true), + DAG.getConstant(0, dl, PtrVT, true), Glue, dl); + Glue = Chain.getValue(1); + + // Handle result values, copying them out of physregs into vregs that we + // return. + if (IsTailCall) + return Chain; + return lowerCallResult(Chain, Glue, RVLocs, dl, DAG, InVals); +} + +/// Lower the result values of a call into the appropriate copies out of +/// physical registers / memory locations. +static SDValue lowerCallResult(SDValue Chain, SDValue Glue, + const SmallVectorImpl &RVLocs, + SDLoc dl, SelectionDAG &DAG, + SmallVectorImpl &InVals) { + SmallVector, 4> ResultMemLocs; + // Copy results out of physical registers. + for (unsigned i = 0, e = RVLocs.size(); i != e; ++i) { + const CCValAssign &VA = RVLocs[i]; + if (VA.isRegLoc()) { + SDValue RetValue; + RetValue = + DAG.getCopyFromReg(Chain, dl, VA.getLocReg(), VA.getValVT(), Glue); + Chain = RetValue.getValue(1); + Glue = RetValue.getValue(2); + InVals.push_back(RetValue); + } else { + assert(VA.isMemLoc() && "Must be memory location."); + ResultMemLocs.push_back( + std::make_pair(VA.getLocMemOffset(), InVals.size())); + + // Reserve space for this result. + InVals.push_back(SDValue()); + } + } + + // Copy results out of memory. + SmallVector MemOpChains; + for (unsigned i = 0, e = ResultMemLocs.size(); i != e; ++i) { + int Offset = ResultMemLocs[i].first; + unsigned Index = ResultMemLocs[i].second; + SDValue StackPtr = DAG.getRegister(ARC::SP, MVT::i32); + SDValue SpLoc = DAG.getNode(ISD::ADD, dl, MVT::i32, StackPtr, + DAG.getConstant(Offset, dl, MVT::i32)); + SDValue Load = + DAG.getLoad(MVT::i32, dl, Chain, SpLoc, MachinePointerInfo()); + InVals[Index] = Load; + MemOpChains.push_back(Load.getValue(1)); + } + + // Transform all loads nodes into one single node because + // all load nodes are independent of each other. + if (!MemOpChains.empty()) + Chain = DAG.getNode(ISD::TokenFactor, dl, MVT::Other, MemOpChains); + + return Chain; +} + +//===----------------------------------------------------------------------===// +// Formal Arguments Calling Convention Implementation +//===----------------------------------------------------------------------===// + +namespace { + +struct ArgDataPair { + SDValue SDV; + ISD::ArgFlagsTy Flags; +}; + +} // end anonymous namespace + +/// ARC formal arguments implementation +SDValue ARCTargetLowering::LowerFormalArguments( + SDValue Chain, CallingConv::ID CallConv, bool IsVarArg, + const SmallVectorImpl &Ins, const SDLoc &dl, + SelectionDAG &DAG, SmallVectorImpl &InVals) const { + switch (CallConv) { + default: + llvm_unreachable("Unsupported calling convention"); + case CallingConv::C: + case CallingConv::Fast: + return LowerCallArguments(Chain, CallConv, IsVarArg, Ins, dl, DAG, InVals); + } +} + +/// Transform physical registers into virtual registers, and generate load +/// operations for argument places on the stack. +SDValue ARCTargetLowering::LowerCallArguments( + SDValue Chain, CallingConv::ID CallConv, bool IsVarArg, + const SmallVectorImpl &Ins, SDLoc dl, SelectionDAG &DAG, + SmallVectorImpl &InVals) const { + MachineFunction &MF = DAG.getMachineFunction(); + MachineFrameInfo &MFI = MF.getFrameInfo(); + MachineRegisterInfo &RegInfo = MF.getRegInfo(); + auto *AFI = MF.getInfo(); + + // Assign locations to all of the incoming arguments. + SmallVector ArgLocs; + CCState CCInfo(CallConv, IsVarArg, DAG.getMachineFunction(), ArgLocs, + *DAG.getContext()); + + CCInfo.AnalyzeFormalArguments(Ins, CC_ARC); + + unsigned StackSlotSize = 4; + + if (!IsVarArg) + AFI->setReturnStackOffset(CCInfo.getNextStackOffset()); + + // All getCopyFromReg ops must precede any getMemcpys to prevent the + // scheduler clobbering a register before it has been copied. + // The stages are: + // 1. CopyFromReg (and load) arg & vararg registers. + // 2. Chain CopyFromReg nodes into a TokenFactor. + // 3. Memcpy 'byVal' args & push final InVals. + // 4. Chain mem ops nodes into a TokenFactor. + SmallVector CFRegNode; + SmallVector ArgData; + SmallVector MemOps; + + // 1a. CopyFromReg (and load) arg registers. + for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) { + CCValAssign &VA = ArgLocs[i]; + SDValue ArgIn; + + if (VA.isRegLoc()) { + // Arguments passed in registers + EVT RegVT = VA.getLocVT(); + switch (RegVT.getSimpleVT().SimpleTy) { + default: { + DEBUG(errs() << "LowerFormalArguments Unhandled argument type: " + << RegVT.getSimpleVT().SimpleTy << "\n"); + llvm_unreachable("Unhandled LowerFormalArguments type."); + } + case MVT::i32: + unsigned VReg = RegInfo.createVirtualRegister(&ARC::GPR32RegClass); + RegInfo.addLiveIn(VA.getLocReg(), VReg); + ArgIn = DAG.getCopyFromReg(Chain, dl, VReg, RegVT); + CFRegNode.push_back(ArgIn.getValue(ArgIn->getNumValues() - 1)); + } + } else { + // sanity check + assert(VA.isMemLoc()); + // Load the argument to a virtual register + unsigned ObjSize = VA.getLocVT().getStoreSize(); + assert((ObjSize <= StackSlotSize) && "Unhandled argument"); + + // Create the frame index object for this incoming parameter... + int FI = MFI.CreateFixedObject(ObjSize, VA.getLocMemOffset(), true); + + // Create the SelectionDAG nodes corresponding to a load + // from this parameter + SDValue FIN = DAG.getFrameIndex(FI, MVT::i32); + ArgIn = DAG.getLoad(VA.getLocVT(), dl, Chain, FIN, + MachinePointerInfo::getFixedStack(MF, FI)); + } + const ArgDataPair ADP = {ArgIn, Ins[i].Flags}; + ArgData.push_back(ADP); + } + + // 1b. CopyFromReg vararg registers. + if (IsVarArg) { + // Argument registers + static const MCPhysReg ArgRegs[] = {ARC::R0, ARC::R1, ARC::R2, ARC::R3, + ARC::R4, ARC::R5, ARC::R6, ARC::R7}; + auto *AFI = MF.getInfo(); + unsigned FirstVAReg = CCInfo.getFirstUnallocated(ArgRegs); + if (FirstVAReg < array_lengthof(ArgRegs)) { + int Offset = 0; + // Save remaining registers, storing higher register numbers at a higher + // address + // There are (array_lengthof(ArgRegs) - FirstVAReg) registers which + // need to be saved. + int VarFI = + MFI.CreateFixedObject((array_lengthof(ArgRegs) - FirstVAReg) * 4, + CCInfo.getNextStackOffset(), true); + AFI->setVarArgsFrameIndex(VarFI); + SDValue FIN = DAG.getFrameIndex(VarFI, MVT::i32); + for (unsigned i = FirstVAReg; i < array_lengthof(ArgRegs); i++) { + // Move argument from phys reg -> virt reg + unsigned VReg = RegInfo.createVirtualRegister(&ARC::GPR32RegClass); + RegInfo.addLiveIn(ArgRegs[i], VReg); + SDValue Val = DAG.getCopyFromReg(Chain, dl, VReg, MVT::i32); + CFRegNode.push_back(Val.getValue(Val->getNumValues() - 1)); + SDValue VAObj = DAG.getNode(ISD::ADD, dl, MVT::i32, FIN, + DAG.getConstant(Offset, dl, MVT::i32)); + // Move argument from virt reg -> stack + SDValue Store = + DAG.getStore(Val.getValue(1), dl, Val, VAObj, MachinePointerInfo()); + MemOps.push_back(Store); + Offset += 4; + } + } else { + llvm_unreachable("Too many var args parameters."); + } + } + + // 2. Chain CopyFromReg nodes into a TokenFactor. + if (!CFRegNode.empty()) + Chain = DAG.getNode(ISD::TokenFactor, dl, MVT::Other, CFRegNode); + + // 3. Memcpy 'byVal' args & push final InVals. + // Aggregates passed "byVal" need to be copied by the callee. + // The callee will use a pointer to this copy, rather than the original + // pointer. + for (const auto &ArgDI : ArgData) { + if (ArgDI.Flags.isByVal() && ArgDI.Flags.getByValSize()) { + unsigned Size = ArgDI.Flags.getByValSize(); + unsigned Align = std::max(StackSlotSize, ArgDI.Flags.getByValAlign()); + // Create a new object on the stack and copy the pointee into it. + int FI = MFI.CreateStackObject(Size, Align, false); + SDValue FIN = DAG.getFrameIndex(FI, MVT::i32); + InVals.push_back(FIN); + MemOps.push_back(DAG.getMemcpy( + Chain, dl, FIN, ArgDI.SDV, DAG.getConstant(Size, dl, MVT::i32), Align, + false, false, false, MachinePointerInfo(), MachinePointerInfo())); + } else { + InVals.push_back(ArgDI.SDV); + } + } + + // 4. Chain mem ops nodes into a TokenFactor. + if (!MemOps.empty()) { + MemOps.push_back(Chain); + Chain = DAG.getNode(ISD::TokenFactor, dl, MVT::Other, MemOps); + } + + return Chain; +} + +//===----------------------------------------------------------------------===// +// Return Value Calling Convention Implementation +//===----------------------------------------------------------------------===// + +bool ARCTargetLowering::CanLowerReturn( + CallingConv::ID CallConv, MachineFunction &MF, bool IsVarArg, + const SmallVectorImpl &Outs, LLVMContext &Context) const { + SmallVector RVLocs; + CCState CCInfo(CallConv, IsVarArg, MF, RVLocs, Context); + if (!CCInfo.CheckReturn(Outs, RetCC_ARC)) + return false; + if (CCInfo.getNextStackOffset() != 0 && IsVarArg) + return false; + return true; +} + +SDValue +ARCTargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv, + bool IsVarArg, + const SmallVectorImpl &Outs, + const SmallVectorImpl &OutVals, + const SDLoc &dl, SelectionDAG &DAG) const { + auto *AFI = DAG.getMachineFunction().getInfo(); + MachineFrameInfo &MFI = DAG.getMachineFunction().getFrameInfo(); + + // CCValAssign - represent the assignment of + // the return value to a location + SmallVector RVLocs; + + // CCState - Info about the registers and stack slot. + CCState CCInfo(CallConv, IsVarArg, DAG.getMachineFunction(), RVLocs, + *DAG.getContext()); + + // Analyze return values. + if (!IsVarArg) + CCInfo.AllocateStack(AFI->getReturnStackOffset(), 4); + + CCInfo.AnalyzeReturn(Outs, RetCC_ARC); + + SDValue Flag; + SmallVector RetOps(1, Chain); + SmallVector MemOpChains; + // Handle return values that must be copied to memory. + for (unsigned i = 0, e = RVLocs.size(); i != e; ++i) { + CCValAssign &VA = RVLocs[i]; + if (VA.isRegLoc()) + continue; + assert(VA.isMemLoc()); + if (IsVarArg) { + report_fatal_error("Can't return value from vararg function in memory"); + } + + int Offset = VA.getLocMemOffset(); + unsigned ObjSize = VA.getLocVT().getStoreSize(); + // Create the frame index object for the memory location. + int FI = MFI.CreateFixedObject(ObjSize, Offset, false); + + // Create a SelectionDAG node corresponding to a store + // to this memory location. + SDValue FIN = DAG.getFrameIndex(FI, MVT::i32); + MemOpChains.push_back(DAG.getStore( + Chain, dl, OutVals[i], FIN, + MachinePointerInfo::getFixedStack(DAG.getMachineFunction(), FI))); + } + + // Transform all store nodes into one single node because + // all stores are independent of each other. + if (!MemOpChains.empty()) + Chain = DAG.getNode(ISD::TokenFactor, dl, MVT::Other, MemOpChains); + + // Now handle return values copied to registers. + for (unsigned i = 0, e = RVLocs.size(); i != e; ++i) { + CCValAssign &VA = RVLocs[i]; + if (!VA.isRegLoc()) + continue; + // Copy the result values into the output registers. + Chain = DAG.getCopyToReg(Chain, dl, VA.getLocReg(), OutVals[i], Flag); + + // guarantee that all emitted copies are + // stuck together, avoiding something bad + 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); + + // What to do with the RetOps? + return DAG.getNode(ARCISD::RET, dl, MVT::Other, RetOps); +} + +//===----------------------------------------------------------------------===// +// Target Optimization Hooks +//===----------------------------------------------------------------------===// + +SDValue ARCTargetLowering::PerformDAGCombine(SDNode *N, + DAGCombinerInfo &DCI) const { + return {}; +} + +//===----------------------------------------------------------------------===// +// Addressing mode description hooks +//===----------------------------------------------------------------------===// + +/// Return true if the addressing mode represented by AM is legal for this +/// target, for a load/store of the specified type. +bool ARCTargetLowering::isLegalAddressingMode(const DataLayout &DL, + const AddrMode &AM, Type *Ty, + unsigned AS, + Instruction *I) const { + return AM.Scale == 0; +} + +// Don't emit tail calls for the time being. +bool ARCTargetLowering::mayBeEmittedAsTailCall(const CallInst *CI) const { + return false; +} + +SDValue ARCTargetLowering::LowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const { + const ARCRegisterInfo &ARI = *Subtarget.getRegisterInfo(); + MachineFunction &MF = DAG.getMachineFunction(); + MachineFrameInfo &MFI = MF.getFrameInfo(); + MFI.setFrameAddressIsTaken(true); + + EVT VT = Op.getValueType(); + SDLoc dl(Op); + assert(cast(Op.getOperand(0))->getZExtValue() == 0 && + "Only support lowering frame addr of current frame."); + unsigned FrameReg = ARI.getFrameRegister(MF); + return DAG.getCopyFromReg(DAG.getEntryNode(), dl, FrameReg, VT); +} + +SDValue ARCTargetLowering::LowerGlobalAddress(SDValue Op, + SelectionDAG &DAG) const { + const GlobalAddressSDNode *GN = cast(Op); + const GlobalValue *GV = GN->getGlobal(); + SDLoc dl(GN); + int64_t Offset = GN->getOffset(); + SDValue GA = DAG.getTargetGlobalAddress(GV, dl, MVT::i32, Offset); + return DAG.getNode(ARCISD::GAWRAPPER, dl, MVT::i32, GA); +} + +static SDValue LowerVASTART(SDValue Op, SelectionDAG &DAG) { + MachineFunction &MF = DAG.getMachineFunction(); + auto *FuncInfo = MF.getInfo(); + + // vastart just stores the address of the VarArgsFrameIndex slot into the + // memory location argument. + SDLoc dl(Op); + EVT PtrVT = DAG.getTargetLoweringInfo().getPointerTy(DAG.getDataLayout()); + SDValue FR = DAG.getFrameIndex(FuncInfo->getVarArgsFrameIndex(), PtrVT); + const Value *SV = cast(Op.getOperand(2))->getValue(); + return DAG.getStore(Op.getOperand(0), dl, FR, Op.getOperand(1), + MachinePointerInfo(SV)); +} + +SDValue ARCTargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const { + switch (Op.getOpcode()) { + case ISD::GlobalAddress: + return LowerGlobalAddress(Op, DAG); + case ISD::FRAMEADDR: + return LowerFRAMEADDR(Op, DAG); + case ISD::SELECT_CC: + return LowerSELECT_CC(Op, DAG); + case ISD::BR_CC: + return LowerBR_CC(Op, DAG); + case ISD::SIGN_EXTEND_INREG: + return LowerSIGN_EXTEND_INREG(Op, DAG); + case ISD::JumpTable: + return LowerJumpTable(Op, DAG); + case ISD::VASTART: + return LowerVASTART(Op, DAG); + default: + llvm_unreachable("unimplemented operand"); + } +} diff --git a/lib/Target/ARC/ARCISelLowering.h b/lib/Target/ARC/ARCISelLowering.h new file mode 100644 index 00000000000..bb9766693f2 --- /dev/null +++ b/lib/Target/ARC/ARCISelLowering.h @@ -0,0 +1,121 @@ +//===- ARCISelLowering.h - ARC 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 ARC uses to lower LLVM code into a +// selection DAG. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_ARC_ARCISELLOWERING_H +#define LLVM_LIB_TARGET_ARC_ARCISELLOWERING_H + +#include "ARC.h" +#include "llvm/CodeGen/SelectionDAG.h" +#include "llvm/Target/TargetLowering.h" + +namespace llvm { + +// Forward delcarations +class ARCSubtarget; +class ARCTargetMachine; + +namespace ARCISD { + +enum NodeType : unsigned { + // Start the numbering where the builtin ops and target ops leave off. + FIRST_NUMBER = ISD::BUILTIN_OP_END, + + // Branch and link (call) + BL, + + // Jump and link (indirect call) + JL, + + // CMP + CMP, + + // CMOV + CMOV, + + // BRcc + BRcc, + + // Global Address Wrapper + GAWRAPPER, + + // return, (j_s [blink]) + RET +}; + +} // end namespace ARCISD + +//===--------------------------------------------------------------------===// +// TargetLowering Implementation +//===--------------------------------------------------------------------===// +class ARCTargetLowering : public TargetLowering { +public: + explicit ARCTargetLowering(const TargetMachine &TM, + const ARCSubtarget &Subtarget); + + /// Provide custom lowering hooks for some operations. + SDValue LowerOperation(SDValue Op, SelectionDAG &DAG) const override; + + /// This method returns the name of a target specific DAG node. + const char *getTargetNodeName(unsigned Opcode) const override; + + /// Return true if the addressing mode represented by AM is legal for this + /// target, for a load/store of the specified type. + bool isLegalAddressingMode(const DataLayout &DL, const AddrMode &AM, Type *Ty, + unsigned AS, + Instruction *I = nullptr) const override; + +private: + const TargetMachine &TM; + const ARCSubtarget &Subtarget; + + // Lower Operand helpers + SDValue LowerCallArguments(SDValue Chain, CallingConv::ID CallConv, + bool isVarArg, + const SmallVectorImpl &Ins, + SDLoc dl, SelectionDAG &DAG, + SmallVectorImpl &InVals) const; + // Lower Operand specifics + SDValue LowerJumpTable(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerSELECT_CC(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerBR_CC(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerSIGN_EXTEND_INREG(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const; + SDValue PerformDAGCombine(SDNode *N, DAGCombinerInfo &DCI) const override; + + SDValue LowerFormalArguments(SDValue Chain, CallingConv::ID CallConv, + bool isVarArg, + const SmallVectorImpl &Ins, + const SDLoc &dl, SelectionDAG &DAG, + SmallVectorImpl &InVals) const override; + + SDValue LowerCall(TargetLowering::CallLoweringInfo &CLI, + SmallVectorImpl &InVals) const override; + + SDValue LowerReturn(SDValue Chain, CallingConv::ID CallConv, bool isVarArg, + const SmallVectorImpl &Outs, + const SmallVectorImpl &OutVals, const SDLoc &dl, + SelectionDAG &DAG) const override; + + bool CanLowerReturn(CallingConv::ID CallConv, MachineFunction &MF, + bool isVarArg, + const SmallVectorImpl &ArgsFlags, + LLVMContext &Context) const override; + + bool mayBeEmittedAsTailCall(const CallInst *CI) const override; +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_ARC_ARCISELLOWERING_H diff --git a/lib/Target/ARC/ARCInstrFormats.td b/lib/Target/ARC/ARCInstrFormats.td new file mode 100644 index 00000000000..94240e90a60 --- /dev/null +++ b/lib/Target/ARC/ARCInstrFormats.td @@ -0,0 +1,508 @@ +//===- ARCInstrFormats.td - ARC Instruction Formats --------*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Instruction format superclass +//===----------------------------------------------------------------------===// + +class Encoding64 { + field bits<64> Inst; + field bits<64> SoftFail = 0; +} + +// Address operands +def immU6 : Operand, PatLeaf<(imm), [{ + return isUInt<6>(N->getSExtValue()); }]> { +} + +def immS12 : Operand, PatLeaf<(imm), [{ + return isInt<12>(N->getSExtValue()); }]> { + let DecoderMethod = "DecodeS12Operand"; +} + +def immS9 : Operand, PatLeaf<(imm), [{ + return isInt<9>(N->getSExtValue()); }]> { + let DecoderMethod = "DecodeS9Operand"; +} + +def MEMii : Operand { + let MIOperandInfo = (ops i32imm, i32imm); +} + +def MEMrs9 : Operand { + let MIOperandInfo = (ops GPR32:$B, immS9:$S9); + let PrintMethod = "printMemOperandRI"; + let DecoderMethod = "DecodeMEMrs9"; +} + +def MEMrlimm : Operand { + let MIOperandInfo = (ops GPR32:$B, i32imm:$LImm); + let PrintMethod = "printMemOperandRI"; + let DecoderMethod = "DecodeMEMrlimm"; +} + +class InstARC pattern> + : Instruction, Encoding64 { + + let Namespace = "ARC"; + dag OutOperandList = outs; + dag InOperandList = ins; + let AsmString = asmstr; + let Pattern = pattern; + let Size = sz; +} + +// ARC pseudo instructions format +class PseudoInstARC pattern> + : InstARC<0, outs, ins, asmstr, pattern> { + let isPseudo = 1; +} + +//===----------------------------------------------------------------------===// +// Instruction formats +//===----------------------------------------------------------------------===// + +// All 32-bit ARC instructions have a 5-bit "major" opcode class designator +// in bits 27-31. +// +// Some general naming conventions: +// N - Delay Slot bit. ARC v2 branch instructions have an optional delay slot +// which is encoded with this bit. When set, a delay slot exists. +// cc - Condition code. +// SX - Signed X-bit immediate. +// UX - Unsigned X-bit immediate. +// +// [ABC] - 32-bit register operand. These are 6-bit fields. This encodes the +// standard 32 general purpose registers, and allows use of additional +// (extension) registers. This also encodes an instruction that uses +// a 32-bit Long Immediate (LImm), using 0x3e==62 as the field value. +// This makes 32-bit format instructions with Long Immediates +// 64-bit instructions, with the Long Immediate in bits 32-63. +// A - Inst[5-0] = A[5-0], when the format has A. A is always a register. +// B - Inst[14-12] = B[5-3], Inst[26-24] = B[2-0], when the format has B. +// B is always a register. +// C - Inst[11-6] = C[5-0], when the format has C. C can either be a register, +// or a 6-bit unsigned immediate (immU6), depending on the format. +// F - Many instructions specify a flag bit. When set, the result of these +// instructions will set the ZNCV flags of the STATUS32 register +// (Zero/Negative/Carry/oVerflow). + +// Branch Instructions. +class F32_BR major, dag outs, dag ins, bit b16, string asmstr, + list pattern> : + InstARC<4, outs, ins, asmstr, pattern> { + bit N; + + let Inst{31-27} = major; + let Inst{16} = b16; + let Inst{5} = N; +} + +class F32_BR_COND major, dag outs, dag ins, bit b16, string asmstr, + list pattern> : + F32_BR { + bits<21> S21; // 2-byte aligned 21-bit byte-offset. + bits<5> cc; + let Inst{26-18} = S21{10-2}; + let Inst{15-6} = S21{20-11}; + let Inst{4-0} = cc; +} + +class F32_BR_UCOND_FAR major, dag outs, dag ins, bit b16, string asmstr, + list pattern> : + F32_BR { + bits<25> S25; // 2-byte aligned 25-bit byte-offset. + let Inst{26-18} = S25{10-2}; + let Inst{15-6} = S25{20-11}; + let Inst{4} = 0; + let Inst{3-0} = S25{24-21}; +} + +class F32_BR0_COND pat> : + F32_BR_COND<0b00000, outs, ins, 0, asmstr, pat> { + let Inst{17} = S21{1}; +} + +// Branch targets are 2-byte aligned, so S25[0] is implied 0. +// |26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10|9|8|7|6|5|4|3|2|1|0 | +// |S25[10-1] | 1|S25[20-11] |N|0|S25[24-21]| +class F32_BR0_UCOND_FAR pat> : + F32_BR_UCOND_FAR<0b00000, outs, ins, 1, asmstr, pat> { + let Inst{17} = S25{1}; +} + +// BL targets (functions) are 4-byte aligned, so S25[1-0] = 0b00 +// |26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10|9|8|7|6|5|4|3|2|1|0 | +// |S25[10-2] | 1| 0|S25[20-11] |N|0|S25[24-21]| +class F32_BR1_BL_UCOND_FAR pat> : + F32_BR_UCOND_FAR<0b00001, outs, ins, 0, asmstr, pat> { + let Inst{17} = 1; +} + +// BLcc targets have 21 bit range, and are 4-byte aligned. +// |26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10|9|8|7|6|5|4|3|2|1|0| +// |S25[10-2] | 0| 0|S25[20-11] |N|0|cc | +class F32_BR1_BL_COND pat> : + F32_BR_COND<0b00001, outs, ins, 0, asmstr, pat> { + let Inst{17} = 0; +} + + +// BRcc targets have limited 9-bit range. These are for compare and branch +// in single instruction. Their targets are 2-byte aligned. They also use +// a different (3-bit) set of condition codes. +// |26|25|24|23|22|21|20|19|18|17|16|15 |14|13|12|11|10|9|8|7|6|5|4|3|2|1|0| +// |B[2-0] |S9[7-1] | 1|S9[8]|B[5-3] |C |N|u|0|cc | +class F32_BR1_BCC pattern> : + InstARC<4, outs, ins, asmstr, pattern> { + + bits<3> cc; + bits<6> B; + bits<6> C; + bit N; + bits<9> S9; // 2-byte aligned 9-bit byte-offset. + + let Inst{31-27} = 0b00001; + let Inst{26-24} = B{2-0}; + let Inst{23-17} = S9{7-1}; + let Inst{16} = 1; + let Inst{15} = S9{8}; + let Inst{14-12} = B{5-3}; + let Inst{11-6} = C; + let Inst{5} = N; + let Inst{4} = IsU6; + let Inst{3} = 0; + let Inst{2-0} = cc; +} + +// General operations instructions. +// Single Operand Instructions. Inst[5-0] specifies the specific operation +// for this format. +// |26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10|9|8|7|6|5|4|3|2|1|0| +// |B[2-0] | 0| 0| 1| 0| 1| 1| 1| 1| F|B[5-3] |C |subop | +class F32_SOP_RR major, bits<6> subop, bit F, dag outs, dag ins, + string asmstr, list pattern> : + InstARC<4, outs, ins, asmstr, pattern> { + + bits<6> C; + bits<6> B; + + let Inst{31-27} = major; + let Inst{26-24} = B{2-0}; + let Inst{23-22} = 0b00; + let Inst{21-16} = 0b101111; + let Inst{15} = F; + let Inst{14-12} = B{5-3}; + let Inst{11-6} = C; + let Inst{5-0} = subop; +} + +// Dual Operand Instructions. Inst[21-16] specifies the specific operation +// for this format. + +// 3-register Dual Operand instruction. +// |26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10|9|8|7|6|5|4|3|2|1|0| +// |B[2-0] | 0| 0| subop| F|B[5-3] |C |A | +class F32_DOP_RR major, bits<6> subop, bit F, dag outs, dag ins, + string asmstr, list pattern> : + InstARC<4, outs, ins, asmstr, pattern> { + bits<6> C; + bits<6> B; + bits<6> A; + + let Inst{31-27} = major; + let Inst{26-24} = B{2-0}; + let Inst{23-22} = 0b00; + let Inst{21-16} = subop; + let Inst{15} = F; + let Inst{14-12} = B{5-3}; + let Inst{11-6} = C; + let Inst{5-0} = A; +} + +// Conditional Dual Operand instruction. This instruction uses B as the +// first 2 operands (i.e, add.cc B, B, C). +// |26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10|9|8|7|6|5|4|3|2|1|0| +// |B[2-0] | 1| 1| subop| F|B[5-3] |C |A | +class F32_DOP_CC_RR major, bits<6> subop, bit F, dag outs, dag ins, + string asmstr, list pattern> : + InstARC<4, outs, ins, asmstr, pattern> { + bits<5> cc; + bits<6> C; + bits<6> B; + + let Inst{31-27} = major; + let Inst{26-24} = B{2-0}; + let Inst{23-22} = 0b11; + let Inst{21-16} = subop; + let Inst{15} = F; + let Inst{14-12} = B{5-3}; + let Inst{11-6} = C; + let Inst{5} = 0; + let Inst{4-0} = cc; +} + + +// 2-register, unsigned 6-bit immediate Dual Operand instruction. +// |26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10|9|8|7|6|5|4|3|2|1|0| +// |B[2-0] | 0| 1| subop| F|B[5-3] |U6 |A | +class F32_DOP_RU6 major, bits<6> subop, bit F, dag outs, dag ins, + string asmstr, list pattern> : + InstARC<4, outs, ins, asmstr, pattern> { + bits<6> U6; + bits<6> B; + bits<6> A; + + let Inst{31-27} = major; + let Inst{26-24} = B{2-0}; + let Inst{23-22} = 0b01; + let Inst{21-16} = subop; + let Inst{15} = F; + let Inst{14-12} = B{5-3}; + let Inst{11-6} = U6; + let Inst{5-0} = A; +} + +// 2-register, signed 12-bit immediate Dual Operand instruction. +// This instruction uses B as the first 2 operands (i.e., add B, B, -128). +// |26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10|9|8|7|6|5|4|3|2|1|0| +// |B[2-0] | 1| 0| subop| F|B[5-3] |S12[5-0] |S12[11-6] | +class F32_DOP_RS12 major, bits<6> subop, bit F, dag outs, dag ins, + string asmstr, list pattern> : + InstARC<4, outs, ins, asmstr, pattern> { + bits<6> B; + bits<12> S12; + + let Inst{31-27} = major; + let Inst{26-24} = B{2-0}; + let Inst{23-22} = 0b10; + let Inst{21-16} = subop; + let Inst{15} = F; + let Inst{14-12} = B{5-3}; + let Inst{11-6} = S12{5-0}; + let Inst{5-0} = S12{11-6}; +} + +// 2-register, 32-bit immediate (LImm) Dual Operand instruction. +// This instruction has the 32-bit immediate in bits 32-63, and +// 62 in the C register operand slot, but is otherwise F32_DOP_RR. +class F32_DOP_RLIMM major, bits<6> subop, bit F, dag outs, dag ins, + string asmstr, list pattern> : + InstARC<8, outs, ins, asmstr, pattern> { + bits<6> B; + bits<6> A; + bits<32> LImm; + + let Inst{63-32} = LImm; + let Inst{31-27} = major; + let Inst{26-24} = B{2-0}; + let Inst{23-22} = 0b00; + let Inst{21-16} = subop; + let Inst{15} = F; + let Inst{14-12} = B{5-3}; + let Inst{11-6} = 0b111110; + let Inst{5-0} = A; +} + + +// Load and store instructions. +// In addition to the previous naming conventions, load and store instructions +// have: +// di - Uncached bit. When set, loads/stores bypass the cache and access +// memory directly. +// aa - Incrementing mode. Loads and stores can write-back address pre- or +// post- memory operation. +// zz - Memory size (can be 8/16/32 bit load/store). +// x - Sign-extending. When set, short loads can be sign-extended to 32-bits. +// Loads and Stores support different memory addressing modes: +// Base Register + Signed 9-bit Immediate: Both Load/Store. +// LImm: Both Load/Store (Load/Store from a fixed 32-bit address). +// Register + Register: Load Only. +// Register + LImm: Load Only. + +// Register + S9 Load. (B + S9) +// |26|25|24|23|22|21|20|19|18|17|16|15 |14|13|12|11|10|9|8|7|6|5|4|3|2|1|0| +// |B[2-0] |S9[7-0] |S9[8]|B[5-3] |di|aa |zz |x|A | +class F32_LD_RS9 aa, bit di, bits<2> zz, dag outs, dag ins, + string asmstr, list pattern> : + InstARC<4, outs, ins, asmstr, pattern> { + bits<6> B; + bits<6> A; + bits<9> S9; + + let Inst{31-27} = 0b00010; + let Inst{26-24} = B{2-0}; + let Inst{23-16} = S9{7-0}; + let Inst{15} = S9{8}; + let Inst{14-12} = B{5-3}; + let Inst{11} = di; + let Inst{10-9} = aa; + let Inst{8-7} = zz; + let Inst{6} = x; + let Inst{5-0} = A; +} + +class F32_LD_ADDR aa, bit di, bits<2> zz, dag outs, dag ins, + string asmstr, list pattern> : + F32_LD_RS9 { + bits<15> addr; + + let B = addr{14-9}; + let S9 = addr{8-0}; +} + + +// LImm Load. The 32-bit immediate address is in Inst[63-32]. +// |26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10|9|8|7|6|5|4|3|2|1|0| +// | 1| 1| 0| 0 | 1| 1| 1|di| 0|0|zz |x|A | +class F32_LD_LIMM zz, dag outs, dag ins, + string asmstr, list pattern> : + InstARC<8, outs, ins, asmstr, pattern> { + bits<6> LImmReg = 0b111110; + bits<6> A; + bits<32> LImm; + + let Inst{63-32} = LImm; + let Inst{31-27} = 0b00010; + let Inst{26-24} = LImmReg{2-0}; + let Inst{23-15} = 0; + let Inst{14-12} = LImmReg{5-3}; + let Inst{11} = di; + let Inst{10-9} = 0; + let Inst{8-7} = zz; + let Inst{6} = x; + let Inst{5-0} = A; + let DecoderMethod = "DecodeLdLImmInstruction"; +} + +// Register + LImm load. The 32-bit immediate address is in Inst[63-32]. +// |26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10|9|8|7|6|5|4|3|2|1|0| +// |B[2-0] |aa | 1| 1| 0|zz | x|di|B[5-3] | 1| 1|1|1|1|0|A | +class F32_LD_RLIMM aa, bit di, bits<2> zz, dag outs, dag ins, + string asmstr, list pattern> : + InstARC<8, outs, ins, asmstr, pattern> { + bits<6> LImmReg = 0b111110; + bits<32> LImm; + bits<6> B; + bits<6> A; + bits<38> addr; + let B = addr{37-32}; + let LImm = addr{31-0}; + + let Inst{63-32} = LImm; + let Inst{31-27} = 0b00100; + let Inst{26-24} = B{2-0}; + let Inst{23-22} = aa; + let Inst{21-19} = 0b110; + let Inst{18-17} = zz; + let Inst{16} = x; + let Inst{15} = di; + let Inst{14-12} = B{5-3}; + let Inst{11-6} = LImmReg; + let Inst{5-0} = A; + let DecoderMethod = "DecodeLdRLImmInstruction"; +} + +// Register + S9 Store. (B + S9) +// |26|25|24|23|22|21|20|19|18|17|16|15 |14|13|12|11|10|9|8|7|6|5 |4|3|2|1|0| +// |B[2-0] |S9[7-0] |S9[8]|B[5-3] |C |di|aa |zz |0| +class F32_ST_RS9 aa, bit di, bits<2> zz, dag outs, dag ins, + string asmstr, list pattern> : + InstARC<4, outs, ins, asmstr, pattern> { + bits<6> B; + bits<6> C; + bits<9> S9; + + let Inst{31-27} = 0b00011; + let Inst{26-24} = B{2-0}; + let Inst{23-16} = S9{7-0}; + let Inst{15} = S9{8}; + let Inst{14-12} = B{5-3}; + let Inst{11-6} = C; + let Inst{5} = di; + let Inst{4-3} = aa; + let Inst{2-1} = zz; + let Inst{0} = 0; +} + +class F32_ST_ADDR aa, bit di, bits<2> zz, dag outs, dag ins, + string asmstr, list pattern> : + F32_ST_RS9 { + bits<15> addr; + + let B = addr{14-9}; + let S9 = addr{8-0}; +} + +// LImm Store. +// |26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10|9|8|7|6|5 |4|3|2|1|0| +// | 1| 1| 0| 0 | 1| 1| 1|C |di|0|0|zz |0| +class F32_ST_LIMM zz, dag outs, dag ins, + string asmstr, list pattern> : + InstARC<8, outs, ins, asmstr, pattern> { + bits<6> LImmReg = 0b111110; + bits<6> C; + bits<32> LImm; + + let Inst{63-32} = LImm; + let Inst{31-27} = 0b00011; + let Inst{26-24} = LImmReg{2-0}; + let Inst{23-15} = 0; + let Inst{14-12} = LImmReg{5-3}; + let Inst{11-6} = C; + let Inst{5} = di; + let Inst{4-3} = 0; + let Inst{2-1} = zz; + let Inst{0} = 0; + let DecoderMethod = "DecodeStLImmInstruction"; +} + +// Special types for different instruction operands. +def cmovpred : Operand, PredicateOp, + ComplexPattern { + let MIOperandInfo = (ops i32imm, i32imm); + let PrintMethod = "printPredicateOperand"; +} + +def ccond : Operand { + let MIOperandInfo = (ops i32imm); + let PrintMethod = "printPredicateOperand"; +} + +def brccond : Operand { + let MIOperandInfo = (ops i32imm); + let PrintMethod = "printBRCCPredicateOperand"; +} + +// Branch targets of different offset sizes. +def btarget : Operand { + let OperandType = "OPERAND_PCREL"; +} + +def btargetS9 : Operand { + let OperandType = "OPERAND_PCREL"; + let DecoderMethod = "DecodeBranchTargetS9"; +} + +def btargetS21 : Operand { + let OperandType = "OPERAND_PCREL"; + let DecoderMethod = "DecodeBranchTargetS21"; +} + +def btargetS25 : Operand { + let OperandType = "OPERAND_PCREL"; + let DecoderMethod = "DecodeBranchTargetS25"; +} + +def calltargetS25: Operand { + let OperandType = "OPERAND_PCREL"; + let DecoderMethod = "DecodeBranchTargetS25"; +} + diff --git a/lib/Target/ARC/ARCInstrInfo.cpp b/lib/Target/ARC/ARCInstrInfo.cpp new file mode 100644 index 00000000000..48d5a00d594 --- /dev/null +++ b/lib/Target/ARC/ARCInstrInfo.cpp @@ -0,0 +1,394 @@ +//===- ARCInstrInfo.cpp - ARC 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 ARC implementation of the TargetInstrInfo class. +// +//===----------------------------------------------------------------------===// + +#include "ARCInstrInfo.h" +#include "ARC.h" +#include "ARCMachineFunctionInfo.h" +#include "ARCSubtarget.h" +#include "MCTargetDesc/ARCInfo.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineMemOperand.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/TargetRegistry.h" + +using namespace llvm; + +#define GET_INSTRINFO_CTOR_DTOR +#include "ARCGenInstrInfo.inc" + +#define DEBUG_TYPE "arc-inst-info" +// Pin the vtable to this file. +void ARCInstrInfo::anchor() {} + +ARCInstrInfo::ARCInstrInfo() + : ARCGenInstrInfo(ARC::ADJCALLSTACKDOWN, ARC::ADJCALLSTACKUP), RI() {} + +static bool isZeroImm(const MachineOperand &Op) { + return Op.isImm() && Op.getImm() == 0; +} + +static bool isLoad(int Opcode) { + return Opcode == ARC::LD_rs9 || Opcode == ARC::LDH_rs9 || + Opcode == ARC::LDB_rs9; +} + +static bool isStore(int Opcode) { + return Opcode == ARC::ST_rs9 || Opcode == ARC::STH_rs9 || + Opcode == ARC::STB_rs9; +} + +/// If the specified machine instruction is a direct +/// load from a stack slot, return the virtual or physical register number of +/// the destination along with the FrameIndex of the loaded stack slot. If +/// not, return 0. This predicate must return 0 if the instruction has +/// any side effects other than loading from the stack slot. +unsigned ARCInstrInfo::isLoadFromStackSlot(const MachineInstr &MI, + int &FrameIndex) const { + int Opcode = MI.getOpcode(); + if (isLoad(Opcode)) { + if ((MI.getOperand(1).isFI()) && // is a stack slot + (MI.getOperand(2).isImm()) && // the imm is zero + (isZeroImm(MI.getOperand(2)))) { + FrameIndex = MI.getOperand(1).getIndex(); + return MI.getOperand(0).getReg(); + } + } + return 0; +} + +/// If the specified machine instruction is a direct +/// store to a stack slot, return the virtual or physical register number of +/// the source reg along with the FrameIndex of the loaded stack slot. If +/// not, return 0. This predicate must return 0 if the instruction has +/// any side effects other than storing to the stack slot. +unsigned ARCInstrInfo::isStoreToStackSlot(const MachineInstr &MI, + int &FrameIndex) const { + int Opcode = MI.getOpcode(); + if (isStore(Opcode)) { + if ((MI.getOperand(1).isFI()) && // is a stack slot + (MI.getOperand(2).isImm()) && // the imm is zero + (isZeroImm(MI.getOperand(2)))) { + FrameIndex = MI.getOperand(1).getIndex(); + return MI.getOperand(0).getReg(); + } + } + return 0; +} + +/// Return the inverse of passed condition, i.e. turning COND_E to COND_NE. +static ARCCC::CondCode GetOppositeBranchCondition(ARCCC::CondCode CC) { + switch (CC) { + default: + llvm_unreachable("Illegal condition code!"); + case ARCCC::EQ: + return ARCCC::NE; + case ARCCC::NE: + return ARCCC::EQ; + case ARCCC::LO: + return ARCCC::HS; + case ARCCC::HS: + return ARCCC::LO; + case ARCCC::GT: + return ARCCC::LE; + case ARCCC::GE: + return ARCCC::LT; + case ARCCC::LT: + return ARCCC::GE; + case ARCCC::LE: + return ARCCC::GT; + case ARCCC::HI: + return ARCCC::LS; + case ARCCC::LS: + return ARCCC::HI; + case ARCCC::NZ: + return ARCCC::Z; + case ARCCC::Z: + return ARCCC::NZ; + } +} + +static bool isUncondBranchOpcode(int Opc) { return Opc == ARC::BR; } + +static bool isCondBranchOpcode(int Opc) { + return Opc == ARC::BRcc_rr_p || Opc == ARC::BRcc_ru6_p; +} + +static bool isJumpOpcode(int Opc) { return Opc == ARC::J; } + +/// Analyze the branching code at the end of MBB, returning +/// true if it cannot be understood (e.g. it's a switch dispatch or isn't +/// implemented for a target). Upon success, this returns false and returns +/// with the following information in various cases: +/// +/// 1. If this block ends with no branches (it just falls through to its succ) +/// just return false, leaving TBB/FBB null. +/// 2. If this block ends with only an unconditional branch, it sets TBB to be +/// the destination block. +/// 3. If this block ends with a conditional branch and it falls through to a +/// successor block, it sets TBB to be the branch destination block and a +/// list of operands that evaluate the condition. These operands can be +/// passed to other TargetInstrInfo methods to create new branches. +/// 4. If this block ends with a conditional branch followed by an +/// unconditional branch, it returns the 'true' destination in TBB, the +/// 'false' destination in FBB, and a list of operands that evaluate the +/// condition. These operands can be passed to other TargetInstrInfo +/// methods to create new branches. +/// +/// Note that RemoveBranch and InsertBranch must be implemented to support +/// cases where this method returns success. +/// +/// If AllowModify is true, then this routine is allowed to modify the basic +/// block (e.g. delete instructions after the unconditional branch). + +bool ARCInstrInfo::analyzeBranch(MachineBasicBlock &MBB, + MachineBasicBlock *&TBB, + MachineBasicBlock *&FBB, + SmallVectorImpl &Cond, + bool AllowModify) const { + TBB = FBB = nullptr; + MachineBasicBlock::iterator I = MBB.end(); + if (I == MBB.begin()) + return false; + --I; + + while (isPredicated(*I) || I->isTerminator() || I->isDebugValue()) { + // Flag to be raised on unanalyzeable instructions. This is useful in cases + // where we want to clean up on the end of the basic block before we bail + // out. + bool CantAnalyze = false; + + // Skip over DEBUG values and predicated nonterminators. + while (I->isDebugValue() || !I->isTerminator()) { + if (I == MBB.begin()) + return false; + --I; + } + + if (isJumpOpcode(I->getOpcode())) { + // Indirect branches and jump tables can't be analyzed, but we still want + // to clean up any instructions at the tail of the basic block. + CantAnalyze = true; + } else if (isUncondBranchOpcode(I->getOpcode())) { + TBB = I->getOperand(0).getMBB(); + } else if (isCondBranchOpcode(I->getOpcode())) { + // Bail out if we encounter multiple conditional branches. + if (!Cond.empty()) + return true; + + assert(!FBB && "FBB should have been null."); + FBB = TBB; + TBB = I->getOperand(0).getMBB(); + Cond.push_back(I->getOperand(1)); + Cond.push_back(I->getOperand(2)); + Cond.push_back(I->getOperand(3)); + } else if (I->isReturn()) { + // Returns can't be analyzed, but we should run cleanup. + CantAnalyze = !isPredicated(*I); + } else { + // We encountered other unrecognized terminator. Bail out immediately. + return true; + } + + // Cleanup code - to be run for unpredicated unconditional branches and + // returns. + if (!isPredicated(*I) && (isUncondBranchOpcode(I->getOpcode()) || + isJumpOpcode(I->getOpcode()) || I->isReturn())) { + // Forget any previous condition branch information - it no longer + // applies. + Cond.clear(); + FBB = nullptr; + + // If we can modify the function, delete everything below this + // unconditional branch. + if (AllowModify) { + MachineBasicBlock::iterator DI = std::next(I); + while (DI != MBB.end()) { + MachineInstr &InstToDelete = *DI; + ++DI; + InstToDelete.eraseFromParent(); + } + } + } + + if (CantAnalyze) + return true; + + if (I == MBB.begin()) + return false; + + --I; + } + + // We made it past the terminators without bailing out - we must have + // analyzed this branch successfully. + return false; +} + +unsigned ARCInstrInfo::removeBranch(MachineBasicBlock &MBB, + int *BytesRemoved) const { + assert(!BytesRemoved && "Code size not handled"); + MachineBasicBlock::iterator I = MBB.getLastNonDebugInstr(); + if (I == MBB.end()) + return 0; + + if (!isUncondBranchOpcode(I->getOpcode()) && + !isCondBranchOpcode(I->getOpcode())) + return 0; + + // Remove the branch. + I->eraseFromParent(); + + I = MBB.end(); + + if (I == MBB.begin()) + return 1; + --I; + if (!isCondBranchOpcode(I->getOpcode())) + return 1; + + // Remove the branch. + I->eraseFromParent(); + return 2; +} + +void ARCInstrInfo::copyPhysReg(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, + const DebugLoc &dl, unsigned DestReg, + unsigned SrcReg, bool KillSrc) const { + assert(ARC::GPR32RegClass.contains(SrcReg) && + "Only GPR32 src copy supported."); + assert(ARC::GPR32RegClass.contains(DestReg) && + "Only GPR32 dest copy supported."); + BuildMI(MBB, I, dl, get(ARC::MOV_rr), DestReg) + .addReg(SrcReg, getKillRegState(KillSrc)); +} + +void ARCInstrInfo::storeRegToStackSlot(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, + unsigned SrcReg, bool isKill, + int FrameIndex, + const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI) const { + DebugLoc dl = MBB.findDebugLoc(I); + MachineFunction &MF = *MBB.getParent(); + MachineFrameInfo &MFI = MF.getFrameInfo(); + unsigned Align = MFI.getObjectAlignment(FrameIndex); + + MachineMemOperand *MMO = MF.getMachineMemOperand( + MachinePointerInfo::getFixedStack(MF, FrameIndex), + MachineMemOperand::MOStore, MFI.getObjectSize(FrameIndex), Align); + + assert(MMO && "Couldn't get MachineMemOperand for store to stack."); + assert(TRI->getSpillSize(*RC) == 4 && + "Only support 4-byte stores to stack now."); + assert(ARC::GPR32RegClass.hasSubClassEq(RC) && + "Only support GPR32 stores to stack now."); + DEBUG(dbgs() << "Created store reg=" << PrintReg(SrcReg, TRI) + << " to FrameIndex=" << FrameIndex << "\n"); + BuildMI(MBB, I, dl, get(ARC::ST_rs9)) + .addReg(SrcReg, getKillRegState(isKill)) + .addFrameIndex(FrameIndex) + .addImm(0) + .addMemOperand(MMO); +} + +void ARCInstrInfo::loadRegFromStackSlot(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, + unsigned DestReg, int FrameIndex, + const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI) const { + DebugLoc dl = MBB.findDebugLoc(I); + MachineFunction &MF = *MBB.getParent(); + MachineFrameInfo &MFI = MF.getFrameInfo(); + unsigned Align = MFI.getObjectAlignment(FrameIndex); + MachineMemOperand *MMO = MF.getMachineMemOperand( + MachinePointerInfo::getFixedStack(MF, FrameIndex), + MachineMemOperand::MOLoad, MFI.getObjectSize(FrameIndex), Align); + + assert(MMO && "Couldn't get MachineMemOperand for store to stack."); + assert(TRI->getSpillSize(*RC) == 4 && + "Only support 4-byte loads from stack now."); + assert(ARC::GPR32RegClass.hasSubClassEq(RC) && + "Only support GPR32 stores to stack now."); + DEBUG(dbgs() << "Created load reg=" << PrintReg(DestReg, TRI) + << " from FrameIndex=" << FrameIndex << "\n"); + BuildMI(MBB, I, dl, get(ARC::LD_rs9)) + .addReg(DestReg, RegState::Define) + .addFrameIndex(FrameIndex) + .addImm(0) + .addMemOperand(MMO); +} + +/// Return the inverse opcode of the specified Branch instruction. +bool ARCInstrInfo::reverseBranchCondition( + SmallVectorImpl &Cond) const { + assert((Cond.size() == 3) && "Invalid ARC branch condition!"); + Cond[2].setImm(GetOppositeBranchCondition((ARCCC::CondCode)Cond[2].getImm())); + return false; +} + +MachineBasicBlock::iterator +ARCInstrInfo::loadImmediate(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, unsigned Reg, + uint64_t Value) const { + DebugLoc dl = MBB.findDebugLoc(MI); + if (isInt<12>(Value)) { + return BuildMI(MBB, MI, dl, get(ARC::MOV_rs12), Reg) + .addImm(Value) + .getInstr(); + } + llvm_unreachable("Need Arc long immediate instructions."); +} + +unsigned ARCInstrInfo::insertBranch(MachineBasicBlock &MBB, + MachineBasicBlock *TBB, + MachineBasicBlock *FBB, + ArrayRef Cond, + const DebugLoc &dl, int *BytesAdded) const { + assert(!BytesAdded && "Code size not handled."); + + // Shouldn't be a fall through. + assert(TBB && "InsertBranch must not be told to insert a fallthrough"); + assert((Cond.size() == 3 || Cond.size() == 0) && + "ARC branch conditions have two components!"); + + if (Cond.empty()) { + BuildMI(&MBB, dl, get(ARC::BR)).addMBB(TBB); + return 1; + } + int BccOpc = Cond[1].isImm() ? ARC::BRcc_ru6_p : ARC::BRcc_rr_p; + MachineInstrBuilder MIB = BuildMI(&MBB, dl, get(BccOpc)); + MIB.addMBB(TBB); + for (unsigned i = 0; i < 3; i++) { + MIB.add(Cond[i]); + } + + // One-way conditional branch. + if (!FBB) { + return 1; + } + + // Two-way conditional branch. + BuildMI(&MBB, dl, get(ARC::BR)).addMBB(FBB); + return 2; +} + +unsigned ARCInstrInfo::getInstSizeInBytes(const MachineInstr &MI) const { + if (MI.getOpcode() == TargetOpcode::INLINEASM) { + const MachineFunction *MF = MI.getParent()->getParent(); + const char *AsmStr = MI.getOperand(0).getSymbolName(); + return getInlineAsmLength(AsmStr, *MF->getTarget().getMCAsmInfo()); + } + return MI.getDesc().getSize(); +} diff --git a/lib/Target/ARC/ARCInstrInfo.h b/lib/Target/ARC/ARCInstrInfo.h new file mode 100644 index 00000000000..5285dce9f12 --- /dev/null +++ b/lib/Target/ARC/ARCInstrInfo.h @@ -0,0 +1,94 @@ +//===- ARCInstrInfo.h - ARC 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 ARC implementation of the TargetInstrInfo class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_ARC_ARCINSTRINFO_H +#define LLVM_LIB_TARGET_ARC_ARCINSTRINFO_H + +#include "ARCRegisterInfo.h" +#include "llvm/Target/TargetInstrInfo.h" + +#define GET_INSTRINFO_HEADER +#include "ARCGenInstrInfo.inc" + +namespace llvm { + +class ARCSubtarget; + +class ARCInstrInfo : public ARCGenInstrInfo { + const ARCRegisterInfo RI; + virtual void anchor(); + +public: + ARCInstrInfo(); + + const ARCRegisterInfo &getRegisterInfo() const { return RI; } + + /// If the specified machine instruction is a direct + /// load from a stack slot, return the virtual or physical register number of + /// the destination along with the FrameIndex of the loaded stack slot. If + /// not, return 0. This predicate must return 0 if the instruction has + /// any side effects other than loading from the stack slot. + unsigned isLoadFromStackSlot(const MachineInstr &MI, + int &FrameIndex) const override; + + /// If the specified machine instruction is a direct + /// store to a stack slot, return the virtual or physical register number of + /// the source reg along with the FrameIndex of the loaded stack slot. If + /// not, return 0. This predicate must return 0 if the instruction has + /// any side effects other than storing to the stack slot. + unsigned isStoreToStackSlot(const MachineInstr &MI, + int &FrameIndex) const override; + + unsigned getInstSizeInBytes(const MachineInstr &MI) const override; + + bool analyzeBranch(MachineBasicBlock &MBB, MachineBasicBlock *&TBB, + MachineBasicBlock *&FBB, + SmallVectorImpl &Cond, + bool AllowModify) const override; + + unsigned insertBranch(MachineBasicBlock &MBB, MachineBasicBlock *TBB, + MachineBasicBlock *FBB, ArrayRef Cond, + const DebugLoc &dl, + int *BytesAdded = nullptr) const override; + + unsigned removeBranch(MachineBasicBlock &MBB, + int *BytesRemoved = nullptr) const override; + + void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, + const DebugLoc &dl, unsigned DestReg, unsigned SrcReg, + bool KillSrc) const override; + + void storeRegToStackSlot(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, unsigned SrcReg, + bool isKill, int FrameIndex, + const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI) const override; + + void loadRegFromStackSlot(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, unsigned DestReg, + int FrameIndex, const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI) const override; + + bool + reverseBranchCondition(SmallVectorImpl &Cond) const override; + + // Emit code before MBBI to load immediate value into physical register Reg. + // Returns an iterator to the new instruction. + MachineBasicBlock::iterator loadImmediate(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + unsigned Reg, uint64_t Value) const; +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_ARC_ARCINSTRINFO_H diff --git a/lib/Target/ARC/ARCInstrInfo.td b/lib/Target/ARC/ARCInstrInfo.td new file mode 100644 index 00000000000..79ab42fcef3 --- /dev/null +++ b/lib/Target/ARC/ARCInstrInfo.td @@ -0,0 +1,504 @@ +//===- ARCInstrInfo.td - Target Description for ARC --------*- 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 ARC instructions in TableGen format. +// +//===----------------------------------------------------------------------===// + +include "ARCInstrFormats.td" + +// --------------------------------------------------------------------------- +// Selection DAG Nodes. +// --------------------------------------------------------------------------- + +// Selection DAG types. +def SDT_ARCcmptst : SDTypeProfile<0, 2, [SDTCisSameAs<0, 1>]>; +def SDT_ARCcmov : SDTypeProfile<1, 3, [SDTCisSameAs<0, 1>]>; +def SDT_ARCmov : SDTypeProfile<1, 1, [SDTCisSameAs<0, 1>]>; +def SDT_ARCbrcc : SDTypeProfile<0, 4, []>; +def SDT_ARCBranchLink : SDTypeProfile<0, 1, [SDTCisPtrTy<0>]>; +def SDT_ARCCallSeqStart : SDCallSeqStart<[ SDTCisVT<0, i32>, + SDTCisVT<1, i32> ]>; +def SDT_ARCCallSeqEnd : SDCallSeqEnd<[ SDTCisVT<0, i32>, + SDTCisVT<1, i32> ]>; + + +// Global Address. +def ARCGAWrapper : SDNode<"ARCISD::GAWRAPPER", SDT_ARCmov, []>; + +// Comparison +def ARCcmp : SDNode<"ARCISD::CMP", SDT_ARCcmptst, [SDNPOutGlue]>; + +// Conditionanal mov +def ARCcmov : SDNode<"ARCISD::CMOV", SDT_ARCcmov, [SDNPInGlue]>; + +// Conditional Branch +def ARCbrcc : SDNode<"ARCISD::BRcc", SDT_ARCbrcc, + [SDNPHasChain, SDNPInGlue, SDNPOutGlue]>; + +// Direct Call +def ARCBranchLink : SDNode<"ARCISD::BL",SDT_ARCBranchLink, + [SDNPHasChain, SDNPOptInGlue, SDNPOutGlue, + SDNPVariadic]>; + +// Indirect Call +def ARCJumpLink : SDNode<"ARCISD::JL",SDT_ARCBranchLink, + [SDNPHasChain, SDNPOptInGlue, SDNPOutGlue, + SDNPVariadic]>; +// Call return +def ret : SDNode<"ARCISD::RET", SDTNone, + [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>; + +// Call sequencing nodes. +// These are target-independent nodes, but have target-specific formats. +def callseq_start : SDNode<"ISD::CALLSEQ_START", SDT_ARCCallSeqStart, + [SDNPHasChain, SDNPOutGlue]>; +def callseq_end : SDNode<"ISD::CALLSEQ_END", SDT_ARCCallSeqEnd, + [SDNPHasChain, SDNPOptInGlue, SDNPOutGlue]>; + +//===----------------------------------------------------------------------===// +// Instruction Pattern Stuff +//===----------------------------------------------------------------------===// + +def imm32 : ImmLeaf; + +// Addressing modes +def FrameADDR_ri : ComplexPattern; +def AddrModeS9 : ComplexPattern; +def AddrModeImm : ComplexPattern; +def AddrModeFar : ComplexPattern; + +//===----------------------------------------------------------------------===// +// Instruction Class Templates +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Pseudo Instructions +//===----------------------------------------------------------------------===// + +let Defs = [SP], Uses = [SP] in { +def ADJCALLSTACKDOWN : PseudoInstARC<(outs), (ins i32imm:$amt, i32imm:$amt2), + "# ADJCALLSTACKDOWN $amt, $amt2", + [(callseq_start timm:$amt, timm:$amt2)]>; +def ADJCALLSTACKUP : PseudoInstARC<(outs), (ins i32imm:$amt1, i32imm:$amt2), + "# ADJCALLSTACKUP $amt1", + [(callseq_end timm:$amt1, timm:$amt2)]>; +} + +def GETFI : PseudoInstARC<(outs GPR32:$dst), (ins MEMii:$addr), + "pldfi $dst, $addr", + [(set GPR32:$dst, FrameADDR_ri:$addr)]>; + + +def ST_FAR : PseudoInstARC<(outs), (ins GPR32:$dst, MEMrlimm:$addr), + "ST_FAR $dst, $addr", + [(store GPR32:$dst, AddrModeFar:$addr)]>; + +def STH_FAR : PseudoInstARC<(outs), (ins GPR32:$dst, MEMrlimm:$addr), + "STH_FAR $dst, $addr", + [(truncstorei16 GPR32:$dst, AddrModeFar:$addr)]>; + +def STB_FAR : PseudoInstARC<(outs), (ins GPR32:$dst, MEMrlimm:$addr), + "STB_FAR $dst, $addr", + [(truncstorei8 GPR32:$dst, AddrModeFar:$addr)]>; + +//===----------------------------------------------------------------------===// +// Instruction Generation multiclasses. +// Generate many variants of a single instruction with a single defining +// multiclass. These classes do not contain Selection DAG patterns. +//===----------------------------------------------------------------------===// + +// Generic 3 operand binary instructions (i.e., add, r0, r1, r2). +multiclass ArcBinaryInst major, bits<6> mincode, + string opasm> { + // 3 register variant. + def _rrr : F32_DOP_RR; + + // 2 register with unsigned 6-bit immediate variant. + def _rru6 : F32_DOP_RU6; + // 2 register with 32-bit immediate variant. + def _rrlimm : F32_DOP_RLIMM; + // 2 matched-register with signed 12-bit immediate variant (add r0, r0, -1). + def _rrs12 : F32_DOP_RS12 + { let Constraints = "$B = $in"; } +} + +// Special multivariant GEN4 DOP format instruction that take 2 registers. +// This is the class that is used for various comparison instructions. +multiclass ArcSpecialDOPInst subop, string opasm, bit F> { + def _rr : F32_DOP_RR<0b00100, subop, F, (outs), (ins GPR32:$B, GPR32:$C), + !strconcat(opasm, "\t$B, $C"), + []>; + + def _ru6 : F32_DOP_RU6<0b00100, subop, F, (outs), (ins GPR32:$B, i32imm:$U6), + !strconcat(opasm, "\t$B, $U6"), + []>; + + def _rlimm : F32_DOP_RLIMM<0b00100, subop, F, (outs), + (ins GPR32:$B, i32imm:$LImm), + !strconcat(opasm, "\t$B, $LImm"), + []>; +} + +// Generic 2-operand unary instructions. +multiclass ArcUnaryInst major, bits<6> subop, + string opasm> { + def _rr : F32_SOP_RR; +} + + +multiclass ArcBinaryGEN4Inst mincode, string opasm> : + ArcBinaryInst<0b00100, mincode, opasm>; +multiclass ArcBinaryEXT5Inst mincode, string opasm> : + ArcBinaryInst<0b00101, mincode, opasm>; + +multiclass ArcUnaryGEN4Inst mincode, string opasm> : + ArcUnaryInst<0b00100, mincode, opasm>; + +// Pattern generation for differnt instruction variants. +multiclass MultiPat { + def _rrr : Pat<(InFrag i32:$B, i32:$C), (RRR i32:$B, i32:$C)>; + def _rru6 : Pat<(InFrag i32:$B, immU6:$U6), (RRU6 i32:$B, immU6:$U6)>; + def _rrlimm : Pat<(InFrag i32:$B, imm32:$LImm), (RRLImm i32:$B, imm32:$LImm)>; +} + +// --------------------------------------------------------------------------- +// Instruction defintions and patterns for 3 operand binary instructions. +// --------------------------------------------------------------------------- + +// Definitions for 3 operand binary instructions. +defm ADD : ArcBinaryGEN4Inst<0b000000, "add">; +defm SUB : ArcBinaryGEN4Inst<0b000010, "sub">; +defm OR : ArcBinaryGEN4Inst<0b000101, "or">; +defm AND : ArcBinaryGEN4Inst<0b000100, "and">; +defm XOR : ArcBinaryGEN4Inst<0b000111, "xor">; +defm MAX : ArcBinaryGEN4Inst<0b001000, "max">; +defm MIN : ArcBinaryGEN4Inst<0b001001, "min">; +defm ASL : ArcBinaryEXT5Inst<0b000000, "asl">; +defm LSR : ArcBinaryEXT5Inst<0b000001, "lsr">; +defm ASR : ArcBinaryEXT5Inst<0b000010, "asr">; +defm ROR : ArcBinaryEXT5Inst<0b000011, "ror">; +defm MPY : ArcBinaryGEN4Inst<0b011010, "mpy">; +defm MPYM : ArcBinaryGEN4Inst<0b011011, "mpym">; +defm MPYMU : ArcBinaryGEN4Inst<0b011100, "mpymu">; + +// Patterns for 3 operand binary instructions. +defm : MultiPat; +defm : MultiPat; +defm : MultiPat; +defm : MultiPat; +defm : MultiPat; +defm : MultiPat; +defm : MultiPat; +defm : MultiPat; +defm : MultiPat; +defm : MultiPat; +defm : MultiPat; +defm : MultiPat; +defm : MultiPat; +defm : MultiPat; + + +// --------------------------------------------------------------------------- +// Unary Instruction definitions. +// --------------------------------------------------------------------------- +// General unary instruction definitions. +defm SEXB : ArcUnaryGEN4Inst<0b000101, "sexb">; +defm SEXH : ArcUnaryGEN4Inst<0b000110, "sexh">; + +// General Unary Instruction fragments. +def : Pat<(sext_inreg i32:$a, i8), (SEXB_rr i32:$a)>; +def : Pat<(sext_inreg i32:$a, i16), (SEXH_rr i32:$a)>; + +// Comparison instruction definition +let isCompare = 1, Defs = [STATUS32] in { +defm CMP : ArcSpecialDOPInst<0b001100, "cmp", 1>; +} + +def cmp : PatFrag<(ops node:$op1, node:$op2), (ARCcmp $op1, $op2)>; +defm : MultiPat; + +// --------------------------------------------------------------------------- +// MOV instruction and variants (conditional mov). +// --------------------------------------------------------------------------- +let isReMaterializable = 1, isAsCheapAsAMove = 1, isMoveImm = 1 in { +def MOV_rs12 : F32_DOP_RS12<0b00100, 0b001010, 0, + (outs GPR32:$B), (ins immS12:$S12), + "mov\t$B, $S12", + [(set GPR32:$B, immS12:$S12)]>; +} + +def MOV_rr : F32_DOP_RR<0b00100, 0b001010, 0, + (outs GPR32:$B), (ins GPR32:$C), + "mov\t$B, $C", []>; + +def MOV_rlimm : F32_DOP_RLIMM<0b00100, 0b001010, 0, + (outs GPR32:$B), (ins i32imm:$LImm), + "mov\t$B, $LImm", []>; + +def MOV_ru6 : F32_DOP_RU6<0b00100, 0b001010, 0, + (outs GPR32:$B), (ins immU6:$U6), + "mov\t$B, $U6", []>; + +def cmov : PatFrag<(ops node:$op1, node:$op2, node:$cc), + (ARCcmov $op1, $op2, $cc)>; +let Uses = [STATUS32] in { +def MOVcc : F32_DOP_CC_RR<0b00100, 0b001010, 0, + (outs GPR32:$B), + (ins GPR32:$C, GPR32:$fval, cmovpred:$cc), + !strconcat("mov.", "$cc\t$B, $C"), + [(set GPR32:$B, (cmov i32:$C, i32:$fval, cmovpred:$cc))]> { + let Constraints = "$B = $fval"; +} +} +def : Pat<(ARCGAWrapper tglobaladdr:$addr), + (MOV_rlimm tglobaladdr:$addr)>; + +def : Pat<(ARCGAWrapper tjumptable:$addr), + (MOV_rlimm tjumptable:$addr)>; + + +// --------------------------------------------------------------------------- +// Control flow instructions (branch, return, calls, etc). +// --------------------------------------------------------------------------- + +// Branch instructions +let isBranch = 1, isTerminator = 1, isBarrier = 1 in { +// Unconditional branch. +def BR : F32_BR0_UCOND_FAR<(outs), (ins btargetS25:$S25), + "b\t$S25", [(br bb:$S25)]>; + +let Uses=[STATUS32] in { +// Conditional branch. +def Bcc : F32_BR0_COND<(outs), (ins btargetS21:$S21, ccond:$cc), + "b$cc\t$S21", []>; +} + +// Compare and branch (limited range). +def BRcc_rr : F32_BR1_BCC<(outs), + (ins btargetS9:$S9, GPR32:$B, GPR32:$C, brccond:$cc), + "br$cc\t$B, $C, $S9", 0, []>; +def BRcc_ru6 : F32_BR1_BCC<(outs), + (ins btargetS9:$S9, GPR32:$B, immU6:$C, brccond:$cc), + "br$cc\t$B, $C, $S9", 1, []>; + +// Pseudo compare and branch. +// After register allocation, this can expand into either a limited range +// Compare and branch (BRcc), or into CMP + Bcc. +// At worst, this expands into 2 4-byte instructions. +def BRcc_rr_p : PseudoInstARC<(outs), + (ins btarget:$T, GPR32:$B, GPR32:$C, ccond:$cc), + "pbr$cc\t$B, $C, $T", + [(ARCbrcc bb:$T, i32:$B, i32:$C, imm32:$cc)]> + { let Size = 8; } + +def BRcc_ru6_p : PseudoInstARC<(outs), + (ins btarget:$T, GPR32:$B, i32imm:$C, ccond:$cc), + "pbr$cc\t$B, $C, $T", + [(ARCbrcc bb:$T, i32:$B, immU6:$C, imm32:$cc)]> + { let Size = 8; } +} + +// Indirect, unconditional Jump. +let isBranch = 1, isTerminator = 1, isBarrier = 1, isIndirectBranch = 1 in { +def J : F32_DOP_RR<0b00100, 0b100000, 0, + (outs), (ins GPR32:$C), + "j\t[$C]", [(brind i32:$C)]>; +} + +// Call instructions. +let isCall = 1, Defs = [BLINK], Uses = [SP] in { +// Direct unconditional call. +def BL : F32_BR1_BL_UCOND_FAR<(outs), (ins calltargetS25:$S25), + "bl\t$S25", [(ARCBranchLink tglobaladdr:$S25)]>; + +// Indirect unconditional call. +let isIndirectBranch = 1, Defs = [BLINK], Uses = [SP] in { +def JL : F32_DOP_RR<0b00100, 0b100010, 0, (outs), (ins GPR32:$C), + "jl\t[$C]", [(ARCJumpLink i32:$C)]>; +} +} + +// Pattern to generate BL instruction. +def : Pat<(ARCBranchLink texternalsym:$dst), (BL texternalsym:$dst)>; + +// Return from call. +let isReturn = 1, isTerminator = 1, isBarrier = 1 in { +// This is a specialized 2-byte instruction that doesn't generalize +// to any larger 2-byte class, so go ahead and define it here. +def J_S_BLINK : InstARC<2, (outs), (ins), "j_s\t[%blink]", [(ret)]> { + let Inst{15-0} = 0b0111111011100000; +} +} + +//---------------------------------------------------------------------------- +// Load/Store instructions. +//---------------------------------------------------------------------------- + +// 2-byte push/pop blink instructions commonly used for prolog/epilog +// generation. These 2 instructions are actually specialized 2-byte +// format instructions that aren't generalized to a larger 2-byte +// class, so we might as well have them here. +let Uses = [BLINK], Defs = [SP] in { +def PUSH_S_BLINK : InstARC<2, (outs), (ins), + "push_s\t%blink", []> { + let Inst{15-0} = 0b1100000011110001; +} +} + +let Defs = [BLINK, SP] in { +def POP_S_BLINK : InstARC<2, (outs), (ins), + "pop_s\t%blink", []> { + let Inst{15-0} = 0b1100000011010001; +} +} + +// Load instruction variants: +// Control bits: x, aa, di, zz +// x - sign extend. +// aa - incrementing mode. (N/A for LIMM). +// di - uncached. +// zz - data size. +multiclass ArcLdInst zz, string asmop> { + let mayLoad = 1 in { + def _rs9 : F32_LD_ADDR<0, 0b00, 0, zz, + (outs GPR32:$A), (ins MEMrs9:$addr), + !strconcat(asmop, "\t$A, [$addr]"), []>; + + def _limm : F32_LD_LIMM<0, 0, zz, + (outs GPR32:$A), (ins MEMii:$addr), + !strconcat(asmop, "\t$A, [$addr]"), []>; + + def _rlimm : F32_LD_RLIMM<0, 0b00, 0, zz, + (outs GPR32:$A), (ins MEMrlimm:$addr), + !strconcat(asmop, "\t$A, [$addr]"), []>; + + def _X_rs9 : F32_LD_ADDR<1, 0b00, 0, zz, + (outs GPR32:$A), (ins MEMrs9:$addr), + !strconcat(asmop, ".x\t$A, [$addr]"), []>; + + def _X_limm : F32_LD_LIMM<1, 0, zz, + (outs GPR32:$A), (ins MEMii:$addr), + !strconcat(asmop, ".x\t$A, [$addr]"), []>; + + def _X_rlimm : F32_LD_RLIMM<1, 0b00, 0, zz, + (outs GPR32:$A), (ins MEMrlimm:$addr), + !strconcat(asmop, ".x\t$A, [$addr]"), []>; + + def _AB_rs9 : F32_LD_RS9<0, 0b10, 0, zz, + (outs GPR32:$addrout, GPR32:$A), + (ins GPR32:$B, immS9:$S9), + !strconcat(asmop, ".ab\t$A, [$B,$S9]"), []> + { let Constraints = "$addrout = $B"; } + } +} + +// Load instruction definitions. +defm LD : ArcLdInst<0b00, "ld">; +defm LDH : ArcLdInst<0b10, "ldh">; +defm LDB : ArcLdInst<0b01, "ldb">; + +// Load instruction patterns. +// 32-bit loads. +def : Pat<(load AddrModeS9:$addr), (LD_rs9 AddrModeS9:$addr)>; +def : Pat<(load AddrModeImm:$addr), (LD_limm AddrModeImm:$addr)>; +def : Pat<(load AddrModeFar:$addr), (LD_rs9 AddrModeFar:$addr)>; + +// 16-bit loads +def : Pat<(zextloadi16 AddrModeS9:$addr), (LDH_rs9 AddrModeS9:$addr)>; +def : Pat<(extloadi16 AddrModeS9:$addr), (LDH_rs9 AddrModeS9:$addr)>; +def : Pat<(zextloadi16 AddrModeImm:$addr), (LDH_limm AddrModeImm:$addr)>; +def : Pat<(extloadi16 AddrModeImm:$addr), (LDH_limm AddrModeImm:$addr)>; +def : Pat<(zextloadi16 AddrModeFar:$addr), (LDH_rlimm AddrModeFar:$addr)>; +def : Pat<(extloadi16 AddrModeFar:$addr), (LDH_rlimm AddrModeFar:$addr)>; +def : Pat<(sextloadi16 AddrModeImm:$addr),(LDH_X_limm AddrModeImm:$addr)>; +def : Pat<(sextloadi16 AddrModeFar:$addr),(LDH_X_rlimm AddrModeFar:$addr)>; +def : Pat<(sextloadi16 AddrModeS9:$addr),(LDH_X_rs9 AddrModeS9:$addr)>; + +// 8-bit loads. +def : Pat<(zextloadi8 AddrModeS9:$addr), (LDB_rs9 AddrModeS9:$addr)>; +def : Pat<(extloadi8 AddrModeS9:$addr), (LDB_rs9 AddrModeS9:$addr)>; +def : Pat<(zextloadi8 AddrModeImm:$addr), (LDB_limm AddrModeImm:$addr)>; +def : Pat<(extloadi8 AddrModeImm:$addr), (LDB_limm AddrModeImm:$addr)>; +def : Pat<(zextloadi8 AddrModeFar:$addr), (LDB_rlimm AddrModeFar:$addr)>; +def : Pat<(extloadi8 AddrModeFar:$addr), (LDB_rlimm AddrModeFar:$addr)>; +def : Pat<(zextloadi1 AddrModeS9:$addr), (LDB_rs9 AddrModeS9:$addr)>; +def : Pat<(extloadi1 AddrModeS9:$addr), (LDB_rs9 AddrModeS9:$addr)>; +def : Pat<(zextloadi1 AddrModeImm:$addr), (LDB_limm AddrModeImm:$addr)>; +def : Pat<(extloadi1 AddrModeImm:$addr), (LDB_limm AddrModeImm:$addr)>; +def : Pat<(zextloadi1 AddrModeFar:$addr), (LDB_rlimm AddrModeFar:$addr)>; +def : Pat<(extloadi1 AddrModeFar:$addr), (LDB_rlimm AddrModeFar:$addr)>; +def : Pat<(sextloadi8 AddrModeImm:$addr),(LDB_X_limm AddrModeImm:$addr)>; +def : Pat<(sextloadi8 AddrModeFar:$addr),(LDB_X_rlimm AddrModeFar:$addr)>; +def : Pat<(sextloadi8 AddrModeS9:$addr),(LDB_X_rs9 AddrModeS9:$addr)>; + + +// Store instruction variants: +// Control bits: aa, di, zz +// aa - incrementing mode. (N/A for LIMM). +// di - uncached. +// zz - data size. +multiclass ArcStInst zz, string asmop> { + let mayStore = 1 in { + def _rs9 : F32_ST_ADDR<0b00, 0, zz, (outs), (ins GPR32:$C, MEMrs9:$addr), + !strconcat(asmop, "\t$C, [$addr]"), []>; + + def _limm : F32_ST_LIMM<0, zz, (outs), (ins GPR32:$C, MEMii:$addr), + !strconcat(asmop, "\t$C, [$addr]"), []>; + + def _AW_rs9 : F32_ST_RS9<0b01, 0, zz, (outs GPR32:$addrout), + (ins GPR32:$C, GPR32:$B, immS9:$S9), + !strconcat(asmop, ".aw\t$C, [$B,$S9]"), []> + { let Constraints = "$addrout = $B"; } + } +} + +// Store instruction definitions. +defm ST : ArcStInst<0b00, "st">; +defm STH : ArcStInst<0b10, "sth">; +defm STB : ArcStInst<0b01, "stb">; + +// Store instruction patterns. +// 32-bit stores +def : Pat<(store i32:$C, AddrModeS9:$addr), + (ST_rs9 i32:$C, AddrModeS9:$addr)>; +def : Pat<(store i32:$C, AddrModeImm:$addr), + (ST_limm i32:$C, AddrModeImm:$addr)>; + +// 16-bit stores +def : Pat<(truncstorei16 i32:$C, AddrModeS9:$addr), + (STH_rs9 i32:$C, AddrModeS9:$addr)>; +def : Pat<(truncstorei16 i32:$C, AddrModeImm:$addr), + (STH_limm i32:$C, AddrModeImm:$addr)>; + +// 8-bit stores +def : Pat<(truncstorei8 i32:$C, AddrModeS9:$addr), + (STB_rs9 i32:$C, AddrModeS9:$addr)>; +def : Pat<(truncstorei8 i32:$C, AddrModeImm:$addr), + (STB_limm i32:$C, AddrModeImm:$addr)>; + diff --git a/lib/Target/ARC/ARCMCInstLower.cpp b/lib/Target/ARC/ARCMCInstLower.cpp new file mode 100644 index 00000000000..4658388924e --- /dev/null +++ b/lib/Target/ARC/ARCMCInstLower.cpp @@ -0,0 +1,115 @@ +//===- ARCMCInstLower.cpp - ARC MachineInstr to MCInst ----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief This file contains code to lower ARC MachineInstrs to their +/// corresponding MCInst records. +/// +//===----------------------------------------------------------------------===// + +#include "ARCMCInstLower.h" +#include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineOperand.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCInst.h" + +using namespace llvm; + +ARCMCInstLower::ARCMCInstLower(MCContext *C, AsmPrinter &AsmPrinter) + : Ctx(C), Printer(AsmPrinter) {} + +MCOperand ARCMCInstLower::LowerSymbolOperand(const MachineOperand &MO, + MachineOperandType MOTy, + unsigned Offset) const { + MCSymbolRefExpr::VariantKind Kind = MCSymbolRefExpr::VK_None; + const MCSymbol *Symbol; + + switch (MOTy) { + case MachineOperand::MO_MachineBasicBlock: + Symbol = MO.getMBB()->getSymbol(); + break; + case MachineOperand::MO_GlobalAddress: + Symbol = Printer.getSymbol(MO.getGlobal()); + Offset += MO.getOffset(); + break; + case MachineOperand::MO_BlockAddress: + Symbol = Printer.GetBlockAddressSymbol(MO.getBlockAddress()); + Offset += MO.getOffset(); + break; + case MachineOperand::MO_ExternalSymbol: + Symbol = Printer.GetExternalSymbolSymbol(MO.getSymbolName()); + Offset += MO.getOffset(); + break; + case MachineOperand::MO_JumpTableIndex: + Symbol = Printer.GetJTISymbol(MO.getIndex()); + break; + case MachineOperand::MO_ConstantPoolIndex: + Symbol = Printer.GetCPISymbol(MO.getIndex()); + Offset += MO.getOffset(); + break; + default: + llvm_unreachable(""); + } + + assert(Symbol && "Symbol creation failed.\n"); + const MCSymbolRefExpr *MCSym = MCSymbolRefExpr::create(Symbol, Kind, *Ctx); + + if (!Offset) + return MCOperand::createExpr(MCSym); + + // Assume offset is never negative. + assert(Offset > 0); + + const MCConstantExpr *OffsetExpr = MCConstantExpr::create(Offset, *Ctx); + const MCBinaryExpr *Add = MCBinaryExpr::createAdd(MCSym, OffsetExpr, *Ctx); + return MCOperand::createExpr(Add); +} + +MCOperand ARCMCInstLower::LowerOperand(const MachineOperand &MO, + unsigned Offset) const { + MachineOperandType MOTy = MO.getType(); + + switch (MOTy) { + default: + llvm_unreachable("unknown operand type"); + case MachineOperand::MO_Register: + // Ignore all implicit register operands. + if (MO.isImplicit()) + break; + return MCOperand::createReg(MO.getReg()); + case MachineOperand::MO_Immediate: + return MCOperand::createImm(MO.getImm() + Offset); + case MachineOperand::MO_MachineBasicBlock: + case MachineOperand::MO_GlobalAddress: + case MachineOperand::MO_ExternalSymbol: + case MachineOperand::MO_JumpTableIndex: + case MachineOperand::MO_ConstantPoolIndex: + case MachineOperand::MO_BlockAddress: + return LowerSymbolOperand(MO, MOTy, Offset); + case MachineOperand::MO_RegisterMask: + break; + } + + return {}; +} + +void ARCMCInstLower::Lower(const MachineInstr *MI, MCInst &OutMI) const { + OutMI.setOpcode(MI->getOpcode()); + + for (unsigned i = 0, e = MI->getNumOperands(); i != e; ++i) { + const MachineOperand &MO = MI->getOperand(i); + MCOperand MCOp = LowerOperand(MO); + + if (MCOp.isValid()) + OutMI.addOperand(MCOp); + } +} diff --git a/lib/Target/ARC/ARCMCInstLower.h b/lib/Target/ARC/ARCMCInstLower.h new file mode 100644 index 00000000000..22e15cdb351 --- /dev/null +++ b/lib/Target/ARC/ARCMCInstLower.h @@ -0,0 +1,44 @@ +//===- ARCMCInstLower.h - Lower MachineInstr to MCInst ----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_ARC_ARCMCINSTLOWER_H +#define LLVM_LIB_TARGET_ARC_ARCMCINSTLOWER_H + +#include "llvm/CodeGen/MachineOperand.h" +#include "llvm/Support/Compiler.h" + +namespace llvm { + +class MCContext; +class MCInst; +class MCOperand; +class MachineInstr; +class MachineFunction; +class Mangler; +class AsmPrinter; + +/// \brief This class is used to lower an MachineInstr into an MCInst. +class LLVM_LIBRARY_VISIBILITY ARCMCInstLower { + using MachineOperandType = MachineOperand::MachineOperandType; + MCContext *Ctx; + AsmPrinter &Printer; + +public: + ARCMCInstLower(MCContext *C, AsmPrinter &asmprinter); + void Lower(const MachineInstr *MI, MCInst &OutMI) const; + MCOperand LowerOperand(const MachineOperand &MO, unsigned offset = 0) const; + +private: + MCOperand LowerSymbolOperand(const MachineOperand &MO, + MachineOperandType MOTy, unsigned Offset) const; +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_ARC_ARCMCINSTLOWER_H diff --git a/lib/Target/ARC/ARCMachineFunctionInfo.cpp b/lib/Target/ARC/ARCMachineFunctionInfo.cpp new file mode 100644 index 00000000000..7672f8d2c6d --- /dev/null +++ b/lib/Target/ARC/ARCMachineFunctionInfo.cpp @@ -0,0 +1,14 @@ +//===- ARCMachineFunctionInfo.cpp - ARC machine func info -------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ARCMachineFunctionInfo.h" + +using namespace llvm; + +void ARCFunctionInfo::anchor() {} diff --git a/lib/Target/ARC/ARCMachineFunctionInfo.h b/lib/Target/ARC/ARCMachineFunctionInfo.h new file mode 100644 index 00000000000..bfb3fdef5eb --- /dev/null +++ b/lib/Target/ARC/ARCMachineFunctionInfo.h @@ -0,0 +1,64 @@ +//===- ARCMachineFunctionInfo.h - ARC 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 ARC-specific per-machine-function information. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_ARC_ARCMACHINEFUNCTIONINFO_H +#define LLVM_LIB_TARGET_ARC_ARCMACHINEFUNCTIONINFO_H + +#include "llvm/CodeGen/MachineFunction.h" +#include + +namespace llvm { + +/// ARCFunctionInfo - This class is derived from MachineFunction private +/// ARC target-specific information for each MachineFunction. +class ARCFunctionInfo : public MachineFunctionInfo { + virtual void anchor(); + bool ReturnStackOffsetSet; + int VarArgsFrameIndex; + unsigned VarArgFrameBytes; + unsigned ReturnStackOffset; + +public: + ARCFunctionInfo() + : ReturnStackOffsetSet(false), VarArgsFrameIndex(0), VarArgFrameBytes(0), + ReturnStackOffset(-1U), MaxCallStackReq(0) {} + + explicit ARCFunctionInfo(MachineFunction &MF) + : ReturnStackOffsetSet(false), VarArgsFrameIndex(0), VarArgFrameBytes(0), + ReturnStackOffset(-1U), MaxCallStackReq(0) { + // Functions are 4-byte (2**2) aligned. + MF.setAlignment(2); + } + + ~ARCFunctionInfo() {} + + void setVarArgsFrameIndex(int off) { VarArgsFrameIndex = off; } + int getVarArgsFrameIndex() const { return VarArgsFrameIndex; } + + void setReturnStackOffset(unsigned value) { + assert(!ReturnStackOffsetSet && "Return stack offset set twice"); + ReturnStackOffset = value; + ReturnStackOffsetSet = true; + } + + unsigned getReturnStackOffset() const { + assert(ReturnStackOffsetSet && "Return stack offset not set"); + return ReturnStackOffset; + } + + unsigned MaxCallStackReq; +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_ARC_ARCMACHINEFUNCTIONINFO_H diff --git a/lib/Target/ARC/ARCRegisterInfo.cpp b/lib/Target/ARC/ARCRegisterInfo.cpp new file mode 100644 index 00000000000..66f95911d3e --- /dev/null +++ b/lib/Target/ARC/ARCRegisterInfo.cpp @@ -0,0 +1,233 @@ +//===- ARCRegisterInfo.cpp - ARC Register Information -----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the ARC implementation of the MRegisterInfo class. +// +//===----------------------------------------------------------------------===// + +#include "ARCRegisterInfo.h" +#include "ARC.h" +#include "ARCInstrInfo.h" +#include "ARCMachineFunctionInfo.h" +#include "ARCSubtarget.h" +#include "llvm/ADT/BitVector.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/RegisterScavenging.h" +#include "llvm/IR/Function.h" +#include "llvm/Support/Debug.h" +#include "llvm/Target/TargetFrameLowering.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Target/TargetOptions.h" + +using namespace llvm; + +#define DEBUG_TYPE "arc-reg-info" + +#define GET_REGINFO_TARGET_DESC +#include "ARCGenRegisterInfo.inc" + +static void ReplaceFrameIndex(MachineBasicBlock::iterator II, + const ARCInstrInfo &TII, unsigned Reg, + unsigned FrameReg, int Offset, int StackSize, + int ObjSize, RegScavenger *RS, int SPAdj) { + assert(RS && "Need register scavenger."); + MachineInstr &MI = *II; + MachineBasicBlock &MBB = *MI.getParent(); + DebugLoc dl = MI.getDebugLoc(); + unsigned BaseReg = FrameReg; + unsigned KillState = 0; + if (MI.getOpcode() == ARC::LD_rs9 && (Offset >= 256 || Offset < -256)) { + // Loads can always be reached with LD_rlimm. + BuildMI(MBB, II, dl, TII.get(ARC::LD_rlimm), Reg) + .addReg(BaseReg) + .addImm(Offset) + .addMemOperand(*MI.memoperands_begin()); + MBB.erase(II); + return; + } + + if (MI.getOpcode() != ARC::GETFI && (Offset >= 256 || Offset < -256)) { + // We need to use a scratch register to reach the far-away frame indexes. + BaseReg = RS->FindUnusedReg(&ARC::GPR32RegClass); + if (!BaseReg) { + // We can be sure that the scavenged-register slot is within the range + // of the load offset. + const TargetRegisterInfo *TRI = + MBB.getParent()->getSubtarget().getRegisterInfo(); + BaseReg = RS->scavengeRegister(&ARC::GPR32RegClass, II, SPAdj); + assert(BaseReg && "Register scavenging failed."); + DEBUG(dbgs() << "Scavenged register " << PrintReg(BaseReg, TRI) + << " for FrameReg=" << PrintReg(FrameReg, TRI) + << "+Offset=" << Offset << "\n"); + (void)TRI; + RS->setRegUsed(BaseReg); + } + unsigned AddOpc = isUInt<6>(Offset) ? ARC::ADD_rru6 : ARC::ADD_rrlimm; + BuildMI(MBB, II, dl, TII.get(AddOpc)) + .addReg(BaseReg, RegState::Define) + .addReg(FrameReg) + .addImm(Offset); + Offset = 0; + KillState = RegState::Kill; + } + switch (MI.getOpcode()) { + case ARC::LD_rs9: + assert((Offset % 4 == 0) && "LD needs 4 byte alignment."); + case ARC::LDH_rs9: + case ARC::LDH_X_rs9: + assert((Offset % 2 == 0) && "LDH needs 2 byte alignment."); + case ARC::LDB_rs9: + case ARC::LDB_X_rs9: + DEBUG(dbgs() << "Building LDFI\n"); + BuildMI(MBB, II, dl, TII.get(MI.getOpcode()), Reg) + .addReg(BaseReg, KillState) + .addImm(Offset) + .addMemOperand(*MI.memoperands_begin()); + break; + case ARC::ST_rs9: + assert((Offset % 4 == 0) && "ST needs 4 byte alignment."); + case ARC::STH_rs9: + assert((Offset % 2 == 0) && "STH needs 2 byte alignment."); + case ARC::STB_rs9: + DEBUG(dbgs() << "Building STFI\n"); + BuildMI(MBB, II, dl, TII.get(MI.getOpcode())) + .addReg(Reg, getKillRegState(MI.getOperand(0).isKill())) + .addReg(BaseReg, KillState) + .addImm(Offset) + .addMemOperand(*MI.memoperands_begin()); + break; + case ARC::GETFI: + DEBUG(dbgs() << "Building GETFI\n"); + BuildMI(MBB, II, dl, + TII.get(isUInt<6>(Offset) ? ARC::ADD_rru6 : ARC::ADD_rrlimm)) + .addReg(Reg, RegState::Define) + .addReg(FrameReg) + .addImm(Offset); + break; + default: + llvm_unreachable("Unhandled opcode."); + } + + // Erase old instruction. + MBB.erase(II); +} + +ARCRegisterInfo::ARCRegisterInfo() : ARCGenRegisterInfo(ARC::BLINK) {} + +bool ARCRegisterInfo::needsFrameMoves(const MachineFunction &MF) { + return MF.getMMI().hasDebugInfo() || + MF.getFunction()->needsUnwindTableEntry(); +} + +const MCPhysReg * +ARCRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const { + return CSR_ARC_SaveList; +} + +BitVector ARCRegisterInfo::getReservedRegs(const MachineFunction &MF) const { + BitVector Reserved(getNumRegs()); + + Reserved.set(ARC::ILINK); + Reserved.set(ARC::SP); + Reserved.set(ARC::GP); + Reserved.set(ARC::R25); + Reserved.set(ARC::BLINK); + Reserved.set(ARC::FP); + return Reserved; +} + +bool ARCRegisterInfo::requiresRegisterScavenging( + const MachineFunction &MF) const { + return true; +} + +bool ARCRegisterInfo::trackLivenessAfterRegAlloc( + const MachineFunction &MF) const { + return true; +} + +bool ARCRegisterInfo::useFPForScavengingIndex(const MachineFunction &MF) const { + return true; +} + +void ARCRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II, + int SPAdj, unsigned FIOperandNum, + RegScavenger *RS) const { + assert(SPAdj == 0 && "Unexpected"); + MachineInstr &MI = *II; + MachineOperand &FrameOp = MI.getOperand(FIOperandNum); + int FrameIndex = FrameOp.getIndex(); + + MachineFunction &MF = *MI.getParent()->getParent(); + const ARCInstrInfo &TII = *MF.getSubtarget().getInstrInfo(); + const ARCFrameLowering *TFI = getFrameLowering(MF); + int Offset = MF.getFrameInfo().getObjectOffset(FrameIndex); + int ObjSize = MF.getFrameInfo().getObjectSize(FrameIndex); + int StackSize = MF.getFrameInfo().getStackSize(); + int LocalFrameSize = MF.getFrameInfo().getLocalFrameSize(); + + DEBUG(dbgs() << "\nFunction : " << MF.getName() << "\n"); + DEBUG(dbgs() << "<--------->\n"); + DEBUG(dbgs() << MI << "\n"); + DEBUG(dbgs() << "FrameIndex : " << FrameIndex << "\n"); + DEBUG(dbgs() << "ObjSize : " << ObjSize << "\n"); + DEBUG(dbgs() << "FrameOffset : " << Offset << "\n"); + DEBUG(dbgs() << "StackSize : " << StackSize << "\n"); + DEBUG(dbgs() << "LocalFrameSize : " << LocalFrameSize << "\n"); + (void)LocalFrameSize; + + // Special handling of DBG_VALUE instructions. + if (MI.isDebugValue()) { + unsigned FrameReg = getFrameRegister(MF); + MI.getOperand(FIOperandNum).ChangeToRegister(FrameReg, false /*isDef*/); + MI.getOperand(FIOperandNum + 1).ChangeToImmediate(Offset); + return; + } + + // fold constant into offset. + Offset += MI.getOperand(FIOperandNum + 1).getImm(); + + // TODO: assert based on the load type: + // ldb needs no alignment, + // ldh needs 2 byte alignment + // ld needs 4 byte alignment + DEBUG(dbgs() << "Offset : " << Offset << "\n" + << "<--------->\n"); + + unsigned Reg = MI.getOperand(0).getReg(); + assert(ARC::GPR32RegClass.contains(Reg) && "Unexpected register operand"); + + if (!TFI->hasFP(MF)) { + Offset = StackSize + Offset; + if (FrameIndex >= 0) + assert((Offset >= 0 && Offset < StackSize) && "SP Offset not in bounds."); + } else { + if (FrameIndex >= 0) { + assert((Offset < 0 && -Offset <= StackSize) && + "FP Offset not in bounds."); + } + } + ReplaceFrameIndex(II, TII, Reg, getFrameRegister(MF), Offset, StackSize, + ObjSize, RS, SPAdj); +} + +unsigned ARCRegisterInfo::getFrameRegister(const MachineFunction &MF) const { + const ARCFrameLowering *TFI = getFrameLowering(MF); + return TFI->hasFP(MF) ? ARC::FP : ARC::SP; +} + +const uint32_t * +ARCRegisterInfo::getCallPreservedMask(const MachineFunction &MF, + CallingConv::ID CC) const { + return CSR_ARC_RegMask; +} diff --git a/lib/Target/ARC/ARCRegisterInfo.h b/lib/Target/ARC/ARCRegisterInfo.h new file mode 100644 index 00000000000..297f82886a4 --- /dev/null +++ b/lib/Target/ARC/ARCRegisterInfo.h @@ -0,0 +1,58 @@ +//===- ARCRegisterInfo.h - ARC 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 ARC implementation of the MRegisterInfo class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_ARC_ARCREGISTERINFO_H +#define LLVM_LIB_TARGET_ARC_ARCREGISTERINFO_H + +#include "llvm/Target/TargetRegisterInfo.h" + +#define GET_REGINFO_HEADER +#include "ARCGenRegisterInfo.inc" + +namespace llvm { + +class TargetInstrInfo; + +struct ARCRegisterInfo : public ARCGenRegisterInfo { +public: + ARCRegisterInfo(); + + /// Code Generation virtual methods... + + const MCPhysReg *getCalleeSavedRegs(const MachineFunction *MF) const override; + + BitVector getReservedRegs(const MachineFunction &MF) const override; + + bool requiresRegisterScavenging(const MachineFunction &MF) const override; + + bool trackLivenessAfterRegAlloc(const MachineFunction &MF) const override; + + bool useFPForScavengingIndex(const MachineFunction &MF) const override; + + void eliminateFrameIndex(MachineBasicBlock::iterator II, int SPAdj, + unsigned FIOperandNum, + RegScavenger *RS = nullptr) const override; + + const uint32_t *getCallPreservedMask(const MachineFunction &MF, + CallingConv::ID CC) const override; + + // Debug information queries. + unsigned getFrameRegister(const MachineFunction &MF) const override; + + //! Return whether to emit frame moves + static bool needsFrameMoves(const MachineFunction &MF); +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_ARC_ARCREGISTERINFO_H diff --git a/lib/Target/ARC/ARCRegisterInfo.td b/lib/Target/ARC/ARCRegisterInfo.td new file mode 100644 index 00000000000..6d8d1b3dfd2 --- /dev/null +++ b/lib/Target/ARC/ARCRegisterInfo.td @@ -0,0 +1,80 @@ +//===- ARCRegisterInfo.td - ARC Register defs --------------*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Declarations that describe the ARC register file +//===----------------------------------------------------------------------===// + +class ARCReg altNames> : Register { + field bits<6> HwEncoding; + let Namespace = "ARC"; +} + +// Registers are identified with 6-bit ID numbers. +// Core - 32-bit core registers +class CorealtNames=[]> : ARCReg { + let HWEncoding = num; +} + +class Status : ARCReg { +} + +// Integer registers +def R0 : Core< 0, "%r0">, DwarfRegNum<[0]>; +def R1 : Core< 1, "%r1">, DwarfRegNum<[1]>; +def R2 : Core< 2, "%r2">, DwarfRegNum<[2]>; +def R3 : Core< 3, "%r3">, DwarfRegNum<[3]>; +let CostPerUse=1 in { +def R4 : Core< 4, "%r4">, DwarfRegNum<[4]>; +def R5 : Core< 5, "%r5">, DwarfRegNum<[5]>; +def R6 : Core< 6, "%r6">, DwarfRegNum<[6]>; +def R7 : Core< 7, "%r7">, DwarfRegNum<[7]>; +def R8 : Core< 8, "%r8">, DwarfRegNum<[8]>; +def R9 : Core< 9, "%r9">, DwarfRegNum<[9]>; +def R10 : Core<10, "%r10">, DwarfRegNum<[10]>; +def R11 : Core<11, "%r11">, DwarfRegNum<[11]>; +} +def R12 : Core<12, "%r12">, DwarfRegNum<[12]>; +def R13 : Core<13, "%r13">, DwarfRegNum<[13]>; +def R14 : Core<14, "%r14">, DwarfRegNum<[14]>; +def R15 : Core<15, "%r15">, DwarfRegNum<[15]>; + +let CostPerUse=1 in { +def R16 : Core<16, "%r16">, DwarfRegNum<[16]>; +def R17 : Core<17, "%r17">, DwarfRegNum<[17]>; +def R18 : Core<18, "%r18">, DwarfRegNum<[18]>; +def R19 : Core<19, "%r19">, DwarfRegNum<[19]>; +def R20 : Core<20, "%r20">, DwarfRegNum<[20]>; +def R21 : Core<21, "%r21">, DwarfRegNum<[21]>; +def R22 : Core<22, "%r22">, DwarfRegNum<[22]>; +def R23 : Core<23, "%r23">, DwarfRegNum<[23]>; +def R24 : Core<24, "%r24">, DwarfRegNum<[24]>; +def R25 : Core<25, "%r25">, DwarfRegNum<[25]>; +def GP : Core<26, "%gp",["%r26"]>, DwarfRegNum<[26]>; +def FP : Core<27, "%fp", ["%r27"]>, DwarfRegNum<[27]>; +def SP : Core<28, "%sp", ["%r28"]>, DwarfRegNum<[28]>; +def ILINK : Core<29, "%ilink">, DwarfRegNum<[29]>; +def R30 : Core<30, "%r30">, DwarfRegNum<[30]>; +def BLINK: Core<31, "%blink">, DwarfRegNum<[31]>; + +def STATUS32 : Status<"status32">, DwarfRegNum<[32]>; +} + +// Register classes. +// +def GPR32: RegisterClass<"ARC", [i32], 32, + (add R0, R1, R2, R3, + R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R15, R16, R17, R18, R19, + R20, R21, R22, R23, R24, R25, GP, FP, SP, ILINK, R30, BLINK)>; + +def SREG : RegisterClass<"ARC", [i32], 1, (add STATUS32)>; + +def GPR_S : RegisterClass<"ARC", [i32], 8, + (add R0, R1, R2, R3, R12, R13, R14, R15)>; + diff --git a/lib/Target/ARC/ARCSubtarget.cpp b/lib/Target/ARC/ARCSubtarget.cpp new file mode 100644 index 00000000000..2107a27bf78 --- /dev/null +++ b/lib/Target/ARC/ARCSubtarget.cpp @@ -0,0 +1,31 @@ +//===- ARCSubtarget.cpp - ARC Subtarget 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 implements the ARC specific subclass of TargetSubtargetInfo. +// +//===----------------------------------------------------------------------===// + +#include "ARCSubtarget.h" +#include "ARC.h" +#include "llvm/Support/TargetRegistry.h" + +using namespace llvm; + +#define DEBUG_TYPE "arc-subtarget" + +#define GET_SUBTARGETINFO_TARGET_DESC +#define GET_SUBTARGETINFO_CTOR +#include "ARCGenSubtargetInfo.inc" + +void ARCSubtarget::anchor() {} + +ARCSubtarget::ARCSubtarget(const Triple &TT, const std::string &CPU, + const std::string &FS, const TargetMachine &TM) + : ARCGenSubtargetInfo(TT, CPU, FS), FrameLowering(*this), + TLInfo(TM, *this) {} diff --git a/lib/Target/ARC/ARCSubtarget.h b/lib/Target/ARC/ARCSubtarget.h new file mode 100644 index 00000000000..97fe2f3a227 --- /dev/null +++ b/lib/Target/ARC/ARCSubtarget.h @@ -0,0 +1,66 @@ +//===- ARCSubtarget.h - Define Subtarget for the ARC ------------*- 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 ARC specific subclass of TargetSubtargetInfo. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_ARC_ARCSUBTARGET_H +#define LLVM_LIB_TARGET_ARC_ARCSUBTARGET_H + +#include "ARCFrameLowering.h" +#include "ARCISelLowering.h" +#include "ARCInstrInfo.h" +#include "llvm/CodeGen/SelectionDAGTargetInfo.h" +#include "llvm/Target/TargetSubtargetInfo.h" +#include + +#define GET_SUBTARGETINFO_HEADER +#include "ARCGenSubtargetInfo.inc" + +namespace llvm { + +class StringRef; +class TargetMachine; + +class ARCSubtarget : public ARCGenSubtargetInfo { + virtual void anchor(); + ARCInstrInfo InstrInfo; + ARCFrameLowering FrameLowering; + ARCTargetLowering TLInfo; + SelectionDAGTargetInfo TSInfo; + +public: + /// This constructor initializes the data members to match that + /// of the specified triple. + ARCSubtarget(const Triple &TT, const std::string &CPU, const std::string &FS, + const TargetMachine &TM); + + /// Parses features string setting specified subtarget options. + /// Definition of function is auto generated by tblgen. + void ParseSubtargetFeatures(StringRef CPU, StringRef FS); + + const ARCInstrInfo *getInstrInfo() const override { return &InstrInfo; } + const ARCFrameLowering *getFrameLowering() const override { + return &FrameLowering; + } + const ARCTargetLowering *getTargetLowering() const override { + return &TLInfo; + } + const ARCRegisterInfo *getRegisterInfo() const override { + return &InstrInfo.getRegisterInfo(); + } + const SelectionDAGTargetInfo *getSelectionDAGInfo() const override { + return &TSInfo; + } +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_ARC_ARCSUBTARGET_H diff --git a/lib/Target/ARC/ARCTargetMachine.cpp b/lib/Target/ARC/ARCTargetMachine.cpp new file mode 100644 index 00000000000..d2512c281a6 --- /dev/null +++ b/lib/Target/ARC/ARCTargetMachine.cpp @@ -0,0 +1,95 @@ +//===- ARCTargetMachine.cpp - Define TargetMachine for ARC ------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// +//===----------------------------------------------------------------------===// + +#include "ARCTargetMachine.h" +#include "ARC.h" +#include "ARCTargetTransformInfo.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" +#include "llvm/CodeGen/TargetPassConfig.h" +#include "llvm/Support/TargetRegistry.h" + +using namespace llvm; + +static Reloc::Model getRelocModel(Optional RM) { + if (!RM.hasValue()) + return Reloc::Static; + return *RM; +} + +static CodeModel::Model getEffectiveCodeModel(Optional CM) { + if (CM) + return *CM; + return CodeModel::Small; +} + +/// ARCTargetMachine ctor - Create an ILP32 architecture model +ARCTargetMachine::ARCTargetMachine(const Target &T, const Triple &TT, + StringRef CPU, StringRef FS, + const TargetOptions &Options, + Optional RM, + Optional CM, + CodeGenOpt::Level OL, bool JIT) + : LLVMTargetMachine(T, + "e-m:e-p:32:32-i1:8:32-i8:8:32-i16:16:32-i32:32:32-" + "f32:32:32-i64:32-f64:32-a:0:32-n32", + TT, CPU, FS, Options, getRelocModel(RM), + getEffectiveCodeModel(CM), OL), + TLOF(make_unique()), + Subtarget(TT, CPU, FS, *this) { + initAsmInfo(); +} + +ARCTargetMachine::~ARCTargetMachine() = default; + +namespace { + +/// ARC Code Generator Pass Configuration Options. +class ARCPassConfig : public TargetPassConfig { +public: + ARCPassConfig(ARCTargetMachine &TM, PassManagerBase &PM) + : TargetPassConfig(TM, PM) {} + + ARCTargetMachine &getARCTargetMachine() const { + return getTM(); + } + + bool addInstSelector() override; + void addPreEmitPass() override; + void addPreRegAlloc() override; +}; + +} // end anonymous namespace + +TargetPassConfig *ARCTargetMachine::createPassConfig(PassManagerBase &PM) { + return new ARCPassConfig(*this, PM); +} + +bool ARCPassConfig::addInstSelector() { + addPass(createARCISelDag(getARCTargetMachine(), getOptLevel())); + return false; +} + +void ARCPassConfig::addPreEmitPass() { addPass(createARCBranchFinalizePass()); } + +void ARCPassConfig::addPreRegAlloc() { addPass(createARCExpandPseudosPass()); } + +// Force static initialization. +extern "C" void LLVMInitializeARCTarget() { + RegisterTargetMachine X(getTheARCTarget()); +} + +TargetIRAnalysis ARCTargetMachine::getTargetIRAnalysis() { + return TargetIRAnalysis([this](const Function &F) { + return TargetTransformInfo(ARCTTIImpl(this, F)); + }); +} diff --git a/lib/Target/ARC/ARCTargetMachine.h b/lib/Target/ARC/ARCTargetMachine.h new file mode 100644 index 00000000000..98021b3dc1d --- /dev/null +++ b/lib/Target/ARC/ARCTargetMachine.h @@ -0,0 +1,51 @@ +//===- ARCTargetMachine.h - Define TargetMachine for ARC --------*- 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 ARC specific subclass of TargetMachine. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_ARC_ARCTARGETMACHINE_H +#define LLVM_LIB_TARGET_ARC_ARCTARGETMACHINE_H + +#include "ARCSubtarget.h" +#include "llvm/Target/TargetMachine.h" + +namespace llvm { + +class TargetPassConfig; + +class ARCTargetMachine : public LLVMTargetMachine { + std::unique_ptr TLOF; + ARCSubtarget Subtarget; + +public: + ARCTargetMachine(const Target &T, const Triple &TT, StringRef CPU, + StringRef FS, const TargetOptions &Options, + Optional RM, Optional CM, + CodeGenOpt::Level OL, bool JIT); + ~ARCTargetMachine() override; + + const ARCSubtarget *getSubtargetImpl() const { return &Subtarget; } + const ARCSubtarget *getSubtargetImpl(const Function &) const override { + return &Subtarget; + } + + // Pass Pipeline Configuration + TargetPassConfig *createPassConfig(PassManagerBase &PM) override; + + TargetIRAnalysis getTargetIRAnalysis() override; + TargetLoweringObjectFile *getObjFileLowering() const override { + return TLOF.get(); + } +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_ARC_ARCTARGETMACHINE_H diff --git a/lib/Target/ARC/ARCTargetStreamer.h b/lib/Target/ARC/ARCTargetStreamer.h new file mode 100644 index 00000000000..29fdfda661a --- /dev/null +++ b/lib/Target/ARC/ARCTargetStreamer.h @@ -0,0 +1,25 @@ +//===- ARCTargetStreamer.h - ARC Target Streamer ----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_ARC_ARCTARGETSTREAMER_H +#define LLVM_LIB_TARGET_ARC_ARCTARGETSTREAMER_H + +#include "llvm/MC/MCStreamer.h" + +namespace llvm { + +class ARCTargetStreamer : public MCTargetStreamer { +public: + ARCTargetStreamer(MCStreamer &S); + ~ARCTargetStreamer() override; +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_ARC_ARCTARGETSTREAMER_H diff --git a/lib/Target/ARC/ARCTargetTransformInfo.h b/lib/Target/ARC/ARCTargetTransformInfo.h new file mode 100644 index 00000000000..20a83d5ae4c --- /dev/null +++ b/lib/Target/ARC/ARCTargetTransformInfo.h @@ -0,0 +1,55 @@ +//===- ARCTargetTransformInfo.h - ARC specific TTI --------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// \file +// This file contains a TargetTransformInfo::Concept conforming object specific +// to the ARC target machine. It uses the target's detailed information to +// provide more precise answers to certain TTI queries, while letting the +// target independent and default TTI implementations handle the rest. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_ARC_ARCTARGETTRANSFORMINFO_H +#define LLVM_LIB_TARGET_ARC_ARCTARGETTRANSFORMINFO_H + +#include "ARC.h" +#include "llvm/Analysis/TargetTransformInfo.h" +#include "llvm/CodeGen/BasicTTIImpl.h" + +namespace llvm { + +class ARCSubtarget; +class ARCTargetLowering; +class ARCTargetMachine; + +class ARCTTIImpl : public BasicTTIImplBase { + using BaseT = BasicTTIImplBase; + friend BaseT; + + const ARCSubtarget *ST; + const ARCTargetLowering *TLI; + + const ARCSubtarget *getST() const { return ST; } + const ARCTargetLowering *getTLI() const { return TLI; } + +public: + explicit ARCTTIImpl(const ARCTargetMachine *TM, const Function &F) + : BaseT(TM, F.getParent()->getDataLayout()), ST(TM->getSubtargetImpl()), + TLI(ST->getTargetLowering()) {} + + // Provide value semantics. MSVC requires that we spell all of these out. + ARCTTIImpl(const ARCTTIImpl &Arg) + : BaseT(static_cast(Arg)), ST(Arg.ST), TLI(Arg.TLI) {} + ARCTTIImpl(ARCTTIImpl &&Arg) + : BaseT(std::move(static_cast(Arg))), ST(std::move(Arg.ST)), + TLI(std::move(Arg.TLI)) {} +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_ARC_ARCTARGETTRANSFORMINFO_H diff --git a/lib/Target/ARC/CMakeLists.txt b/lib/Target/ARC/CMakeLists.txt new file mode 100644 index 00000000000..b862a5e61e0 --- /dev/null +++ b/lib/Target/ARC/CMakeLists.txt @@ -0,0 +1,30 @@ +set(LLVM_TARGET_DEFINITIONS ARC.td) + +tablegen(LLVM ARCGenRegisterInfo.inc -gen-register-info) +tablegen(LLVM ARCGenInstrInfo.inc -gen-instr-info) +tablegen(LLVM ARCGenDisassemblerTables.inc -gen-disassembler) +tablegen(LLVM ARCGenAsmWriter.inc -gen-asm-writer) +tablegen(LLVM ARCGenDAGISel.inc -gen-dag-isel) +tablegen(LLVM ARCGenCallingConv.inc -gen-callingconv) +tablegen(LLVM ARCGenSubtargetInfo.inc -gen-subtarget) +add_public_tablegen_target(ARCCommonTableGen) + +add_llvm_target(ARCCodeGen + ARCAsmPrinter.cpp + ARCBranchFinalize.cpp + ARCExpandPseudos.cpp + ARCFrameLowering.cpp + ARCInstrInfo.cpp + ARCISelDAGToDAG.cpp + ARCISelLowering.cpp + ARCMachineFunctionInfo.cpp + ARCMCInstLower.cpp + ARCRegisterInfo.cpp + ARCSubtarget.cpp + ARCTargetMachine.cpp + ) + +add_subdirectory(InstPrinter) +add_subdirectory(TargetInfo) +add_subdirectory(MCTargetDesc) +add_subdirectory(Disassembler) diff --git a/lib/Target/ARC/Disassembler/ARCDisassembler.cpp b/lib/Target/ARC/Disassembler/ARCDisassembler.cpp new file mode 100644 index 00000000000..b49658004f7 --- /dev/null +++ b/lib/Target/ARC/Disassembler/ARCDisassembler.cpp @@ -0,0 +1,298 @@ +//===- ARCDisassembler.cpp - Disassembler for ARC ---------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief This file is part of the ARC Disassembler. +/// +//===----------------------------------------------------------------------===// + +#include "ARC.h" +#include "ARCRegisterInfo.h" +#include "MCTargetDesc/ARCMCTargetDesc.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCDisassembler/MCDisassembler.h" +#include "llvm/MC/MCFixedLenDisassembler.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Support/TargetRegistry.h" + +using namespace llvm; + +#define DEBUG_TYPE "arc-disassembler" + +using DecodeStatus = MCDisassembler::DecodeStatus; + +namespace { + +/// \brief A disassembler class for ARC. +class ARCDisassembler : public MCDisassembler { +public: + std::unique_ptr const MCII; + + ARCDisassembler(const MCSubtargetInfo &STI, MCContext &Ctx, + MCInstrInfo const *MCII) + : MCDisassembler(STI, Ctx), MCII(MCII) {} + + DecodeStatus getInstruction(MCInst &Instr, uint64_t &Size, + ArrayRef Bytes, uint64_t Address, + raw_ostream &VStream, + raw_ostream &CStream) const override; +}; + +} // end anonymous namespace + +static bool readInstruction32(ArrayRef Bytes, uint64_t Address, + uint64_t &Size, uint32_t &Insn) { + Size = 4; + // Read 2 16-bit values, but swap hi/lo parts. + Insn = + (Bytes[0] << 16) | (Bytes[1] << 24) | (Bytes[2] << 0) | (Bytes[3] << 8); + return true; +} + +static bool readInstruction64(ArrayRef Bytes, uint64_t Address, + uint64_t &Size, uint64_t &Insn) { + Size = 8; + Insn = ((uint64_t)Bytes[0] << 16) | ((uint64_t)Bytes[1] << 24) | + ((uint64_t)Bytes[2] << 0) | ((uint64_t)Bytes[3] << 8) | + ((uint64_t)Bytes[4] << 48) | ((uint64_t)Bytes[5] << 56) | + ((uint64_t)Bytes[6] << 32) | ((uint64_t)Bytes[7] << 40); + return true; +} + +static bool readInstruction16(ArrayRef Bytes, uint64_t Address, + uint64_t &Size, uint32_t &Insn) { + Size = 2; + Insn = (Bytes[0] << 0) | (Bytes[1] << 8); + return true; +} + +static MCDisassembler::DecodeStatus DecodeS12Operand(MCInst &, unsigned, + uint64_t, const void *); + +static MCDisassembler::DecodeStatus DecodeS9Operand(MCInst &, unsigned, + uint64_t, const void *); + +static MCDisassembler::DecodeStatus +DecodeBranchTargetS9(MCInst &, unsigned, uint64_t, const void *); + +static MCDisassembler::DecodeStatus +DecodeBranchTargetS21(MCInst &, unsigned, uint64_t, const void *); + +static MCDisassembler::DecodeStatus +DecodeBranchTargetS25(MCInst &, unsigned, uint64_t, const void *); + +static MCDisassembler::DecodeStatus DecodeMEMrs9(MCInst &, unsigned, uint64_t, + const void *); + +static MCDisassembler::DecodeStatus +DecodeLdLImmInstruction(MCInst &, uint64_t, uint64_t, const void *); + +static MCDisassembler::DecodeStatus +DecodeStLImmInstruction(MCInst &, uint64_t, uint64_t, const void *); + +static MCDisassembler::DecodeStatus +DecodeLdRLImmInstruction(MCInst &, uint64_t, uint64_t, const void *); + +static const uint16_t GPR32DecoderTable[] = { + ARC::R0, ARC::R1, ARC::R2, ARC::R3, ARC::R4, ARC::R5, ARC::R6, + ARC::R7, ARC::R8, ARC::R9, ARC::R10, ARC::R11, ARC::R12, ARC::R13, + ARC::R14, ARC::R15, ARC::R16, ARC::R17, ARC::R18, ARC::R19, ARC::R20, + ARC::R21, ARC::R22, ARC::R23, ARC::R24, ARC::R25, ARC::GP, ARC::FP, + ARC::SP, ARC::ILINK, ARC::R30, ARC::BLINK}; + +static DecodeStatus DecodeGPR32RegisterClass(MCInst &Inst, unsigned RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo >= 32) { + DEBUG(dbgs() << "Not a GPR32 register."); + return MCDisassembler::Fail; + } + unsigned Reg = GPR32DecoderTable[RegNo]; + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; +} + +#include "ARCGenDisassemblerTables.inc" + +static unsigned decodeCField(unsigned Insn) { + return fieldFromInstruction(Insn, 6, 6); +} + +static unsigned decodeBField(unsigned Insn) { + return (fieldFromInstruction(Insn, 12, 3) << 3) | + fieldFromInstruction(Insn, 24, 3); +} + +static unsigned decodeAField(unsigned Insn) { + return fieldFromInstruction(Insn, 0, 6); +} + +static MCDisassembler::DecodeStatus +DecodeMEMrs9(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Dec) { + // We have the 9-bit immediate in the low bits, 6-bit register in high bits. + unsigned S9 = Insn & 0x1ff; + unsigned R = (Insn & (0x7fff & ~0x1ff)) >> 9; + DecodeGPR32RegisterClass(Inst, R, Address, Dec); + Inst.addOperand(MCOperand::createImm(SignExtend32<9>(S9))); + return MCDisassembler::Success; +} + +static MCDisassembler::DecodeStatus DecodeS9Operand(MCInst &Inst, + unsigned InsnS9, + uint64_t Address, + const void *Decoder) { + Inst.addOperand(MCOperand::createImm(SignExtend32<9>(0x1ff & InsnS9))); + return MCDisassembler::Success; +} + +static MCDisassembler::DecodeStatus DecodeS12Operand(MCInst &Inst, + unsigned InsnS12, + uint64_t Address, + const void *Decoder) { + Inst.addOperand(MCOperand::createImm(SignExtend32<12>(0xfff & InsnS12))); + return MCDisassembler::Success; +} + +static MCDisassembler::DecodeStatus DecodeBranchTargetS9(MCInst &Inst, + unsigned S, + uint64_t Address, + const void *Decoder) { + Inst.addOperand(MCOperand::createImm(SignExtend32<9>(S))); + return MCDisassembler::Success; +} + +static MCDisassembler::DecodeStatus DecodeBranchTargetS21(MCInst &Inst, + unsigned S, + uint64_t Address, + const void *Decoder) { + Inst.addOperand(MCOperand::createImm(SignExtend32<21>(S))); + return MCDisassembler::Success; +} + +static MCDisassembler::DecodeStatus DecodeBranchTargetS25(MCInst &Inst, + unsigned S, + uint64_t Address, + const void *Decoder) { + Inst.addOperand(MCOperand::createImm(SignExtend32<25>(S))); + return MCDisassembler::Success; +} + +static MCDisassembler::DecodeStatus +DecodeStLImmInstruction(MCInst &Inst, uint64_t Insn, uint64_t Address, + const void *Decoder) { + unsigned SrcC, DstB, LImm; + DstB = decodeBField(Insn); + if (DstB != 62) { + DEBUG(dbgs() << "Decoding StLImm found non-limm register."); + return MCDisassembler::Fail; + } + SrcC = decodeCField(Insn); + DecodeGPR32RegisterClass(Inst, SrcC, Address, Decoder); + LImm = (Insn >> 32); + Inst.addOperand(MCOperand::createImm(LImm)); + Inst.addOperand(MCOperand::createImm(0)); + return MCDisassembler::Success; +} + +static MCDisassembler::DecodeStatus +DecodeLdLImmInstruction(MCInst &Inst, uint64_t Insn, uint64_t Address, + const void *Decoder) { + unsigned DstA, SrcB, LImm; + DEBUG(dbgs() << "Decoding LdLImm:\n"); + SrcB = decodeBField(Insn); + if (SrcB != 62) { + DEBUG(dbgs() << "Decoding LdLImm found non-limm register."); + return MCDisassembler::Fail; + } + DstA = decodeAField(Insn); + DecodeGPR32RegisterClass(Inst, DstA, Address, Decoder); + LImm = (Insn >> 32); + Inst.addOperand(MCOperand::createImm(LImm)); + Inst.addOperand(MCOperand::createImm(0)); + return MCDisassembler::Success; +} + +static MCDisassembler::DecodeStatus +DecodeLdRLImmInstruction(MCInst &Inst, uint64_t Insn, uint64_t Address, + const void *Decoder) { + unsigned DstA, SrcB; + DEBUG(dbgs() << "Decoding LdRLimm\n"); + DstA = decodeAField(Insn); + DecodeGPR32RegisterClass(Inst, DstA, Address, Decoder); + SrcB = decodeBField(Insn); + DecodeGPR32RegisterClass(Inst, SrcB, Address, Decoder); + if (decodeCField(Insn) != 62) { + DEBUG(dbgs() << "Decoding LdRLimm found non-limm register."); + return MCDisassembler::Fail; + } + Inst.addOperand(MCOperand::createImm((uint32_t)(Insn >> 32))); + return MCDisassembler::Success; +} + +MCDisassembler::DecodeStatus ARCDisassembler::getInstruction( + MCInst &Instr, uint64_t &Size, ArrayRef Bytes, uint64_t Address, + raw_ostream &vStream, raw_ostream &cStream) const { + MCDisassembler::DecodeStatus Result; + if (Bytes.size() < 2) { + Size = 0; + return Fail; + } + uint8_t DecodeByte = (Bytes[1] & 0xF7) >> 3; + // 0x00 -> 0x07 are 32-bit instructions. + // 0x08 -> 0x1F are 16-bit instructions. + if (DecodeByte < 0x08) { + // 32-bit instruction. + if (Bytes.size() < 4) { + // Did we decode garbage? + Size = 0; + return Fail; + } + if (Bytes.size() >= 8) { + // Attempt to decode 64-bit instruction. + uint64_t Insn64; + if (!readInstruction64(Bytes, Address, Size, Insn64)) + return Fail; + Result = + decodeInstruction(DecoderTable64, Instr, Insn64, Address, this, STI); + if (Result == MCDisassembler::Success) { + DEBUG(dbgs() << "Successfully decoded 64-bit instruction."); + return MCDisassembler::Success; + } + DEBUG(dbgs() << "Not a 64-bit instruction, falling back to 32-bit."); + } + uint32_t Insn32; + if (!readInstruction32(Bytes, Address, Size, Insn32)) { + return Fail; + } + // Calling the auto-generated decoder function. + return decodeInstruction(DecoderTable32, Instr, Insn32, Address, this, STI); + } + + // 16-bit instruction. + uint32_t Insn16; + if (!readInstruction16(Bytes, Address, Size, Insn16)) { + return Fail; + } + // Calling the auto-generated decoder function. + return decodeInstruction(DecoderTable16, Instr, Insn16, Address, this, STI); +} + +static MCDisassembler *createARCDisassembler(const Target &T, + const MCSubtargetInfo &STI, + MCContext &Ctx) { + return new ARCDisassembler(STI, Ctx, T.createMCInstrInfo()); +} + +extern "C" void LLVMInitializeARCDisassembler() { + // Register the disassembler. + TargetRegistry::RegisterMCDisassembler(getTheARCTarget(), + createARCDisassembler); +} diff --git a/lib/Target/ARC/Disassembler/CMakeLists.txt b/lib/Target/ARC/Disassembler/CMakeLists.txt new file mode 100644 index 00000000000..b692c4baf58 --- /dev/null +++ b/lib/Target/ARC/Disassembler/CMakeLists.txt @@ -0,0 +1,3 @@ +add_llvm_library(LLVMARCDisassembler + ARCDisassembler.cpp + ) diff --git a/lib/Target/ARC/Disassembler/LLVMBuild.txt b/lib/Target/ARC/Disassembler/LLVMBuild.txt new file mode 100644 index 00000000000..fd77f0ba08f --- /dev/null +++ b/lib/Target/ARC/Disassembler/LLVMBuild.txt @@ -0,0 +1,23 @@ +;===- ./lib/Target/ARC/Disassembler/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 = ARCDisassembler +parent = ARC +required_libraries = MCDisassembler Support ARCInfo +add_to_library_groups = ARC diff --git a/lib/Target/ARC/InstPrinter/ARCInstPrinter.cpp b/lib/Target/ARC/InstPrinter/ARCInstPrinter.cpp new file mode 100644 index 00000000000..d4f1046db12 --- /dev/null +++ b/lib/Target/ARC/InstPrinter/ARCInstPrinter.cpp @@ -0,0 +1,166 @@ +//===- ARCInstPrinter.cpp - ARC MCInst to assembly syntax -------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class prints an ARC MCInst to a .s file. +// +//===----------------------------------------------------------------------===// + +#include "ARCInstPrinter.h" +#include "MCTargetDesc/ARCInfo.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCSymbol.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +#define DEBUG_TYPE "asm-printer" + +#include "ARCGenAsmWriter.inc" + +static const char *ARCBRCondCodeToString(ARCCC::BRCondCode BRCC) { + switch (BRCC) { + case ARCCC::BREQ: + return "eq"; + case ARCCC::BRNE: + return "ne"; + case ARCCC::BRLT: + return "lt"; + case ARCCC::BRGE: + return "ge"; + case ARCCC::BRLO: + return "lo"; + case ARCCC::BRHS: + return "hs"; + default: + llvm_unreachable("Unhandled ARCCC::BRCondCode"); + } +} + +static const char *ARCCondCodeToString(ARCCC::CondCode CC) { + switch (CC) { + case ARCCC::EQ: + return "eq"; + case ARCCC::NE: + return "ne"; + case ARCCC::P: + return "p"; + case ARCCC::N: + return "n"; + case ARCCC::HS: + return "hs"; + case ARCCC::LO: + return "lo"; + case ARCCC::GT: + return "gt"; + case ARCCC::GE: + return "ge"; + case ARCCC::LT: + return "lt"; + case ARCCC::LE: + return "le"; + case ARCCC::HI: + return "hi"; + case ARCCC::LS: + return "ls"; + case ARCCC::PNZ: + return "pnz"; + case ARCCC::AL: + return "al"; + case ARCCC::NZ: + return "nz"; + case ARCCC::Z: + return "z"; + } + llvm_unreachable("Unhandled ARCCC::CondCode"); +} + +void ARCInstPrinter::printRegName(raw_ostream &OS, unsigned RegNo) const { + OS << StringRef(getRegisterName(RegNo)).lower(); +} + +void ARCInstPrinter::printInst(const MCInst *MI, raw_ostream &O, + StringRef Annot, const MCSubtargetInfo &STI) { + printInstruction(MI, O); + printAnnotation(O, Annot); +} + +static void printExpr(const MCExpr *Expr, const MCAsmInfo *MAI, + raw_ostream &OS) { + int Offset = 0; + const MCSymbolRefExpr *SRE; + + if (const auto *BE = dyn_cast(Expr)) { + SRE = dyn_cast(BE->getLHS()); + const auto *CE = dyn_cast(BE->getRHS()); + assert(SRE && CE && "Binary expression must be sym+const."); + Offset = CE->getValue(); + } else { + SRE = dyn_cast(Expr); + assert(SRE && "Unexpected MCExpr type."); + } + assert(SRE->getKind() == MCSymbolRefExpr::VK_None); + + // Symbols are prefixed with '@' + OS << '@'; + SRE->getSymbol().print(OS, MAI); + + if (Offset) { + if (Offset > 0) + OS << '+'; + OS << Offset; + } +} + +void ARCInstPrinter::printOperand(const MCInst *MI, unsigned OpNum, + raw_ostream &O) { + const MCOperand &Op = MI->getOperand(OpNum); + if (Op.isReg()) { + printRegName(O, Op.getReg()); + return; + } + + if (Op.isImm()) { + O << Op.getImm(); + return; + } + + assert(Op.isExpr() && "unknown operand kind in printOperand"); + printExpr(Op.getExpr(), &MAI, O); +} + +void ARCInstPrinter::printMemOperandRI(const MCInst *MI, unsigned OpNum, + raw_ostream &O) { + const MCOperand &base = MI->getOperand(OpNum); + const MCOperand &offset = MI->getOperand(OpNum + 1); + assert(base.isReg() && "Base should be register."); + assert(offset.isImm() && "Offset should be immediate."); + printRegName(O, base.getReg()); + O << "," << offset.getImm(); +} + +void ARCInstPrinter::printPredicateOperand(const MCInst *MI, unsigned OpNum, + raw_ostream &O) { + + const MCOperand &Op = MI->getOperand(OpNum); + assert(Op.isImm() && "Predicate operand is immediate."); + O << ARCCondCodeToString((ARCCC::CondCode)Op.getImm()); +} + +void ARCInstPrinter::printBRCCPredicateOperand(const MCInst *MI, unsigned OpNum, + raw_ostream &O) { + const MCOperand &Op = MI->getOperand(OpNum); + assert(Op.isImm() && "Predicate operand is immediate."); + O << ARCBRCondCodeToString((ARCCC::BRCondCode)Op.getImm()); +} diff --git a/lib/Target/ARC/InstPrinter/ARCInstPrinter.h b/lib/Target/ARC/InstPrinter/ARCInstPrinter.h new file mode 100644 index 00000000000..e26c08104e2 --- /dev/null +++ b/lib/Target/ARC/InstPrinter/ARCInstPrinter.h @@ -0,0 +1,46 @@ +//===- ARCInstPrinter.h - Convert ARC MCInst to assembly syntax -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief This file contains the declaration of the ARCInstPrinter class, +/// which is used to print ARC MCInst to a .s file. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_ARC_INSTPRINTER_ARCINSTPRINTER_H +#define LLVM_LIB_TARGET_ARC_INSTPRINTER_ARCINSTPRINTER_H + +#include "llvm/MC/MCInstPrinter.h" + +namespace llvm { + +class ARCInstPrinter : public MCInstPrinter { +public: + ARCInstPrinter(const MCAsmInfo &MAI, const MCInstrInfo &MII, + const MCRegisterInfo &MRI) + : MCInstPrinter(MAI, MII, MRI) {} + + // Autogenerated by tblgen. + void printInstruction(const MCInst *MI, raw_ostream &O); + static const char *getRegisterName(unsigned RegNo); + + void printRegName(raw_ostream &OS, unsigned RegNo) const override; + void printInst(const MCInst *MI, raw_ostream &O, StringRef Annot, + const MCSubtargetInfo &STI) override; + +private: + void printMemOperandRI(const MCInst *MI, unsigned OpNum, raw_ostream &O); + void printOperand(const MCInst *MI, unsigned OpNum, raw_ostream &O); + void printPredicateOperand(const MCInst *MI, unsigned OpNum, raw_ostream &O); + void printBRCCPredicateOperand(const MCInst *MI, unsigned OpNum, + raw_ostream &O); +}; +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_ARC_INSTPRINTER_ARCINSTPRINTER_H diff --git a/lib/Target/ARC/InstPrinter/CMakeLists.txt b/lib/Target/ARC/InstPrinter/CMakeLists.txt new file mode 100644 index 00000000000..fd6b469cdfb --- /dev/null +++ b/lib/Target/ARC/InstPrinter/CMakeLists.txt @@ -0,0 +1,3 @@ +add_llvm_library(LLVMARCAsmPrinter + ARCInstPrinter.cpp + ) diff --git a/lib/Target/ARC/InstPrinter/LLVMBuild.txt b/lib/Target/ARC/InstPrinter/LLVMBuild.txt new file mode 100644 index 00000000000..6718b48652c --- /dev/null +++ b/lib/Target/ARC/InstPrinter/LLVMBuild.txt @@ -0,0 +1,23 @@ +;===- ./lib/Target/ARC/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 = ARCAsmPrinter +parent = ARC +required_libraries = MC Support +add_to_library_groups = ARC diff --git a/lib/Target/ARC/LLVMBuild.txt b/lib/Target/ARC/LLVMBuild.txt new file mode 100644 index 00000000000..4889f1141fb --- /dev/null +++ b/lib/Target/ARC/LLVMBuild.txt @@ -0,0 +1,45 @@ +;===- ./lib/Target/ARC/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 = Disassembler InstPrinter MCTargetDesc TargetInfo + +[component_0] +type = TargetGroup +name = ARC +parent = Target +has_asmprinter = 1 +has_disassembler = 1 + +[component_1] +type = Library +name = ARCCodeGen +parent = ARC +required_libraries = + Analysis + AsmPrinter + CodeGen + Core + MC + SelectionDAG + Support + Target + TransformUtils + ARCAsmPrinter + ARCDesc + ARCInfo +add_to_library_groups = ARC diff --git a/lib/Target/ARC/MCTargetDesc/ARCInfo.h b/lib/Target/ARC/MCTargetDesc/ARCInfo.h new file mode 100644 index 00000000000..b9ed9988570 --- /dev/null +++ b/lib/Target/ARC/MCTargetDesc/ARCInfo.h @@ -0,0 +1,57 @@ +//===- ARCInfo.h - Additional ARC Info --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains small standalone helper functions and enum definitions for +// the ARC 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_LIB_TARGET_ARC_MCTARGETDESC_ARCINFO_H +#define LLVM_LIB_TARGET_ARC_MCTARGETDESC_ARCINFO_H + +namespace llvm { + +// Enums corresponding to ARC condition codes +namespace ARCCC { + +enum CondCode { + AL = 0x0, + EQ = 0x1, + NE = 0x2, + P = 0x3, + N = 0x4, + LO = 0x5, + HS = 0x6, + GT = 0x9, + GE = 0xa, + LT = 0xb, + LE = 0xc, + HI = 0xd, + LS = 0xe, + PNZ = 0xf, + Z = 0x11, // Low 4-bits = EQ + NZ = 0x12 // Low 4-bits = NE +}; + +enum BRCondCode { + BREQ = 0x0, + BRNE = 0x1, + BRLT = 0x2, + BRGE = 0x3, + BRLO = 0x4, + BRHS = 0x5 +}; + +} // end namespace ARCCC + +} // end namespace llvm + +#endif diff --git a/lib/Target/ARC/MCTargetDesc/ARCMCAsmInfo.cpp b/lib/Target/ARC/MCTargetDesc/ARCMCAsmInfo.cpp new file mode 100644 index 00000000000..5d3fb52cfb4 --- /dev/null +++ b/lib/Target/ARC/MCTargetDesc/ARCMCAsmInfo.cpp @@ -0,0 +1,32 @@ +//===- ARCMCAsmInfo.cpp - ARC asm properties --------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ARCMCAsmInfo.h" +using namespace llvm; + +void ARCMCAsmInfo::anchor() {} + +ARCMCAsmInfo::ARCMCAsmInfo(const Triple &TT) { + SupportsDebugInformation = true; + Data16bitsDirective = "\t.short\t"; + Data32bitsDirective = "\t.word\t"; + Data64bitsDirective = nullptr; + ZeroDirective = "\t.space\t"; + CommentString = ";"; + + UsesELFSectionDirectiveForBSS = true; + AllowAtInName = true; + HiddenVisibilityAttr = MCSA_Invalid; + HiddenDeclarationVisibilityAttr = MCSA_Invalid; + ProtectedVisibilityAttr = MCSA_Invalid; + + // Debug + ExceptionsType = ExceptionHandling::DwarfCFI; + DwarfRegNumForCFI = true; +} diff --git a/lib/Target/ARC/MCTargetDesc/ARCMCAsmInfo.h b/lib/Target/ARC/MCTargetDesc/ARCMCAsmInfo.h new file mode 100644 index 00000000000..997a370fee8 --- /dev/null +++ b/lib/Target/ARC/MCTargetDesc/ARCMCAsmInfo.h @@ -0,0 +1,32 @@ +//===- ARCMCAsmInfo.h - ARC 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 ARCMCAsmInfo class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_ARC_MCTARGETDESC_ARCMCASMINFO_H +#define LLVM_LIB_TARGET_ARC_MCTARGETDESC_ARCMCASMINFO_H + +#include "llvm/MC/MCAsmInfoELF.h" + +namespace llvm { + +class Triple; + +class ARCMCAsmInfo : public MCAsmInfoELF { + void anchor() override; + +public: + explicit ARCMCAsmInfo(const Triple &TT); +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_ARC_MCTARGETDESC_ARCMCASMINFO_H diff --git a/lib/Target/ARC/MCTargetDesc/ARCMCTargetDesc.cpp b/lib/Target/ARC/MCTargetDesc/ARCMCTargetDesc.cpp new file mode 100644 index 00000000000..17be15f730d --- /dev/null +++ b/lib/Target/ARC/MCTargetDesc/ARCMCTargetDesc.cpp @@ -0,0 +1,103 @@ +//===- ARCMCTargetDesc.cpp - ARC 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 ARC specific target descriptions. +// +//===----------------------------------------------------------------------===// + +#include "ARCMCTargetDesc.h" +#include "ARCMCAsmInfo.h" +#include "ARCTargetStreamer.h" +#include "InstPrinter/ARCInstPrinter.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FormattedStream.h" +#include "llvm/Support/TargetRegistry.h" + +using namespace llvm; + +#define GET_INSTRINFO_MC_DESC +#include "ARCGenInstrInfo.inc" + +#define GET_SUBTARGETINFO_MC_DESC +#include "ARCGenSubtargetInfo.inc" + +#define GET_REGINFO_MC_DESC +#include "ARCGenRegisterInfo.inc" + +static MCInstrInfo *createARCMCInstrInfo() { + auto *X = new MCInstrInfo(); + InitARCMCInstrInfo(X); + return X; +} + +static MCRegisterInfo *createARCMCRegisterInfo(const Triple &TT) { + auto *X = new MCRegisterInfo(); + InitARCMCRegisterInfo(X, ARC::BLINK); + return X; +} + +static MCSubtargetInfo *createARCMCSubtargetInfo(const Triple &TT, + StringRef CPU, StringRef FS) { + return createARCMCSubtargetInfoImpl(TT, CPU, FS); +} + +static MCAsmInfo *createARCMCAsmInfo(const MCRegisterInfo &MRI, + const Triple &TT) { + MCAsmInfo *MAI = new ARCMCAsmInfo(TT); + + // Initial state of the frame pointer is SP. + MCCFIInstruction Inst = MCCFIInstruction::createDefCfa(nullptr, ARC::SP, 0); + MAI->addInitialFrameState(Inst); + + return MAI; +} + +static MCInstPrinter *createARCMCInstPrinter(const Triple &T, + unsigned SyntaxVariant, + const MCAsmInfo &MAI, + const MCInstrInfo &MII, + const MCRegisterInfo &MRI) { + return new ARCInstPrinter(MAI, MII, MRI); +} + +ARCTargetStreamer::ARCTargetStreamer(MCStreamer &S) : MCTargetStreamer(S) {} +ARCTargetStreamer::~ARCTargetStreamer() = default; + +static MCTargetStreamer *createTargetAsmStreamer(MCStreamer &S, + formatted_raw_ostream &OS, + MCInstPrinter *InstPrint, + bool isVerboseAsm) { + return new ARCTargetStreamer(S); +} + +// Force static initialization. +extern "C" void LLVMInitializeARCTargetMC() { + // Register the MC asm info. + Target &TheARCTarget = getTheARCTarget(); + RegisterMCAsmInfoFn X(TheARCTarget, createARCMCAsmInfo); + + // Register the MC instruction info. + TargetRegistry::RegisterMCInstrInfo(TheARCTarget, createARCMCInstrInfo); + + // Register the MC register info. + TargetRegistry::RegisterMCRegInfo(TheARCTarget, createARCMCRegisterInfo); + + // Register the MC subtarget info. + TargetRegistry::RegisterMCSubtargetInfo(TheARCTarget, + createARCMCSubtargetInfo); + + // Register the MCInstPrinter + TargetRegistry::RegisterMCInstPrinter(TheARCTarget, createARCMCInstPrinter); + + TargetRegistry::RegisterAsmTargetStreamer(TheARCTarget, + createTargetAsmStreamer); +} diff --git a/lib/Target/ARC/MCTargetDesc/ARCMCTargetDesc.h b/lib/Target/ARC/MCTargetDesc/ARCMCTargetDesc.h new file mode 100644 index 00000000000..dd152a6a34f --- /dev/null +++ b/lib/Target/ARC/MCTargetDesc/ARCMCTargetDesc.h @@ -0,0 +1,39 @@ +//===- ARCMCTargetDesc.h - ARC 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 ARC specific target descriptions. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_ARC_MCTARGETDESC_ARCMCTARGETDESC_H +#define LLVM_LIB_TARGET_ARC_MCTARGETDESC_ARCMCTARGETDESC_H + +#include "llvm/Support/DataTypes.h" + +namespace llvm { + +class Target; + +Target &getTheARCTarget(); + +} // end namespace llvm + +// Defines symbolic names for ARC registers. This defines a mapping from +// register name to register number. +#define GET_REGINFO_ENUM +#include "ARCGenRegisterInfo.inc" + +// Defines symbolic names for the ARC instructions. +#define GET_INSTRINFO_ENUM +#include "ARCGenInstrInfo.inc" + +#define GET_SUBTARGETINFO_ENUM +#include "ARCGenSubtargetInfo.inc" + +#endif // LLVM_LIB_TARGET_ARC_MCTARGETDESC_ARCMCTARGETDESC_H diff --git a/lib/Target/ARC/MCTargetDesc/CMakeLists.txt b/lib/Target/ARC/MCTargetDesc/CMakeLists.txt new file mode 100644 index 00000000000..243198d253f --- /dev/null +++ b/lib/Target/ARC/MCTargetDesc/CMakeLists.txt @@ -0,0 +1,4 @@ +add_llvm_library(LLVMARCDesc + ARCMCTargetDesc.cpp + ARCMCAsmInfo.cpp + ) diff --git a/lib/Target/ARC/MCTargetDesc/LLVMBuild.txt b/lib/Target/ARC/MCTargetDesc/LLVMBuild.txt new file mode 100644 index 00000000000..9ee33b2d0b6 --- /dev/null +++ b/lib/Target/ARC/MCTargetDesc/LLVMBuild.txt @@ -0,0 +1,23 @@ +;===- ./lib/Target/ARC/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 = ARCDesc +parent = ARC +required_libraries = MC Support ARCAsmPrinter ARCInfo +add_to_library_groups = ARC diff --git a/lib/Target/ARC/TargetInfo/ARCTargetInfo.cpp b/lib/Target/ARC/TargetInfo/ARCTargetInfo.cpp new file mode 100644 index 00000000000..e94a373e34e --- /dev/null +++ b/lib/Target/ARC/TargetInfo/ARCTargetInfo.cpp @@ -0,0 +1,22 @@ +//===- ARCTargetInfo.cpp - ARC Target Implementation ----------- *- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ARC.h" +#include "llvm/Support/TargetRegistry.h" + +using namespace llvm; + +Target &llvm::getTheARCTarget() { + static Target TheARCTarget; + return TheARCTarget; +} + +extern "C" void LLVMInitializeARCTargetInfo() { + RegisterTarget X(getTheARCTarget(), "arc", "ARC"); +} diff --git a/lib/Target/ARC/TargetInfo/CMakeLists.txt b/lib/Target/ARC/TargetInfo/CMakeLists.txt new file mode 100644 index 00000000000..5f9b405481f --- /dev/null +++ b/lib/Target/ARC/TargetInfo/CMakeLists.txt @@ -0,0 +1,3 @@ +add_llvm_library(LLVMARCInfo + ARCTargetInfo.cpp + ) diff --git a/lib/Target/ARC/TargetInfo/LLVMBuild.txt b/lib/Target/ARC/TargetInfo/LLVMBuild.txt new file mode 100644 index 00000000000..bf352034879 --- /dev/null +++ b/lib/Target/ARC/TargetInfo/LLVMBuild.txt @@ -0,0 +1,23 @@ +;===- ./lib/Target/ARC/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 = ARCInfo +parent = ARC +required_libraries = Support +add_to_library_groups = ARC diff --git a/lib/Target/LLVMBuild.txt b/lib/Target/LLVMBuild.txt index 34b966df776..0d899a9c782 100644 --- a/lib/Target/LLVMBuild.txt +++ b/lib/Target/LLVMBuild.txt @@ -20,6 +20,7 @@ [common] subdirectories = AMDGPU + ARC ARM AArch64 AVR diff --git a/test/CodeGen/ARC/alu.ll b/test/CodeGen/ARC/alu.ll new file mode 100644 index 00000000000..a5e07636fec --- /dev/null +++ b/test/CodeGen/ARC/alu.ll @@ -0,0 +1,255 @@ +; RUN: llc -march=arc < %s | FileCheck %s + +; CHECK-LABEL: add_r +; CHECK: add %r0, %r{{[01]}}, %r{{[01]}} +define i32 @add_r(i32 %a, i32 %b) nounwind { +entry: + %v = add i32 %a, %b + ret i32 %v +} + +; CHECK-LABEL: add_u6 +; CHECK: add %r0, %r0, 15 +define i32 @add_u6(i32 %a) nounwind { + %v = add i32 %a, 15 + ret i32 %v +} + +; CHECK-LABEL: add_limm +; CHECK: add %r0, %r0, 12345 +define i32 @add_limm(i32 %a) nounwind { + %v = add i32 %a, 12345 + ret i32 %v +} + +; CHECK-LABEL: mpy_r +; CHECK: mpy %r0, %r{{[01]}}, %r{{[01]}} +define i32 @mpy_r(i32 %a, i32 %b) nounwind { +entry: + %v = mul i32 %a, %b + ret i32 %v +} + +; CHECK-LABEL: mpy_u6 +; CHECK: mpy %r0, %r0, 10 +define i32 @mpy_u6(i32 %a) nounwind { + %v = mul i32 %a, 10 + ret i32 %v +} + +; CHECK-LABEL: mpy_limm +; CHECK: mpy %r0, %r0, 12345 +define i32 @mpy_limm(i32 %a) nounwind { + %v = mul i32 %a, 12345 + ret i32 %v +} + +; CHECK-LABEL: max_r +; CHECK: max %r0, %r{{[01]}}, %r{{[01]}} +define i32 @max_r(i32 %a, i32 %b) nounwind { + %i = icmp sgt i32 %a, %b + %v = select i1 %i, i32 %a, i32 %b + ret i32 %v +} + +; CHECK-LABEL: max_u6 +; CHECK: max %r0, %r0, 12 +define i32 @max_u6(i32 %a) nounwind { + %i = icmp sgt i32 %a, 12 + %v = select i1 %i, i32 %a, i32 12 + ret i32 %v +} + +; CHECK-LABEL: max_limm +; CHECK: max %r0, %r0, 2345 +define i32 @max_limm(i32 %a) nounwind { + %i = icmp sgt i32 %a, 2345 + %v = select i1 %i, i32 %a, i32 2345 + ret i32 %v +} + +; CHECK-LABEL: min_r +; CHECK: min %r0, %r{{[01]}}, %r{{[01]}} +define i32 @min_r(i32 %a, i32 %b) nounwind { + %i = icmp slt i32 %a, %b + %v = select i1 %i, i32 %a, i32 %b + ret i32 %v +} + +; CHECK-LABEL: min_u6 +; CHECK: min %r0, %r0, 20 +define i32 @min_u6(i32 %a) nounwind { + %i = icmp slt i32 %a, 20 + %v = select i1 %i, i32 %a, i32 20 + ret i32 %v +} + +; CHECK-LABEL: min_limm +; CHECK: min %r0, %r0, 2040 +define i32 @min_limm(i32 %a) nounwind { + %i = icmp slt i32 %a, 2040 + %v = select i1 %i, i32 %a, i32 2040 + ret i32 %v +} + +; CHECK-LABEL: and_r +; CHECK: and %r0, %r{{[01]}}, %r{{[01]}} +define i32 @and_r(i32 %a, i32 %b) nounwind { + %v = and i32 %a, %b + ret i32 %v +} + +; CHECK-LABEL: and_u6 +; CHECK: and %r0, %r0, 7 +define i32 @and_u6(i32 %a) nounwind { + %v = and i32 %a, 7 + ret i32 %v +} + +; 0xfffff == 1048575 +; CHECK-LABEL: and_limm +; CHECK: and %r0, %r0, 1048575 +define i32 @and_limm(i32 %a) nounwind { + %v = and i32 %a, 1048575 + ret i32 %v +} + +; CHECK-LABEL: or_r +; CHECK: or %r0, %r{{[01]}}, %r{{[01]}} +define i32 @or_r(i32 %a, i32 %b) nounwind { + %v = or i32 %a, %b + ret i32 %v +} + +; CHECK-LABEL: or_u6 +; CHECK: or %r0, %r0, 7 +define i32 @or_u6(i32 %a) nounwind { + %v = or i32 %a, 7 + ret i32 %v +} + +; 0xf0f0f == 986895 +; CHECK-LABEL: or_limm +define i32 @or_limm(i32 %a) nounwind { + %v = or i32 %a, 986895 + ret i32 %v +} + +; CHECK-LABEL: xor_r +; CHECK: xor %r0, %r{{[01]}}, %r{{[01]}} +define i32 @xor_r(i32 %a, i32 %b) nounwind { + %v = xor i32 %a, %b + ret i32 %v +} + +; CHECK-LABEL: xor_u6 +; CHECK: xor %r0, %r0, 3 +define i32 @xor_u6(i32 %a) nounwind { + %v = xor i32 %a, 3 + ret i32 %v +} + +; CHECK-LABEL: xor_limm +; CHECK: xor %r0, %r0, 986895 +define i32 @xor_limm(i32 %a) nounwind { + %v = xor i32 %a, 986895 + ret i32 %v +} + +; CHECK-LABEL: asl_r +; CHECK: asl %r0, %r{{[01]}}, %r{{[01]}} +define i32 @asl_r(i32 %a, i32 %b) nounwind { + %v = shl i32 %a, %b + ret i32 %v +} + +; CHECK-LABEL: asl_u6 +; CHECK: asl %r0, %r0, 4 +define i32 @asl_u6(i32 %a) nounwind { + %v = shl i32 %a, 4 + ret i32 %v +} + +; CHECK-LABEL: lsr_r +; CHECK: lsr %r0, %r{{[01]}}, %r{{[01]}} +define i32 @lsr_r(i32 %a, i32 %b) nounwind { + %v = lshr i32 %a, %b + ret i32 %v +} + +; CHECK-LABEL: lsr_u6 +; CHECK: lsr %r0, %r0, 6 +define i32 @lsr_u6(i32 %a) nounwind { + %v = lshr i32 %a, 6 + ret i32 %v +} + +; CHECK-LABEL: asr_r +; CHECK: asr %r0, %r{{[01]}}, %r{{[01]}} +define i32 @asr_r(i32 %a, i32 %b) nounwind { + %v = ashr i32 %a, %b + ret i32 %v +} + +; CHECK-LABEL: asr_u6 +; CHECK: asr %r0, %r0, 8 +define i32 @asr_u6(i32 %a) nounwind { + %v = ashr i32 %a, 8 + ret i32 %v +} + +; CHECK-LABEL: ror_r +; CHECK: ror %r0, %r{{[01]}}, %r{{[01]}} +define i32 @ror_r(i32 %a, i32 %b) nounwind { + %v1 = lshr i32 %a, %b + %ls = sub i32 32, %b + %v2 = shl i32 %a, %ls + %v = or i32 %v1, %v2 + ret i32 %v +} + +; CHECK-LABEL: ror_u6 +; CHECK: ror %r0, %r0, 10 +define i32 @ror_u6(i32 %a) nounwind { + %v1 = lshr i32 %a, 10 + %v2 = shl i32 %a, 22 + %v = or i32 %v1, %v2 + ret i32 %v +} + +; CHECK-LABEL: sexh_r +; CHECK: sexh %r0, %r0 +define i32 @sexh_r(i32 %a) nounwind { + %v1 = shl i32 %a, 16 + %v = ashr i32 %v1, 16 + ret i32 %v +} + +; CHECK-LABEL: sexb_r +; CHECK: sexb %r0, %r0 +define i32 @sexb_r(i32 %a) nounwind { + %v1 = shl i32 %a, 24 + %v = ashr i32 %v1, 24 + ret i32 %v +} + +; CHECK-LABEL: mulu64 +; CHECK-DAG: mpy %r[[REG:[0-9]+]], %r{{[01]}}, %r{{[01]}} +; CHECK-DAG: mpymu %r[[REG:[0-9]+]], %r{{[01]}}, %r{{[01]}} +define i64 @mulu64(i32 %a, i32 %b) nounwind { + %a64 = zext i32 %a to i64 + %b64 = zext i32 %b to i64 + %v = mul i64 %a64, %b64 + ret i64 %v +} + +; CHECK-LABEL: muls64 +; CHECK-DAG: mpy %r[[REG:[0-9]+]], %r{{[01]}}, %r{{[01]}} +; CHECK-DAG: mpym %r[[REG:[0-9]+]], %r{{[01]}}, %r{{[01]}} +define i64 @muls64(i32 %a, i32 %b) nounwind { + %a64 = sext i32 %a to i64 + %b64 = sext i32 %b to i64 + %v = mul i64 %a64, %b64 + ret i64 %v +} + diff --git a/test/CodeGen/ARC/brcc.ll b/test/CodeGen/ARC/brcc.ll new file mode 100644 index 00000000000..ca4035e3598 --- /dev/null +++ b/test/CodeGen/ARC/brcc.ll @@ -0,0 +1,37 @@ +; RUN: llc -march=arc < %s | FileCheck %s + +; CHECK-LABEL: brcc1 +; CHECK: brne %r0, %r1 +define i32 @brcc1(i32 %a, i32 %b) nounwind { +entry: + %wb = icmp eq i32 %a, %b + br i1 %wb, label %t1, label %t2 +t1: + %t1v = add i32 %a, 4 + br label %exit +t2: + %t2v = add i32 %b, 8 + br label %exit +exit: + %v = phi i32 [ %t1v, %t1 ], [ %t2v, %t2 ] + ret i32 %v +} + +; CHECK-LABEL: brcc2 +; CHECK: breq %r0, %r1 +define i32 @brcc2(i32 %a, i32 %b) nounwind { +entry: + %wb = icmp ne i32 %a, %b + br i1 %wb, label %t1, label %t2 +t1: + %t1v = add i32 %a, 4 + br label %exit +t2: + %t2v = add i32 %b, 8 + br label %exit +exit: + %v = phi i32 [ %t1v, %t1 ], [ %t2v, %t2 ] + ret i32 %v +} + + diff --git a/test/CodeGen/ARC/call.ll b/test/CodeGen/ARC/call.ll new file mode 100644 index 00000000000..2985439b5f1 --- /dev/null +++ b/test/CodeGen/ARC/call.ll @@ -0,0 +1,88 @@ +; RUN: llc -march=arc < %s | FileCheck %s + + +declare i32 @goo1(i32) nounwind + +; CHECK-LABEL: call1 +; CHECK: bl @goo1 +define i32 @call1(i32 %a) nounwind { +entry: + %x = call i32 @goo1(i32 %a) + ret i32 %x +} + +declare i32 @goo2(i32, i32, i32, i32, i32, i32, i32, i32) nounwind + +; CHECK-LABEL: call2 +; CHECK-DAG: mov %r0, 0 +; CHECK-DAG: mov %r1, 1 +; CHECK-DAG: mov %r2, 2 +; CHECK-DAG: mov %r3, 3 +; CHECK-DAG: mov %r4, 4 +; CHECK-DAG: mov %r5, 5 +; CHECK-DAG: mov %r6, 6 +; CHECK-DAG: mov %r7, 7 +; CHECK: bl @goo2 +define i32 @call2() nounwind { +entry: + %x = call i32 @goo2(i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7) + ret i32 %x +} + +declare i32 @goo3(i64, i32, i64) nounwind +; call goo3(0xEEEEEEEE77777777, 0x55555555, 0xAAAAAAAA33333333) +; 0xEEEEEEEE == -286331154 +; 0x77777777 == 2004318071 +; 0x55555555 == 1431655765 +; 0xAAAAAAAA == -1431655766 +; 0x33333333 == 858993459 +; CHECK-LABEL: call3 +; CHECK-DAG: mov %r0, 2004318071 +; CHECK-DAG: mov %r1, -286331154 +; CHECK-DAG: mov %r2, 1431655765 +; CHECK-DAG: mov %r3, 858993459 +; CHECK-DAG: mov %r4, -1431655766 +; CHECK: bl @goo3 +define i32 @call3() nounwind { +entry: + %x = call i32 @goo3(i64 17216961133457930103, + i32 1431655765, + i64 12297829380468716339) + ret i32 %x +} + +declare i64 @goo4() + +; 64-bit values are returned in r0r1 +; CHECK-LABEL: call4 +; CHECK: bl @goo4 +; CHECK: lsr %r0, %r1, 16 +define i32 @call4() nounwind { + %x = call i64 @goo4() + %v1 = lshr i64 %x, 48 + %v = trunc i64 %v1 to i32 + ret i32 %v +} + +; 0x0000ffff00ff00ff=281470698455295 +; returned as r0=0x00ff00ff=16711935, r1=0x0000ffff=65535 +; CHECK-LABEL: ret1 +; CHECK-DAG: mov %r1, 65535 +; CHECK-DAG: mov %r0, 16711935 +define i64 @ret1() nounwind { + ret i64 281470698455295 +} + +@funcptr = external global i32 (i32)*, align 4 +; Indirect calls use JL +; CHECK-LABEL: call_indirect +; CHECK-DAG: ld %r[[REG:[0-9]+]], [@funcptr] +; CHECK-DAG: mov %r0, 12 +; CHECK: jl [%r[[REG]]] +define i32 @call_indirect(i32 %x) nounwind { + %f = load i32 (i32)*, i32 (i32)** @funcptr, align 4 + %call = call i32 %f(i32 12) + %add = add nsw i32 %call, %x + ret i32 %add +} + diff --git a/test/CodeGen/ARC/ldst.ll b/test/CodeGen/ARC/ldst.ll new file mode 100644 index 00000000000..67f938a652c --- /dev/null +++ b/test/CodeGen/ARC/ldst.ll @@ -0,0 +1,272 @@ +; RUN: llc -march=arc < %s | FileCheck %s + +; CHECK-LABEL: load32 +; CHECK: ld %r0, [%r0,16000] + +define i32 @load32(i32* %bp) nounwind { +entry: + %gep = getelementptr i32, i32* %bp, i32 4000 + %v = load i32, i32* %gep, align 4 + ret i32 %v +} + +; CHECK-LABEL: load16 +; CHECK: ldh %r0, [%r0,8000] + +define i16 @load16(i16* %bp) nounwind { +entry: + %gep = getelementptr i16, i16* %bp, i32 4000 + %v = load i16, i16* %gep, align 2 + ret i16 %v +} + +; CHECK-LABEL: load8 +; CHECK: ldb %r0, [%r0,4000] + +define i8 @load8(i8* %bp) nounwind { +entry: + %gep = getelementptr i8, i8* %bp, i32 4000 + %v = load i8, i8* %gep, align 1 + ret i8 %v +} + +; CHECK-LABEL: sextload16 +; CHECK: ldh.x %r0, [%r0,8000] + +define i32 @sextload16(i16* %bp) nounwind { +entry: + %gep = getelementptr i16, i16* %bp, i32 4000 + %vl = load i16, i16* %gep, align 2 + %v = sext i16 %vl to i32 + ret i32 %v +} + +; CHECK-LABEL: sextload8 +; CHECK: ldb.x %r0, [%r0,4000] + +define i32 @sextload8(i8* %bp) nounwind { +entry: + %gep = getelementptr i8, i8* %bp, i32 4000 + %vl = load i8, i8* %gep, align 1 + %v = sext i8 %vl to i32 + ret i32 %v +} + +; CHECK-LABEL: s_sextload16 +; CHECK: ldh.x %r0, [%r0,32] + +define i32 @s_sextload16(i16* %bp) nounwind { +entry: + %gep = getelementptr i16, i16* %bp, i32 16 + %vl = load i16, i16* %gep, align 2 + %v = sext i16 %vl to i32 + ret i32 %v +} + +; CHECK-LABEL: s_sextload8 +; CHECK: ldb.x %r0, [%r0,16] + +define i32 @s_sextload8(i8* %bp) nounwind { +entry: + %gep = getelementptr i8, i8* %bp, i32 16 + %vl = load i8, i8* %gep, align 1 + %v = sext i8 %vl to i32 + ret i32 %v +} + +; CHECK-LABEL: store32 +; CHECK: add %r[[REG:[0-9]+]], %r1, 16000 +; CHECK: st %r0, [%r[[REG]],0] + +; Long range stores (offset does not fit in s9) must be add followed by st. +define void @store32(i32 %val, i32* %bp) nounwind { +entry: + %gep = getelementptr i32, i32* %bp, i32 4000 + store i32 %val, i32* %gep, align 4 + ret void +} + +; CHECK-LABEL: store16 +; CHECK: add %r[[REG:[0-9]+]], %r1, 8000 +; CHECK: sth %r0, [%r[[REG]],0] + +define void @store16(i16 zeroext %val, i16* %bp) nounwind { +entry: + %gep = getelementptr i16, i16* %bp, i32 4000 + store i16 %val, i16* %gep, align 2 + ret void +} + +; CHECK-LABEL: store8 +; CHECK: add %r[[REG:[0-9]+]], %r1, 4000 +; CHECK: stb %r0, [%r[[REG]],0] + +define void @store8(i8 zeroext %val, i8* %bp) nounwind { +entry: + %gep = getelementptr i8, i8* %bp, i32 4000 + store i8 %val, i8* %gep, align 1 + ret void +} + +; Short range stores can be done with [reg, s9]. +; CHECK-LABEL: s_store32 +; CHECK-NOT: add +; CHECK: st %r0, [%r1,64] +define void @s_store32(i32 %val, i32* %bp) nounwind { +entry: + %gep = getelementptr i32, i32* %bp, i32 16 + store i32 %val, i32* %gep, align 4 + ret void +} + +; CHECK-LABEL: s_store16 +; CHECK-NOT: add +; CHECK: sth %r0, [%r1,32] +define void @s_store16(i16 zeroext %val, i16* %bp) nounwind { +entry: + %gep = getelementptr i16, i16* %bp, i32 16 + store i16 %val, i16* %gep, align 2 + ret void +} + +; CHECK-LABEL: s_store8 +; CHECK-NOT: add +; CHECK: stb %r0, [%r1,16] +define void @s_store8(i8 zeroext %val, i8* %bp) nounwind { +entry: + %gep = getelementptr i8, i8* %bp, i32 16 + store i8 %val, i8* %gep, align 1 + ret void +} + + +@aaaa = internal global [128 x i32] zeroinitializer +@bbbb = internal global [128 x i16] zeroinitializer +@cccc = internal global [128 x i8] zeroinitializer + +; CHECK-LABEL: g_store32 +; CHECK-NOT: add +; CHECK: st %r0, [@aaaa+64] +define void @g_store32(i32 %val) nounwind { +entry: + store i32 %val, i32* getelementptr inbounds ([128 x i32], [128 x i32]* @aaaa, i32 0, i32 16), align 4 + ret void +} + +; CHECK-LABEL: g_load32 +; CHECK-NOT: add +; CHECK: ld %r0, [@aaaa+64] +define i32 @g_load32() nounwind { + %gep = getelementptr inbounds [128 x i32], [128 x i32]* @aaaa, i32 0, i32 16 + %v = load i32, i32* %gep, align 4 + ret i32 %v +} + +; CHECK-LABEL: g_store16 +; CHECK-NOT: add +; CHECK: sth %r0, [@bbbb+32] +define void @g_store16(i16 %val) nounwind { +entry: + store i16 %val, i16* getelementptr inbounds ([128 x i16], [128 x i16]* @bbbb, i16 0, i16 16), align 2 + ret void +} + +; CHECK-LABEL: g_load16 +; CHECK-NOT: add +; CHECK: ldh %r0, [@bbbb+32] +define i16 @g_load16() nounwind { + %gep = getelementptr inbounds [128 x i16], [128 x i16]* @bbbb, i16 0, i16 16 + %v = load i16, i16* %gep, align 2 + ret i16 %v +} + +; CHECK-LABEL: g_store8 +; CHECK-NOT: add +; CHECK: stb %r0, [@cccc+16] +define void @g_store8(i8 %val) nounwind { +entry: + store i8 %val, i8* getelementptr inbounds ([128 x i8], [128 x i8]* @cccc, i8 0, i8 16), align 1 + ret void +} + +; CHECK-LABEL: g_load8 +; CHECK-NOT: add +; CHECK: ldb %r0, [@cccc+16] +define i8 @g_load8() nounwind { + %gep = getelementptr inbounds [128 x i8], [128 x i8]* @cccc, i8 0, i8 16 + %v = load i8, i8* %gep, align 1 + ret i8 %v +} + +; CHECK-LABEL: align2_load32 +; CHECK-DAG: ldh %r[[REG0:[0-9]+]], [%r0,0] +; CHECK-DAG: ldh %r[[REG1:[0-9]+]], [%r0,2] +; CHECK-DAG: asl %r[[REG2:[0-9]+]], %r[[REG1]], 16 +define i32 @align2_load32(i8* %p) nounwind { +entry: + %bp = bitcast i8* %p to i32* + %v = load i32, i32* %bp, align 2 + ret i32 %v +} + +; CHECK-LABEL: align1_load32 +; CHECK-DAG: ldb %r[[REG0:[0-9]+]], [%r0,0] +; CHECK-DAG: ldb %r[[REG1:[0-9]+]], [%r0,1] +; CHECK-DAG: ldb %r[[REG2:[0-9]+]], [%r0,2] +; CHECK-DAG: ldb %r[[REG3:[0-9]+]], [%r0,3] +; CHECK-DAG: asl %r[[AREG1:[0-9]+]], %r[[REG1]], 8 +; CHECK-DAG: asl %r[[AREG3:[0-9]+]], %r[[REG3]], 8 +define i32 @align1_load32(i8* %p) nounwind { +entry: + %bp = bitcast i8* %p to i32* + %v = load i32, i32* %bp, align 1 + ret i32 %v +} + +; CHECK-LABEL: align1_load16 +; CHECK-DAG: ldb %r[[REG0:[0-9]+]], [%r0,0] +; CHECK-DAG: ldb %r[[REG1:[0-9]+]], [%r0,1] +; CHECK-DAG: asl %r[[REG2:[0-9]+]], %r[[REG1]], 8 +define i16 @align1_load16(i8* %p) nounwind { +entry: + %bp = bitcast i8* %p to i16* + %v = load i16, i16* %bp, align 1 + ret i16 %v +} + +; CHECK-LABEL: align2_store32 +; CHECK-DAG: lsr %r[[REG:[0-9]+]], %r1, 16 +; CHECK-DAG: sth %r1, [%r0,0] +; CHECK-DAG: sth %r[[REG:[0-9]+]], [%r0,2] +define void @align2_store32(i8* %p, i32 %v) nounwind { +entry: + %bp = bitcast i8* %p to i32* + store i32 %v, i32* %bp, align 2 + ret void +} + +; CHECK-LABEL: align1_store16 +; CHECK-DAG: lsr %r[[REG:[0-9]+]], %r1, 8 +; CHECK-DAG: stb %r1, [%r0,0] +; CHECK-DAG: stb %r[[REG:[0-9]+]], [%r0,1] +define void @align1_store16(i8* %p, i16 %v) nounwind { +entry: + %bp = bitcast i8* %p to i16* + store i16 %v, i16* %bp, align 1 + ret void +} + +; CHECK-LABEL: align1_store32 +; CHECK-DAG: lsr %r[[REG0:[0-9]+]], %r1, 8 +; CHECK-DAG: lsr %r[[REG1:[0-9]+]], %r1, 16 +; CHECK-DAG: lsr %r[[REG2:[0-9]+]], %r1, 24 +; CHECK-DAG: stb %r1, [%r0,0] +; CHECK-DAG: stb %r[[REG0]], [%r0,1] +; CHECK-DAG: stb %r[[REG1]], [%r0,2] +; CHECK-DAG: stb %r[[REG2]], [%r0,3] +define void @align1_store32(i8* %p, i32 %v) nounwind { +entry: + %bp = bitcast i8* %p to i32* + store i32 %v, i32* %bp, align 1 + ret void +} diff --git a/test/CodeGen/ARC/lit.local.cfg b/test/CodeGen/ARC/lit.local.cfg new file mode 100644 index 00000000000..9070deb0db5 --- /dev/null +++ b/test/CodeGen/ARC/lit.local.cfg @@ -0,0 +1,3 @@ +if not 'ARC' in config.root.targets: + config.unsupported = True + diff --git a/test/MC/Disassembler/ARC/alu.txt b/test/MC/Disassembler/ARC/alu.txt new file mode 100644 index 00000000000..b4461c73829 --- /dev/null +++ b/test/MC/Disassembler/ARC/alu.txt @@ -0,0 +1,75 @@ +# RUN: llvm-mc -triple=arc -disassemble %s | FileCheck %s + +# CHECK: add %r0, %r0, %r0 +0x00 0x20 0x00 0x00 + +# CHECK: add %r4, %r0, %r0 +0x00 0x20 0x04 0x00 + +# CHECK: add %r2, %r0, %r3 +0x00 0x20 0xc2 0x00 + +# CHECK: add %r2, %r0, %r4 +0x00 0x20 0x02 0x01 + +# CHECK: add %r2, %r7, %r4 +0x00 0x27 0x02 0x01 + +# CHECK: and %r2, %r7, %r4 +0x04 0x27 0x02 0x01 + +# CHECK: and %r2, %r7, 4 +0x44 0x27 0x02 0x01 + +# CHECK: and %r1, %r1, 255 +0x84 0x21 0xc3 0x0f + +# CHECK: asl %r1, %r1, 2 +0x40 0x29 0x81 0x00 + +# CHECK: asl %r0, %r0, %r0 +0x00 0x28 0x00 0x00 + +# CHECK: asr %r1, %r2, 31 +0x42 0x2a 0xc1 0x07 + +# CHECK: asr %r1, %r3, 7 +0x42 0x2b 0xc1 0x01 + +# CHECK: asr %r1, %r1, %r2 +0x02 0x29 0x81 0x00 + +# CHECK: max %r0, %r2, %r1 +0x08 0x22 0x40 0x00 + +# CHECK: max %r0, %r1, 15 +0x48 0x21 0xc0 0x03 + +# CHECK: max %r0, %r2, 4000 +0x08 0x22 0x80 0x0f 0x00 0x00 0xa0 0x0f + +# CHECK: max %r2, %r2, 255 +0x88 0x22 0xc3 0x0f + +# CHECK: or %r18, %r16, 61440 +0x05 0x20 0x92 0x2f 0x00 0x00 0x00 0xf0 + +# CHECK: or %r1, %r1, %r14 +0x05 0x21 0x81 0x03 + +# CHECK: or %r1, %r14, %r1 +0x05 0x26 0x41 0x10 + +# CHECK: or %r1, %r1, 128 +0x85 0x21 0x02 0x00 + +# CHECK: sub %sp, %fp, 92 +0x02 0x23 0x9c 0x3f 0x00 0x00 0x5c 0x00 + +# CHECK: sub %r2, %r7, %r4 +0x02 0x27 0x02 0x01 + +# CHECK: sub %r0, %r22, %r0 +0x02 0x26 0x00 0x20 + + diff --git a/test/MC/Disassembler/ARC/br.txt b/test/MC/Disassembler/ARC/br.txt new file mode 100644 index 00000000000..741084bc8ca --- /dev/null +++ b/test/MC/Disassembler/ARC/br.txt @@ -0,0 +1,29 @@ +# RUN: llvm-mc -triple=arc -disassemble %s | FileCheck %s + +# CHECK: brlt %r2, 0, 60 +0x3d 0x0a 0x12 0x00 + +# CHECK: brlo %r10, %r4, -112 +0x91 0x0a 0x04 0x91 + +# CHECK: breq %r2, %r1, 44 +0x2d 0x0a 0x40 0x00 + +# CHECK: brne %r0, 0, -16 +0xf1 0x08 0x11 0x80 + +# CHECK: brhs %r2, %r8, 38 +0x27 0x0a 0x05 0x02 + +# CHECK: bne 304 +0x30 0x01 0x02 0x00 + +# CHECK: beq 268 +0x0c 0x01 0x01 0x00 + +# CHECK: bhi 416 +0xa0 0x01 0x0d 0x00 + +# CHECK: b -68 +0xbd 0x07 0xcf 0xff + diff --git a/test/MC/Disassembler/ARC/ldst.txt b/test/MC/Disassembler/ARC/ldst.txt new file mode 100644 index 00000000000..51080b54697 --- /dev/null +++ b/test/MC/Disassembler/ARC/ldst.txt @@ -0,0 +1,47 @@ +# RUN: llvm-mc -triple=arc -disassemble %s | FileCheck %s + +# CHECK: ld %r0, [%r0,0] +0x00 0x10 0x00 0x00 + +# CHECK: ldh %r0, [%r0,0] +0x00 0x10 0x00 0x01 + +# CHECK: ldb %r0, [%r0,0] +0x00 0x10 0x80 0x00 + +# CHECK: ld %r1, [%r0,12] +0x0c 0x10 0x01 0x00 + +# CHECK: ld %r14, [%fp,-12] +0xf4 0x13 0x0e 0xb0 + +# CHECK: ld %r3, [%r0,-12] +0xf4 0x10 0x03 0x80 + +# CHECK: ld %r0, [%r0,244] +0xf4 0x10 0x00 0x00 + +# CHECK: ld %r0, [%r0,-12] +0xf4 0x10 0x00 0x80 + +# CHECK: ldh.x %r3, [%r1,0] +0x00 0x11 0x43 0x01 + +# CHECK: ldh.x %r2, [%r1,2] +0x02 0x11 0x42 0x01 + +# CHECK: ldh.x %r2, [%fp,-132] +0x7c 0x13 0x42 0xb1 + +# CHECK: ld %r0, [%r0,64000] +0x30 0x20 0x80 0x0f 0x00 0x00 0x00 0xfa + +# CHECK: ld %r6, [63920] +0x00 0x16 0x06 0x70 0x00 0x00 0xb0 0xf9 + +# CHECK: stb %r2, [%sp,35] +0x23 0x1c 0x82 0x30 + +# CHECK: st %r7, [63920] +0x00 0x1e 0xc0 0x71 0x00 0x00 0xb0 0xf9 + diff --git a/test/MC/Disassembler/ARC/lit.local.cfg b/test/MC/Disassembler/ARC/lit.local.cfg new file mode 100644 index 00000000000..9070deb0db5 --- /dev/null +++ b/test/MC/Disassembler/ARC/lit.local.cfg @@ -0,0 +1,3 @@ +if not 'ARC' in config.root.targets: + config.unsupported = True + diff --git a/test/MC/Disassembler/ARC/misc.txt b/test/MC/Disassembler/ARC/misc.txt new file mode 100644 index 00000000000..e5ab6957421 --- /dev/null +++ b/test/MC/Disassembler/ARC/misc.txt @@ -0,0 +1,42 @@ +# RUN: llvm-mc -triple=arc -disassemble %s | FileCheck %s + +# CHECK: mov %r0, -1 +0x8a 0x20 0xff 0x0f + +# 32767 == 0x7fff +# CHECK: mov %r4, 32767 +0x0a 0x24 0x80 0x0f 0x00 0x00 0xff 0x7f + +# CHECK: mov.eq %r2, %r6 +0xca 0x22 0x81 0x01 + +# CHECK: mov %r13, %r2 +0x0a 0x25 0x80 0x10 + +# CHECK: mov %r1, 20 +0x4a 0x21 0x00 0x05 + +# CHECK: st.aw %fp, [%sp,-4] +0xfc 0x1c 0xc8 0xb6 + +# CHECK: ld.ab %fp, [%sp,4] +0x04 0x14 0x1b 0x34 + +# CHECK: bl -2028 +0x16 0x08 0xcf 0xff + +# CHECK: cmp %r13, %r10 +0x0c 0x25 0x80 0x92 + +# CHECK: cmp %r14, 0 +0x4c 0x26 0x00 0x90 + +# CHECK: cmp %r23, 1 +0x4c 0x27 0x40 0xa0 + +# CHECK: jl [%r21] +0x22 0x20 0x40 0x05 + +# CHECK: j [%r3] +0x20 0x20 0xc0 0x00 +