Index: CMakeLists.txt =================================================================== --- CMakeLists.txt +++ CMakeLists.txt @@ -320,6 +320,7 @@ PowerPC Sparc SystemZ + M680x0 X86 XCore ) Index: cmake/config-ix.cmake =================================================================== --- cmake/config-ix.cmake +++ cmake/config-ix.cmake @@ -370,6 +370,8 @@ if (LLVM_NATIVE_ARCH MATCHES "i[2-6]86") set(LLVM_NATIVE_ARCH X86) +elseif (LLVM_NATIVE_ARCH STREQUAL "m68k") + set(LLVM_NATIVE_ARCH M680x0) elseif (LLVM_NATIVE_ARCH STREQUAL "x86") set(LLVM_NATIVE_ARCH X86) elseif (LLVM_NATIVE_ARCH STREQUAL "amd64") Index: cmake/modules/HandleLLVMOptions.cmake =================================================================== --- cmake/modules/HandleLLVMOptions.cmake +++ cmake/modules/HandleLLVMOptions.cmake @@ -223,6 +223,12 @@ append_if(SUPPORTS_FVISIBILITY_INLINES_HIDDEN_FLAG "-fvisibility-inlines-hidden" CMAKE_CXX_FLAGS) endif() +# GCC m68k on Linux by default aligns on 16bit, we want 32 +if(LLVM_INFERRED_HOST_TRIPLE STREQUAL "m68k-unknown-linux-gnu") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -malign-int") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -malign-int") +endif() + if( CMAKE_SIZEOF_VOID_P EQUAL 8 AND NOT WIN32 ) # TODO: support other platforms and toolchains. if( LLVM_BUILD_32_BITS ) Index: include/llvm/ADT/Triple.h =================================================================== --- include/llvm/ADT/Triple.h +++ include/llvm/ADT/Triple.h @@ -76,6 +76,7 @@ tcele, // TCE little endian (http://tce.cs.tut.fi/): tcele thumb, // Thumb (little endian): thumb, thumbv.* thumbeb, // Thumb (big endian): thumbeb + m680x0, // M680x0: Motorola 680x0 family x86, // X86: i[3-9]86 x86_64, // X86-64: amd64, x86_64 xcore, // XCore: xcore Index: include/llvm/BinaryFormat/ELF.h =================================================================== --- include/llvm/BinaryFormat/ELF.h +++ include/llvm/BinaryFormat/ELF.h @@ -725,6 +725,11 @@ #include "ELFRelocs/BPF.def" }; +// ELF Relocation types for M680x0 +enum { +#include "ELFRelocs/m680x0.def" +}; + #undef ELF_RELOC // Section header. Index: include/llvm/BinaryFormat/ELFRelocs/m680x0.def =================================================================== --- /dev/null +++ include/llvm/BinaryFormat/ELFRelocs/m680x0.def @@ -0,0 +1,50 @@ + +#ifndef ELF_RELOC +#error "ELF_RELOC must be defined" +#endif + +ELF_RELOC (R_M680x0_NONE, 0) /* No reloc */ +ELF_RELOC (R_M680x0_32, 1) /* Direct 32 bit */ +ELF_RELOC (R_M680x0_16, 2) /* Direct 16 bit */ +ELF_RELOC (R_M680x0_8, 3) /* Direct 8 bit */ +ELF_RELOC (R_M680x0_PC32, 4) /* PC relative 32 bit */ +ELF_RELOC (R_M680x0_PC16, 5) /* PC relative 16 bit */ +ELF_RELOC (R_M680x0_PC8, 6) /* PC relative 8 bit */ +ELF_RELOC (R_M680x0_GOTPCREL32, 7) /* 32 bit PC relative GOT entry */ +ELF_RELOC (R_M680x0_GOTPCREL16, 8) /* 16 bit PC relative GOT entry */ +ELF_RELOC (R_M680x0_GOTPCREL8, 9) /* 8 bit PC relative GOT entry */ +ELF_RELOC (R_M680x0_GOTOFF32, 10) /* 32 bit GOT offset */ +ELF_RELOC (R_M680x0_GOTOFF16, 11) /* 16 bit GOT offset */ +ELF_RELOC (R_M680x0_GOTOFF8, 12) /* 8 bit GOT offset */ +ELF_RELOC (R_M680x0_PLT32, 13) /* 32 bit PC relative PLT address */ +ELF_RELOC (R_M680x0_PLT16, 14) /* 16 bit PC relative PLT address */ +ELF_RELOC (R_M680x0_PLT8, 15) /* 8 bit PC relative PLT address */ +ELF_RELOC (R_M680x0_PLTOFF32, 16) /* 32 bit PLT offset */ +ELF_RELOC (R_M680x0_PLTOFF16, 17) /* 16 bit PLT offset */ +ELF_RELOC (R_M680x0_PLTOFF8, 18) /* 8 bit PLT offset */ +ELF_RELOC (R_M680x0_COPY, 19) /* Copy symbol at runtime */ +ELF_RELOC (R_M680x0_GLOB_DAT, 20) /* Create GOT entry */ +ELF_RELOC (R_M680x0_JMP_SLOT, 21) /* Create PLT entry */ +ELF_RELOC (R_M680x0_RELATIVE, 22) /* Adjust by program base */ +/* These are GNU extensions to enable C++ vtable garbage collection. */ +ELF_RELOC (R_M680x0_GNU_VTINHERIT, 23) +ELF_RELOC (R_M680x0_GNU_VTENTRY, 24) +/* TLS static relocations. */ +ELF_RELOC (R_M680x0_TLS_GD32, 25) +ELF_RELOC (R_M680x0_TLS_GD16, 26) +ELF_RELOC (R_M680x0_TLS_GD8, 27) +ELF_RELOC (R_M680x0_TLS_LDM32, 28) +ELF_RELOC (R_M680x0_TLS_LDM16, 29) +ELF_RELOC (R_M680x0_TLS_LDM8, 30) +ELF_RELOC (R_M680x0_TLS_LDO32, 31) +ELF_RELOC (R_M680x0_TLS_LDO16, 32) +ELF_RELOC (R_M680x0_TLS_LDO8, 33) +ELF_RELOC (R_M680x0_TLS_IE32, 34) +ELF_RELOC (R_M680x0_TLS_IE16, 35) +ELF_RELOC (R_M680x0_TLS_IE8, 36) +ELF_RELOC (R_M680x0_TLS_LE32, 37) +ELF_RELOC (R_M680x0_TLS_LE16, 38) +ELF_RELOC (R_M680x0_TLS_LE8, 39) +ELF_RELOC (R_M680x0_TLS_DTPMOD32, 40) +ELF_RELOC (R_M680x0_TLS_DTPREL32, 41) +ELF_RELOC (R_M680x0_TLS_TPREL32, 42) Index: include/llvm/MC/MCExpr.h =================================================================== --- include/llvm/MC/MCExpr.h +++ include/llvm/MC/MCExpr.h @@ -172,6 +172,7 @@ VK_GOT, VK_GOTOFF, VK_GOTREL, + VK_GOTPC, VK_GOTPCREL, VK_GOTTPOFF, VK_INDNTPOFF, Index: include/llvm/MC/MCInstrDesc.h =================================================================== --- include/llvm/MC/MCInstrDesc.h +++ include/llvm/MC/MCInstrDesc.h @@ -62,7 +62,21 @@ } -/// This holds information about one operand of a machine instruction, +/// \brief Defines an operand group. +/// +/// Many machine operands are aggregates of multiple target operands (e.g. +/// register, immediate) and during codegen it is not always clear how many +/// those operands to read. This type provides this information. +class MIOperandInfo { + public: + int16_t MINo; + int16_t Type; + int16_t OpsNum; + + bool isTargetType() { return Type >= MCOI::OPERAND_FIRST_TARGET; }; +}; + +/// \brief This holds information about one operand of a machine instruction, /// indicating the register class for register operands, etc. class MCOperandInfo { public: @@ -162,6 +176,7 @@ class MCInstrDesc { public: unsigned short Opcode; // The opcode number + unsigned short NumMIOperands; // Num of MI operands unsigned short NumOperands; // Num of args (may be more if variable_ops) unsigned char NumDefs; // Num of args that are definitions unsigned char Size; // Number of bytes in encoding. @@ -170,6 +185,7 @@ uint64_t TSFlags; // Target Specific Flag values const MCPhysReg *ImplicitUses; // Registers implicitly read by this instr const MCPhysReg *ImplicitDefs; // Registers implicitly defined by this instr + const MIOperandInfo *MIOpInfo; // 'NumMIOperands' entries about operands const MCOperandInfo *OpInfo; // 'NumOperands' entries about operands // Subtarget feature that this is deprecated on, if any // -1 implies this is not deprecated by any single feature. It may still be Index: include/llvm/Object/ELFObjectFile.h =================================================================== --- include/llvm/Object/ELFObjectFile.h +++ include/llvm/Object/ELFObjectFile.h @@ -1003,6 +1003,8 @@ switch (EF.getHeader()->e_ident[ELF::EI_CLASS]) { case ELF::ELFCLASS32: switch (EF.getHeader()->e_machine) { + case ELF::EM_68K: + return "ELF32-M680x0"; case ELF::EM_386: return "ELF32-i386"; case ELF::EM_IAMCU: @@ -1065,6 +1067,8 @@ template Triple::ArchType ELFObjectFile::getArch() const { bool IsLittleEndian = ELFT::TargetEndianness == support::little; switch (EF.getHeader()->e_machine) { + case ELF::EM_68K: + return Triple::m680x0; case ELF::EM_386: case ELF::EM_IAMCU: return Triple::x86; Index: include/llvm/module.modulemap =================================================================== --- include/llvm/module.modulemap +++ include/llvm/module.modulemap @@ -58,6 +58,7 @@ textual header "BinaryFormat/ELFRelocs/Sparc.def" textual header "BinaryFormat/ELFRelocs/SystemZ.def" textual header "BinaryFormat/ELFRelocs/x86_64.def" + textual header "BinaryFormat/ELFRelocs/m680x0.def" textual header "BinaryFormat/WasmRelocs.def" } Index: lib/MC/MCExpr.cpp =================================================================== --- lib/MC/MCExpr.cpp +++ lib/MC/MCExpr.cpp @@ -197,6 +197,7 @@ case VK_GOT: return "GOT"; case VK_GOTOFF: return "GOTOFF"; case VK_GOTREL: return "GOTREL"; + case VK_GOTPC: return "GOTPC"; case VK_GOTPCREL: return "GOTPCREL"; case VK_GOTTPOFF: return "GOTTPOFF"; case VK_INDNTPOFF: return "INDNTPOFF"; @@ -323,6 +324,7 @@ .Case("got", VK_GOT) .Case("gotoff", VK_GOTOFF) .Case("gotrel", VK_GOTREL) + .Case("gotpc", VK_GOTPC) .Case("gotpcrel", VK_GOTPCREL) .Case("gottpoff", VK_GOTTPOFF) .Case("indntpoff", VK_INDNTPOFF) Index: lib/Object/ELF.cpp =================================================================== --- lib/Object/ELF.cpp +++ lib/Object/ELF.cpp @@ -23,6 +23,13 @@ StringRef llvm::object::getELFRelocationTypeName(uint32_t Machine, uint32_t Type) { switch (Machine) { + case ELF::EM_68K: + switch (Type) { +#include "llvm/BinaryFormat/ELFRelocs/m680x0.def" + default: + break; + } + break; case ELF::EM_X86_64: switch (Type) { #include "llvm/BinaryFormat/ELFRelocs/x86_64.def" Index: lib/ObjectYAML/ELFYAML.cpp =================================================================== --- lib/ObjectYAML/ELFYAML.cpp +++ lib/ObjectYAML/ELFYAML.cpp @@ -644,6 +644,9 @@ case ELF::EM_BPF: #include "llvm/BinaryFormat/ELFRelocs/BPF.def" break; + case ELF::EM_68K: +#include "llvm/BinaryFormat/ELFRelocs/m680x0.def" + break; default: llvm_unreachable("Unsupported architecture"); } Index: lib/Support/Triple.cpp =================================================================== --- lib/Support/Triple.cpp +++ lib/Support/Triple.cpp @@ -51,6 +51,7 @@ case tcele: return "tcele"; case thumb: return "thumb"; case thumbeb: return "thumbeb"; + case m680x0: return "m680x0"; case x86: return "i386"; case x86_64: return "x86_64"; case xcore: return "xcore"; @@ -118,6 +119,8 @@ case systemz: return "s390"; + case m680x0: return "m680x0"; + case x86: case x86_64: return "x86"; @@ -289,6 +292,7 @@ .Case("tcele", tcele) .Case("thumb", thumb) .Case("thumbeb", thumbeb) + .Case("m680x0", m680x0) .Case("x86", x86) .Case("x86-64", x86_64) .Case("xcore", xcore) @@ -379,6 +383,9 @@ static Triple::ArchType parseArch(StringRef ArchName) { auto AT = StringSwitch(ArchName) + .Cases("m680x0", "m68k", Triple::m680x0) + .Cases("m68000", "m68010", "m68020", Triple::m680x0) + .Cases("m68030", "m68040", "m68060", Triple::m680x0) .Cases("i386", "i486", "i586", "i686", Triple::x86) // FIXME: Do we need to support these? .Cases("i786", "i886", "i986", Triple::x86) @@ -622,6 +629,7 @@ case Triple::aarch64: case Triple::arm: case Triple::thumb: + case Triple::m680x0: // FIXME should be COFF? case Triple::x86: case Triple::x86_64: if (T.isOSDarwin()) @@ -1205,6 +1213,7 @@ case llvm::Triple::tcele: case llvm::Triple::thumb: case llvm::Triple::thumbeb: + case llvm::Triple::m680x0: case llvm::Triple::x86: case llvm::Triple::xcore: case llvm::Triple::amdil: @@ -1262,6 +1271,7 @@ case Triple::avr: case Triple::bpfel: case Triple::bpfeb: + case Triple::m680x0: case Triple::msp430: case Triple::systemz: case Triple::ppc64le: @@ -1371,6 +1381,7 @@ case Triple::sparc: T.setArch(Triple::sparcv9); break; case Triple::riscv32: T.setArch(Triple::riscv64); break; case Triple::x86: T.setArch(Triple::x86_64); break; + case Triple::m680x0: T.setArch(Triple::m680x0); break; case Triple::amdil: T.setArch(Triple::amdil64); break; case Triple::hsail: T.setArch(Triple::hsail64); break; case Triple::spir: T.setArch(Triple::spir64); break; Index: lib/Target/LLVMBuild.txt =================================================================== --- lib/Target/LLVMBuild.txt +++ lib/Target/LLVMBuild.txt @@ -36,6 +36,7 @@ Sparc SystemZ WebAssembly + M680x0 X86 XCore Index: lib/Target/M680x0/CMakeLists.txt =================================================================== --- /dev/null +++ lib/Target/M680x0/CMakeLists.txt @@ -0,0 +1,35 @@ +set(LLVM_TARGET_DEFINITIONS M680x0.td) + +tablegen(LLVM M680x0GenRegisterInfo.inc -gen-register-info) +tablegen(LLVM M680x0GenInstrInfo.inc -gen-instr-info) +tablegen(LLVM M680x0GenSubtargetInfo.inc -gen-subtarget) +tablegen(LLVM M680x0GenMCCodeBeads.inc -gen-code-beads) +tablegen(LLVM M680x0GenMCPseudoLowering.inc -gen-pseudo-lowering) +tablegen(LLVM M680x0GenDAGISel.inc -gen-dag-isel) +tablegen(LLVM M680x0GenCallingConv.inc -gen-callingconv) +tablegen(LLVM M680x0GenAsmWriter.inc -gen-asm-writer) + +# M680x0CommonTableGen must be defined +add_public_tablegen_target(M680x0CommonTableGen) + +# M680x0CodeGen should match with LLVMBuild.txt M680x0CodeGen +add_llvm_target(M680x0CodeGen + M680x0AsmPrinter.cpp + M680x0MCInstLower.cpp + M680x0FrameLowering.cpp + M680x0InstrInfo.cpp + M680x0ISelLowering.cpp + M680x0ISelDAGToDAG.cpp + M680x0ExpandPseudo.cpp + M680x0CollapseMOVEMPass.cpp + M680x0MachineFunction.cpp + M680x0RegisterInfo.cpp + M680x0Subtarget.cpp + M680x0TargetObjectFile.cpp + M680x0TargetMachine.cpp +) + +# Should match with "subdirectories = MCTargetDesc TargetInfo" in LLVMBuild.txt +add_subdirectory(InstPrinter) +add_subdirectory(TargetInfo) +add_subdirectory(MCTargetDesc) Index: lib/Target/M680x0/InstPrinter/CMakeLists.txt =================================================================== --- /dev/null +++ lib/Target/M680x0/InstPrinter/CMakeLists.txt @@ -0,0 +1,3 @@ +add_llvm_library(LLVMM680x0AsmPrinter + M680x0InstPrinter.cpp +) Index: lib/Target/M680x0/InstPrinter/LLVMBuild.txt =================================================================== --- /dev/null +++ lib/Target/M680x0/InstPrinter/LLVMBuild.txt @@ -0,0 +1,23 @@ +;===- ./lib/Target/M680x0/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 = M680x0AsmPrinter +parent = M680x0 +required_libraries = MC Support +add_to_library_groups = M680x0 Index: lib/Target/M680x0/InstPrinter/M680x0InstPrinter.h =================================================================== --- /dev/null +++ lib/Target/M680x0/InstPrinter/M680x0InstPrinter.h @@ -0,0 +1,159 @@ +//===-- M680x0InstPrinter.h - Convert M680x0 MCInst to asm ------*- 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 declarations for an M680x0 MCInst printer. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_M680X0_INSTPRINTER_M680X0INSTPRINTER_H +#define LLVM_LIB_TARGET_M680X0_INSTPRINTER_M680X0INSTPRINTER_H + +#include "llvm/MC/MCInstPrinter.h" + +namespace llvm { + +class TargetMachine; + +class M680x0InstPrinter : public MCInstPrinter { +public: + M680x0InstPrinter(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; + + bool printAliasInstr(const MCInst *MI, raw_ostream &OS); + void printCustomAliasOperand(const MCInst *MI, unsigned OpIdx, + unsigned PrintMethodIdx, raw_ostream &O); + +private: + void printOperand(const MCInst *MI, unsigned opNum, raw_ostream &O); + void printImmediate(const MCInst *MI, int opNum, raw_ostream &O); + /// Print register mask for MOVEM instruction in order D0-D7,A0-A7 + void printMoveMask(const MCInst *MI, int opNum, raw_ostream &O); + /// Print register mask for MOVEM instruction in order A7-A0,D7-D0 + void printMoveMaskR(const MCInst *MI, int opNum, raw_ostream &O); + void printDisp(const MCInst *MI, int opNum, raw_ostream &O); + void printARIMem(const MCInst *MI, int opNum, raw_ostream &O); + void printARIPIMem(const MCInst *MI, int opNum, raw_ostream &O); + void printARIPDMem(const MCInst *MI, int opNum, raw_ostream &O); + void printARIDMem(const MCInst *MI, int opNum, raw_ostream &O); + void printARIIMem(const MCInst *MI, int opNum, raw_ostream &O); + void printAbsMem(const MCInst *MI, int opNum, raw_ostream &O); + void printPCDMem(const MCInst *MI, int opNum, raw_ostream &O); + void printPCIMem(const MCInst *MI, int opNum, raw_ostream &O); + + //===----------------------------------------------------------------------===// + // Specializations + //===----------------------------------------------------------------------===// + // + void printPCRelImm(const MCInst *MI, int opNum, raw_ostream &O) { + printOperand(MI, opNum, O); + } + + void printARI8Mem(const MCInst *MI, int opNum, raw_ostream &O) { + printARIMem(MI, opNum, O); + } + void printARI16Mem(const MCInst *MI, int opNum, raw_ostream &O) { + printARIMem(MI, opNum, O); + } + void printARI32Mem(const MCInst *MI, int opNum, raw_ostream &O) { + printARIMem(MI, opNum, O); + } + + void printARIPI8Mem(const MCInst *MI, int opNum, raw_ostream &O) { + printARIPIMem(MI, opNum, O); + } + void printARIPI16Mem(const MCInst *MI, int opNum, raw_ostream &O) { + printARIPIMem(MI, opNum, O); + } + void printARIPI32Mem(const MCInst *MI, int opNum, raw_ostream &O) { + printARIPIMem(MI, opNum, O); + } + + void printARIPD8Mem(const MCInst *MI, int opNum, raw_ostream &O) { + printARIPDMem(MI, opNum, O); + } + void printARIPD16Mem(const MCInst *MI, int opNum, raw_ostream &O) { + printARIPDMem(MI, opNum, O); + } + void printARIPD32Mem(const MCInst *MI, int opNum, raw_ostream &O) { + printARIPDMem(MI, opNum, O); + } + + void printARID8Mem(const MCInst *MI, int opNum, raw_ostream &O) { + printARIDMem(MI, opNum, O); + } + void printARID16Mem(const MCInst *MI, int opNum, raw_ostream &O) { + printARIDMem(MI, opNum, O); + } + void printARID32Mem(const MCInst *MI, int opNum, raw_ostream &O) { + printARIDMem(MI, opNum, O); + } + + void printARII8Mem(const MCInst *MI, int opNum, raw_ostream &O) { + printARIIMem(MI, opNum, O); + } + void printARII16Mem(const MCInst *MI, int opNum, raw_ostream &O) { + printARIIMem(MI, opNum, O); + } + void printARII32Mem(const MCInst *MI, int opNum, raw_ostream &O) { + printARIIMem(MI, opNum, O); + } + + void printAS8Mem(const MCInst *MI, int opNum, raw_ostream &O) { + printAbsMem(MI, opNum, O); + } + void printAS16Mem(const MCInst *MI, int opNum, raw_ostream &O) { + printAbsMem(MI, opNum, O); + } + void printAS32Mem(const MCInst *MI, int opNum, raw_ostream &O) { + printAbsMem(MI, opNum, O); + } + + void printAL8Mem(const MCInst *MI, int opNum, raw_ostream &O) { + printAbsMem(MI, opNum, O); + } + void printAL16Mem(const MCInst *MI, int opNum, raw_ostream &O) { + printAbsMem(MI, opNum, O); + } + void printAL32Mem(const MCInst *MI, int opNum, raw_ostream &O) { + printAbsMem(MI, opNum, O); + } + + void printPCD8Mem(const MCInst *MI, int opNum, raw_ostream &O) { + printPCDMem(MI, opNum, O); + } + void printPCD16Mem(const MCInst *MI, int opNum, raw_ostream &O) { + printPCDMem(MI, opNum, O); + } + void printPCD32Mem(const MCInst *MI, int opNum, raw_ostream &O) { + printPCDMem(MI, opNum, O); + } + + void printPCI8Mem(const MCInst *MI, int opNum, raw_ostream &O) { + printPCIMem(MI, opNum, O); + } + void printPCI16Mem(const MCInst *MI, int opNum, raw_ostream &O) { + printPCIMem(MI, opNum, O); + } + void printPCI32Mem(const MCInst *MI, int opNum, raw_ostream &O) { + printPCIMem(MI, opNum, O); + } +}; +} // end namespace llvm + +#endif Index: lib/Target/M680x0/InstPrinter/M680x0InstPrinter.cpp =================================================================== --- /dev/null +++ lib/Target/M680x0/InstPrinter/M680x0InstPrinter.cpp @@ -0,0 +1,196 @@ +//===-- M680x0InstPrinter.cpp - Convert M680x0 MCInst to asm ----*- 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 definitions for an M680x0 MCInst printer. +/// +//===----------------------------------------------------------------------===// + +// TODO finish printer, it does not conform to Motorola asm at all + +#include "M680x0InstPrinter.h" + +#include "M680x0InstrInfo.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCSymbol.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +#define DEBUG_TYPE "asm-printer" + +#define PRINT_ALIAS_INSTR +#include "M680x0GenAsmWriter.inc" + +void M680x0InstPrinter::printRegName(raw_ostream &OS, unsigned RegNo) const { + OS << "%" << StringRef(getRegisterName(RegNo)); +} + +void M680x0InstPrinter::printInst(const MCInst *MI, raw_ostream &O, + StringRef Annot, const MCSubtargetInfo &STI) { + if (!printAliasInstr(MI, O)) { + printInstruction(MI, O); + } + printAnnotation(O, Annot); +} + +void M680x0InstPrinter::printOperand(const MCInst *MI, unsigned OpNo, + raw_ostream &O) { + const MCOperand &MO = MI->getOperand(OpNo); + if (MO.isReg()) { + printRegName(O, MO.getReg()); + return; + } + + if (MO.isImm()) { + printImmediate(MI, OpNo, O); + return; + } + + assert(MO.isExpr() && "Unknown operand kind in printOperand"); + MO.getExpr()->print(O, &MAI); +} + +void M680x0InstPrinter::printImmediate(const MCInst *MI, int opNum, + raw_ostream &O) { + const MCOperand &MO = MI->getOperand(opNum); + if (MO.isImm()) { + O << '#' << MO.getImm(); + } else if (MO.isExpr()) { + O << '#'; + MO.getExpr()->print(O, &MAI); + } else { + llvm_unreachable("Unknown immediate kind"); + } +} + +// +void M680x0InstPrinter::printMoveMask(const MCInst *MI, int opNum, + raw_ostream &O) { + unsigned Mask = MI->getOperand(opNum).getImm(); + assert((Mask & 0xFFFF) == Mask); + + unsigned HalfMask, Reg; + for (int s = 0; s < 8; s += 8) { + HalfMask = Mask >> s; + if (HalfMask && s != 0) { + O << ','; + } + + for (int i = 0; HalfMask; ++i) { + if ((HalfMask >> i) & 0x01) { + HalfMask ^= 1 << i; + Reg = M680x0II::getMaskedSpillRegister(i + s); + printRegName(O, Reg); + + int j = i; + while ((HalfMask >> (j + 1)) & 0x01) { + HalfMask ^= 1 << ++j; + } + + if (j != i) { + O << '-'; + Reg = M680x0II::getMaskedSpillRegister(j + s); + printRegName(O, Reg); + } + + i = j; + + if (HalfMask) { + O << ','; + } + } else { + } + } + } +} + +void M680x0InstPrinter::printDisp(const MCInst *MI, int opNum, raw_ostream &O) { + const MCOperand &Op = MI->getOperand(opNum); + if (Op.isImm()) { + O << Op.getImm(); + return; + } + assert(Op.isExpr() && "Unknown operand kind in printOperand"); + Op.getExpr()->print(O, &MAI); +} + +void M680x0InstPrinter::printARIMem(const MCInst *MI, int opNum, + raw_ostream &O) { + O << '('; + printOperand(MI, opNum, O); + O << ')'; +} + +void M680x0InstPrinter::printARIPIMem(const MCInst *MI, int opNum, + raw_ostream &O) { + O << "("; + printOperand(MI, opNum, O); + O << ")+"; +} + +void M680x0InstPrinter::printARIPDMem(const MCInst *MI, int opNum, + raw_ostream &O) { + O << "-("; + printOperand(MI, opNum, O); + O << ")"; +} + +void M680x0InstPrinter::printARIDMem(const MCInst *MI, int opNum, + raw_ostream &O) { + O << '('; + printDisp(MI, opNum + M680x0::MemDisp, O); + O << ','; + printOperand(MI, opNum + M680x0::MemBase, O); + O << ')'; +} + +void M680x0InstPrinter::printARIIMem(const MCInst *MI, int opNum, + raw_ostream &O) { + O << '('; + printDisp(MI, opNum + M680x0::MemDisp, O); + O << ','; + printOperand(MI, opNum + M680x0::MemBase, O); + O << ','; + printOperand(MI, opNum + M680x0::MemIndex, O); + O << ')'; +} + +// NOTE forcing (W,L) size available since M68020 only +void M680x0InstPrinter::printAbsMem(const MCInst *MI, int opNum, + raw_ostream &O) { + const MCOperand &MO = MI->getOperand(opNum); + if (MO.isImm()) { + // ??? Print it in hex? + O << (unsigned int)MO.getImm(); + } else { + printOperand(MI, opNum, O); + } +} + +void M680x0InstPrinter::printPCDMem(const MCInst *MI, int opNum, + raw_ostream &O) { + O << '('; + printDisp(MI, opNum + M680x0::PCRelDisp, O); + O << ",%pc)"; +} + +void M680x0InstPrinter::printPCIMem(const MCInst *MI, int opNum, + raw_ostream &O) { + O << '('; + printDisp(MI, opNum + M680x0::PCRelDisp, O); + O << ",%pc,"; + printOperand(MI, opNum + M680x0::PCRelIndex, O); + O << ')'; +} Index: lib/Target/M680x0/LLVMBuild.txt =================================================================== --- /dev/null +++ lib/Target/M680x0/LLVMBuild.txt @@ -0,0 +1,39 @@ +;===-- M680x0/LLVMBuild.txt ------------------------------------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[common] +subdirectories = InstPrinter MCTargetDesc TargetInfo + +[component_0] +type = TargetGroup +name = M680x0 +parent = Target + +[component_1] +type = Library +name = M680x0CodeGen +parent = M680x0 +required_libraries = + CodeGen Core MC AsmPrinter + M680x0Desc + M680x0Info + M680x0AsmPrinter + SelectionDAG + Support + Target + +add_to_library_groups = M680x0 Index: lib/Target/M680x0/M680x0.h =================================================================== --- /dev/null +++ lib/Target/M680x0/M680x0.h @@ -0,0 +1,53 @@ +//===- M680x0.h - Top-level interface for M680x0 representation -*- 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 the entry points for global functions defined in the +/// M680x0 target library, as used by the LLVM JIT. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_M680x0_M680x0_H +#define LLVM_LIB_TARGET_M680x0_M680x0_H + +#include "llvm/Support/CodeGen.h" + +namespace llvm { + +class FunctionPass; +class M680x0TargetMachine; + +/// This pass converts a legalized DAG into a M680x0-specific DAG, ready for +/// instruction scheduling. +FunctionPass *createM680x0ISelDag(M680x0TargetMachine &TM); + +/// Return a Machine IR pass that expands M680x0-specific pseudo +/// instructions into a sequence of actual instructions. This pass +/// must run after prologue/epilogue insertion and before lowering +/// the MachineInstr to MC. +FunctionPass *createM680x0ExpandPseudoPass(); + +/// This pass initializes a global base register for PIC on M680x0. +FunctionPass *createM680x0GlobalBaseRegPass(); + +/// Finds sequential MOVEM instruction and collapse them into a single one. This +/// pass has to be run after all pseudo expansions and prologue/epilogue +/// emission so that all possible MOVEM are already in place. +FunctionPass *createM680x0CollapseMOVEMPass(); + +/// Finds MOVE instructions before any conditioanl branch instruction and +/// replaces them with MOVEM instruction. Motorola's MOVEs do trash(V,C) flags +/// register which prevents branch from taking the correct route. This pass +/// has to be run after all pseudo expansions and prologue/epilogue emission +/// so that all possible MOVEs are present. +FunctionPass *createM680x0ConvertMOVToMOVMPass(); + +} // namespace llvm + +#endif Index: lib/Target/M680x0/M680x0.td =================================================================== --- /dev/null +++ lib/Target/M680x0/M680x0.td @@ -0,0 +1,85 @@ +//===-- M680x0.td - Motorola 680x0 target definitions ------*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This is a target description file for the Motorola 680x0 family, referred +/// to here as the "M680x0" architecture. +/// +//===----------------------------------------------------------------------===// + +include "llvm/Target/Target.td" + +//===----------------------------------------------------------------------===// +// X86 Subtarget features +//===----------------------------------------------------------------------===// + +def FeatureISAx00 + : SubtargetFeature<"x00", "IsM68000", "true", "Is M68000 ISA supported">; + +def FeatureISAx10 + : SubtargetFeature<"x10", "IsM68010", "true", "Is M68010 ISA supported", + [ FeatureISAx00 ]>; + +def FeatureISAx20 + : SubtargetFeature<"x20", "IsM68020", "true", "Is M68020 ISA supported", + [ FeatureISAx10 ]>; + +def FeatureISAx30 + : SubtargetFeature<"x30", "IsM68030", "true", "Is M68030 ISA supported", + [ FeatureISAx20 ]>; + +def FeatureISAx40 + : SubtargetFeature<"x40", "IsM68040", "true", "Is M68040 ISA supported", + [ FeatureISAx30 ]>; + +//===----------------------------------------------------------------------===// +// M680x0 processors supported. +//===----------------------------------------------------------------------===// + +include "M680x0Schedule.td" + +class Proc Features> + : ProcessorModel; + +def : Proc<"generic", [ FeatureISAx00 ]>; +def : Proc<"M68000", [ FeatureISAx00 ]>; +def : Proc<"M68010", [ FeatureISAx10 ]>; +def : Proc<"M68020", [ FeatureISAx20 ]>; +def : Proc<"M68030", [ FeatureISAx30 ]>; +def : Proc<"M68040", [ FeatureISAx40 ]>; + +//===----------------------------------------------------------------------===// +// Register File Description +//===----------------------------------------------------------------------===// + +include "M680x0RegisterInfo.td" + +//===----------------------------------------------------------------------===// +// Instruction Descriptions +//===----------------------------------------------------------------------===// + +include "M680x0InstrInfo.td" + +def M680x0InstrInfo : InstrInfo; + +//===----------------------------------------------------------------------===// +// Calling Conventions +//===----------------------------------------------------------------------===// + +include "M680x0CallingConv.td" + +//===----------------------------------------------------------------------===// +// Target +//===----------------------------------------------------------------------===// + +def M680x0 : Target { + let InstructionSet = M680x0InstrInfo; + // let AssemblyParserVariants = [ATTAsmParserVariant, IntelAsmParserVariant]; + // let AssemblyWriters = [ATTAsmWriter, IntelAsmWriter]; +} Index: lib/Target/M680x0/M680x0AsmPrinter.h =================================================================== --- /dev/null +++ lib/Target/M680x0/M680x0AsmPrinter.h @@ -0,0 +1,63 @@ +//===-- M680x0AsmPrinter.h - M680x0 LLVM Assembly Printer -------- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains M680x0 assembler printer declarations. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_M680X0_M680X0ASMPRINTER_H +#define LLVM_LIB_TARGET_M680X0_M680X0ASMPRINTER_H + +#include "M680x0MCInstLower.h" +#include "M680x0TargetMachine.h" + +#include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Target/TargetMachine.h" + +namespace llvm { +class MCStreamer; +class MachineInstr; +class MachineBasicBlock; +class Module; +class raw_ostream; + +class M680x0Subtarget; +class M680x0MachineFunctionInfo; + +class LLVM_LIBRARY_VISIBILITY M680x0AsmPrinter : public AsmPrinter { + + void EmitInstrWithMacroNoAT(const MachineInstr *MI); + +public: + const M680x0Subtarget *Subtarget; + const M680x0MachineFunctionInfo *MMFI; + std::unique_ptr MCInstLowering; + + explicit M680x0AsmPrinter(TargetMachine &TM, + std::unique_ptr Streamer) + : AsmPrinter(TM, std::move(Streamer)) { + Subtarget = static_cast(TM).getSubtargetImpl(); + } + + StringRef getPassName() const override { return "M680x0 Assembly Printer"; } + + virtual bool runOnMachineFunction(MachineFunction &MF) override; + + void EmitInstruction(const MachineInstr *MI) override; + void EmitFunctionBodyStart() override; + void EmitFunctionBodyEnd() override; + void EmitStartOfAsmFile(Module &M) override; + void EmitEndOfAsmFile(Module &M) override; +}; +} // namespace llvm + +#endif Index: lib/Target/M680x0/M680x0AsmPrinter.cpp =================================================================== --- /dev/null +++ lib/Target/M680x0/M680x0AsmPrinter.cpp @@ -0,0 +1,99 @@ +//===-- M680x0AsmPrinter.cpp - M680x0 LLVM Assembly Printer -----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains a printer that converts from our internal representation +/// of machine-dependent LLVM code to GAS-format M680x0 assembly language. +/// +//===----------------------------------------------------------------------===// + +// TODO make it print Motorola asm + +#include "M680x0AsmPrinter.h" + +#include "M680x0.h" +#include "M680x0InstrInfo.h" +#include "M680x0MachineFunction.h" + +#include "InstPrinter/M680x0InstPrinter.h" +#include "MCTargetDesc/M680x0BaseInfo.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/Twine.h" +#include "llvm/CodeGen/MachineConstantPool.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineMemOperand.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Mangler.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstBuilder.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSymbol.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Target/TargetLoweringObjectFile.h" +#include "llvm/Target/TargetOptions.h" + +using namespace llvm; + +#define DEBUG_TYPE "m680x0-asm-printer" + +bool M680x0AsmPrinter::runOnMachineFunction(MachineFunction &MF) { + MMFI = MF.getInfo(); + MCInstLowering = make_unique(MF, *this); + AsmPrinter::runOnMachineFunction(MF); + return true; +} + +void M680x0AsmPrinter::EmitInstruction(const MachineInstr *MI) { + switch (MI->getOpcode()) { + default: { + if (MI->isPseudo()) { + LLVM_DEBUG(dbgs() << "Pseudo opcode(" << MI->getOpcode() + << ") found in EmitInstruction()\n"); + llvm_unreachable("Cannot proceed"); + } + break; + } + case M680x0::TAILJMPj: + case M680x0::TAILJMPq: + // Lower these as normal, but add some comments. + OutStreamer->AddComment("TAILCALL"); + break; + } + + MCInst TmpInst0; + MCInstLowering->Lower(MI, TmpInst0); + OutStreamer->EmitInstruction(TmpInst0, getSubtargetInfo()); +} + +void M680x0AsmPrinter::EmitFunctionBodyStart() { + // TODO +} + +void M680x0AsmPrinter::EmitFunctionBodyEnd() { + // TODO +} + +void M680x0AsmPrinter::EmitStartOfAsmFile(Module &M) { + OutStreamer->EmitSyntaxDirective(); +} + +void M680x0AsmPrinter::EmitEndOfAsmFile(Module &M) {} + +extern "C" void LLVMInitializeM680x0AsmPrinter() { + RegisterAsmPrinter X(TheM680x0Target); +} Index: lib/Target/M680x0/M680x0CallingConv.h =================================================================== --- /dev/null +++ lib/Target/M680x0/M680x0CallingConv.h @@ -0,0 +1,80 @@ +//===-- M680x0CallingConv.h - M680x0 Custom CC Routines ---------*- 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 the custom routines for the M680x0 Calling Convention +/// that aren't done by tablegen. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_M680X0_M680X0CALLINGCONV_H +#define LLVM_LIB_TARGET_M680X0_M680X0CALLINGCONV_H + +#include "MCTargetDesc/M680x0MCTargetDesc.h" + +#include "llvm/CodeGen/CallingConvLower.h" +#include "llvm/IR/CallingConv.h" + +namespace llvm { + +/// Custom state to propagate llvm type info to register CC assigner +class M680x0CCState : public CCState { +public: + const llvm::Function &F; + + M680x0CCState(const llvm::Function &F, CallingConv::ID CC, bool isVarArg, + MachineFunction &MF, SmallVectorImpl &locs, + LLVMContext &C) + : CCState(CC, isVarArg, MF, locs, C), F(F) {} +}; + +/// NOTE this function is used to select registers for formal arguments and call +/// TODO Need to assigne all the pointers first +inline bool CC_M680x0_Any_AssignToReg(unsigned &ValNo, MVT &ValVT, MVT &LocVT, + CCValAssign::LocInfo &LocInfo, + ISD::ArgFlagsTy &ArgFlags, + CCState &State) { + M680x0CCState CCInfo = static_cast(State); + + static const MCPhysReg DataRegList[] = {M680x0::D0, M680x0::D1, M680x0::A0, + M680x0::A1}; + + // Address registers have %a register priority + static const MCPhysReg AddrRegList[] = { + M680x0::A0, + M680x0::A1, + M680x0::D0, + M680x0::D1, + }; + + // SHIT rewrite this + // NOTE This is probably wrong + auto I = CCInfo.F.arg_begin(); + int No = ValNo; + while (No > 0) { + No -= I->getType()->isIntegerTy(64) ? 2 : 1; + I++; + } + + bool isPtr = I != CCInfo.F.arg_end() && I->getType()->isPointerTy(); + + unsigned Reg = + isPtr ? State.AllocateReg(AddrRegList) : State.AllocateReg(DataRegList); + + if (Reg) { + State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo)); + return true; + } + + return false; +} + +} // namespace llvm + +#endif Index: lib/Target/M680x0/M680x0CallingConv.td =================================================================== --- /dev/null +++ lib/Target/M680x0/M680x0CallingConv.td @@ -0,0 +1,116 @@ +//===-- M680X0CallingConv.td - Calling Conventions M680X0 --*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This describes the calling conventions for the M680x0 architectures. These +/// conventions assume Int to be 4 bytes and 4 byte aligned. Short variant is +/// not supported yet. +/// +//===----------------------------------------------------------------------===// + +class CCIfSubtarget + : CCIf" + "(State.getMachineFunction().getSubtarget()).", F), A>; + +//===----------------------------------------------------------------------===// +// Return Value Calling Conventions +//===----------------------------------------------------------------------===// + +/// Return-value conventions common to all M680x0 CC's. +def RetCC_M680x0Common : CallingConv<[ +]>; + +/// M680x0 C return convention. +def RetCC_M680x0_C : CallingConv<[ + CCIfType<[i1], CCPromoteToType>, + CCIfType<[i8], CCAssignToReg<[BD0, BD1]>>, + CCIfType<[i16], CCAssignToReg<[WD0, WD1]>>, + CCIfType<[i32], CCAssignToReg<[D0, D1]>>, + CCDelegateTo +]>; + +/// M680x0 fastcc return convention. +/// TODO: +/// This convention allows to return upto 16 bytes in registers which can be +/// split among 16 1-byte values or used for a single 16-byte value. +def RetCC_M680x0_Fast : CallingConv<[ + CCIfType<[i1], CCPromoteToType>, + CCIfType<[i8], CCAssignToReg<[BD0, BD1]>>, + CCIfType<[i16], CCAssignToReg<[WD0, WD1, WA0, WA1]>>, + CCIfType<[i32], CCAssignToReg<[D0, D1, A0, A1]>>, + CCDelegateTo +]>; + +/// This is the root return-value convention for the M680x0 backend. +def RetCC_M680x0 : CallingConv<[ + CCIfCC<"CallingConv::Fast", CCDelegateTo>, + CCDelegateTo +]>; + +//===----------------------------------------------------------------------===// +// M680x0 C Calling Convention +//===----------------------------------------------------------------------===// + +/// CC_M680x0_Common - In all M680x0 calling conventions, extra integers and FP +/// values are spilled on the stack. +def CC_M680x0_Common : CallingConv<[ + /// Handles byval parameters. + CCIfByVal>, + + /// Integer values get stored in stack slots that are 4 bytes in + /// size and 4-byte aligned. + CCIfType<[i32], CCAssignToStack<4, 4>> +]>; + +def CC_M680x0_Fast : CallingConv<[ + /// Promote i1/i8/i16 arguments to i32. + CCIfType<[i1, i8, i16], CCPromoteToType>, + + /// The 'nest' parameter, if any, is passed in A1. + CCIfNest>, // FIXME is this correct? + + /// Since M680x0 uses %An for pointers and we want them be passed in regs + /// too we have to use custom function. + CCIfType<[i32], CCCustom<"CC_M680x0_Any_AssignToReg">>, + + /// Otherwise, same as everything else. + CCDelegateTo +]>; + +def CC_M680x0_C : CallingConv<[ + /// Promote i1/i8/i16 arguments to i32. + CCIfType<[i1, i8, i16], CCPromoteToType>, + + /// The 'nest' parameter, if any, is passed in A1. + CCIfNest>, // FIXME is this correct? + + /// Use registers only if 'inreg' used and the call is not vararg + CCIfNotVarArg>>>, + + /// Otherwise, same as everything else. + CCDelegateTo +]>; + +/// This is the root argument convention for the M680x0 backend. +def CC_M680x0 : CallingConv<[ + CCIfCC<"CallingConv::Fast", CCDelegateTo>, + CCDelegateTo +]>; + +//===----------------------------------------------------------------------===// +// Callee-saved Registers. +//===----------------------------------------------------------------------===// + +def CSR_NoRegs : CalleeSavedRegs<(add)>; + +// A5 - BP +// A6 - FP +def CSR_STD : CalleeSavedRegs<(add D2, D3, D4, D5, D6, D7, + A2, A3, A4, A5, A6)>; + Index: lib/Target/M680x0/M680x0CollapseMOVEMPass.cpp =================================================================== --- /dev/null +++ lib/Target/M680x0/M680x0CollapseMOVEMPass.cpp @@ -0,0 +1,300 @@ +//===-- createM680x0CollapseMOVEMPass.cpp - Expand MOVEM pass ---*- 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 pass that collapses sequential MOVEM instructions into +/// a single one. +/// +//===----------------------------------------------------------------------===// + +#include "M680x0.h" +#include "M680x0FrameLowering.h" +#include "M680x0InstrInfo.h" +#include "M680x0MachineFunction.h" +#include "M680x0Subtarget.h" + +#include "llvm/Analysis/EHPersonalities.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/IR/GlobalValue.h" + +using namespace llvm; + +#define DEBUG_TYPE "M680x0-collapse-movem" + +namespace { + +enum UpdateType { Ascending, Descending, Intermixed }; + +struct MOVEMState { +private: + MachineBasicBlock::iterator Begin; + MachineBasicBlock::iterator End; + + unsigned Base; + + int Start; + int Stop; + + unsigned Mask; + + enum { None, Load, Store } Type; + +public: + MOVEMState() + : Begin(nullptr), End(nullptr), Base(0), Start(INT_MIN), Stop(INT_MAX), + Mask(0), Type(None) {} + + void SetBegin(MachineBasicBlock::iterator &MI) { + assert(Begin == nullptr); + Begin = MI; + } + + void SetEnd(MachineBasicBlock::iterator &MI) { + assert(End == nullptr); + End = MI; + } + + bool HasBase() { return Base != 0; } + + unsigned GetBase() { + assert(Base); + return Base; + } + + MachineBasicBlock::iterator GetBegin() { + assert(Begin != nullptr); + return Begin; + } + + MachineBasicBlock::iterator GetEnd() { + assert(End != nullptr); + return End; + } + + unsigned GetMask() { return Mask; } + + void SetBase(int Value) { + assert(!HasBase()); + Base = Value; + } + + // You need to call this before Mask update + UpdateType ClassifyUpdateByMask(unsigned Value) { + assert(Value); + + if (Mask == 0) { + return Ascending; + } else if (Mask < Value) { + return Ascending; + } else if (Mask > Value) { + return Descending; + } + + return Intermixed; + } + + bool Update(int O, int M) { + UpdateType Type = ClassifyUpdateByMask(M); + // assert(Type != Intermixed); + if (Type == Intermixed) + return false; + if (Start == INT_MIN) { + Start = Stop = O; + UpdateMask(M); + return true; + } else if (Type == Descending && O == Start - 4) { + Start -= 4; + UpdateMask(M); + return true; + } else if (Type == Ascending && O == Stop + 4) { + Stop += 4; + UpdateMask(M); + return true; + } + + return false; + } + + int GetFinalOffset() { + // Since MOVEM in control mode increment the address on each iteration + assert(Start != INT_MIN); + return Start; + } + + bool UpdateMask(unsigned Value) { + assert(Value == (Value & 0xFFFF) && "Mask must fit 16 bit"); + assert(!(Value & Mask) && + "This is weird, there should be no intersections"); + Mask |= Value; + return true; + } + + void SetLoad() { Type = Load; } + void SetStore() { Type = Store; } + + bool IsLoad() { return Type == Load; } + bool IsStore() { return Type == Store; } +}; + +class M680x0CollapseMOVEM : public MachineFunctionPass { +public: + static char ID; + + const M680x0Subtarget *STI; + const M680x0InstrInfo *TII; + const M680x0RegisterInfo *TRI; + const M680x0MachineFunctionInfo *MFI; + const M680x0FrameLowering *FL; + + M680x0CollapseMOVEM() : MachineFunctionPass(ID) {} + + void Finish(MachineBasicBlock &MBB, MOVEMState &State) { + auto MI = State.GetBegin(); + auto End = State.GetEnd(); + auto DL = MI->getDebugLoc(); + + // No need to delete then add a single instruction + if (std::next(MI) == End) { + State = MOVEMState(); + return; + } + + // Delete all the MOVEM instruction till the end + while (MI != End) { + auto Next = std::next(MI); + MBB.erase(MI); + MI = Next; + } + + // Add a unified one + if (State.IsLoad()) { + BuildMI(MBB, End, DL, TII->get(M680x0::MOVM32mp)) + .addImm(State.GetMask()) + .addImm(State.GetFinalOffset()) + .addReg(State.GetBase()); + } else { + BuildMI(MBB, End, DL, TII->get(M680x0::MOVM32pm)) + .addImm(State.GetFinalOffset()) + .addReg(State.GetBase()) + .addImm(State.GetMask()); + } + + State = MOVEMState(); + } + + bool ProcessMI(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, + MOVEMState &State, unsigned Mask, int Offset, unsigned Reg, + bool IsStore = false) { + if (State.HasBase()) { + // If current Type, Reg, Offset and Mask is in proper order then + // merge in the state + MOVEMState Temp = State; + if (State.IsStore() == IsStore && State.GetBase() == Reg && + State.Update(Offset, Mask)) { + return true; + // Otherwise we Finish processing of the current MOVEM sequance and + // start a new one + } else { + State = Temp; + State.SetEnd(MI); + Finish(MBB, State); + return ProcessMI(MBB, MI, State, Mask, Offset, Reg, IsStore); + } + // If this is the first instruction is sequance then initialize the State + } else if (Reg == TRI->getStackRegister() || + Reg == TRI->getBaseRegister() || + Reg == TRI->getFrameRegister(*MBB.getParent())) { + State.SetBegin(MI); + State.SetBase(Reg); + State.Update(Offset, Mask); + IsStore ? State.SetStore() : State.SetLoad(); + return true; + } + return false; + } + + bool runOnMachineFunction(MachineFunction &MF) override { + STI = &static_cast(MF.getSubtarget()); + TII = STI->getInstrInfo(); + TRI = STI->getRegisterInfo(); + MFI = MF.getInfo(); + FL = STI->getFrameLowering(); + + bool Modified = false; + + MOVEMState State; + + unsigned Mask = 0; + unsigned Reg = 0; + int Offset = 0; + + for (auto &MBB : MF) { + auto MI = MBB.begin(), E = MBB.end(); + while (MI != E) { + // Processing might change current instruction, save next first + auto NMI = std::next(MI); + switch (MI->getOpcode()) { + default: + if (State.HasBase()) { + State.SetEnd(MI); + Finish(MBB, State); + Modified = true; + } + break; + case M680x0::MOVM32jm: + Mask = MI->getOperand(1).getImm(); + Reg = MI->getOperand(0).getReg(); + Offset = 0; + Modified |= ProcessMI(MBB, MI, State, Mask, Offset, Reg, true); + break; + case M680x0::MOVM32pm: + Mask = MI->getOperand(2).getImm(); + Reg = MI->getOperand(1).getReg(); + Offset = MI->getOperand(0).getImm(); + Modified |= ProcessMI(MBB, MI, State, Mask, Offset, Reg, true); + break; + case M680x0::MOVM32mj: + Mask = MI->getOperand(0).getImm(); + Reg = MI->getOperand(1).getReg(); + Offset = 0; + Modified |= ProcessMI(MBB, MI, State, Mask, Offset, Reg, false); + break; + case M680x0::MOVM32mp: + Mask = MI->getOperand(0).getImm(); + Reg = MI->getOperand(2).getReg(); + Offset = MI->getOperand(1).getImm(); + Modified |= ProcessMI(MBB, MI, State, Mask, Offset, Reg, false); + break; + } + MI = NMI; + } + + if (State.HasBase()) { + State.SetEnd(MI); + Finish(MBB, State); + } + } + + return Modified; + } + + StringRef getPassName() const override { + return "M680x0 MOVEM collapser pass"; + } +}; + +char M680x0CollapseMOVEM::ID = 0; +} // anonymous namespace. + +/// Returns an instance of the pseudo instruction expansion pass. +FunctionPass *llvm::createM680x0CollapseMOVEMPass() { + return new M680x0CollapseMOVEM(); +} Index: lib/Target/M680x0/M680x0ExpandPseudo.cpp =================================================================== --- /dev/null +++ lib/Target/M680x0/M680x0ExpandPseudo.cpp @@ -0,0 +1,329 @@ +//===--M680x0ExpandPseudo.cpp - Expand pseudo instructions ------*- 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 pass that expands pseudo instructions into target +/// instructions to allow proper scheduling, if-conversion, other late +/// optimizations, or simply the encoding of the instructions. +/// +//===----------------------------------------------------------------------===// + +#include "M680x0.h" +#include "M680x0FrameLowering.h" +#include "M680x0InstrInfo.h" +#include "M680x0MachineFunction.h" +#include "M680x0Subtarget.h" + +#include "llvm/Analysis/EHPersonalities.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/Passes.h" // For IDs of passes that are preserved. +#include "llvm/IR/GlobalValue.h" + +using namespace llvm; + +#define DEBUG_TYPE "M680x0-expand-pseudos" + +namespace { +class M680x0ExpandPseudo : public MachineFunctionPass { +public: + static char ID; + M680x0ExpandPseudo() : MachineFunctionPass(ID) {} + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesCFG(); + AU.addPreservedID(MachineLoopInfoID); + AU.addPreservedID(MachineDominatorsID); + MachineFunctionPass::getAnalysisUsage(AU); + } + + const M680x0Subtarget *STI; + const M680x0InstrInfo *TII; + const M680x0RegisterInfo *TRI; + const M680x0MachineFunctionInfo *MFI; + const M680x0FrameLowering *FL; + + bool runOnMachineFunction(MachineFunction &Fn) override; + + MachineFunctionProperties getRequiredProperties() const override { + return MachineFunctionProperties().set( + MachineFunctionProperties::Property::NoVRegs); + } + + StringRef getPassName() const override { + return "M680x0 pseudo instruction expansion pass"; + } + +private: + bool ExpandMI(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI); + bool ExpandMBB(MachineBasicBlock &MBB); +}; +char M680x0ExpandPseudo::ID = 0; +} // End anonymous namespace. + +/// If \p MBBI is a pseudo instruction, this method expands +/// it to the corresponding (sequence of) actual instruction(s). +/// \returns true if \p MBBI has been expanded. +bool M680x0ExpandPseudo::ExpandMI(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI) { + MachineInstr &MI = *MBBI; + MachineInstrBuilder MIB(*MI.getParent()->getParent(), MI); + unsigned Opcode = MI.getOpcode(); + DebugLoc DL = MBBI->getDebugLoc(); + switch (Opcode) { + default: + return false; + /// TODO would be nice to infer all these parameters + + case M680x0::MOVXd16d8: + return TII->ExpandMOVX_RR(MIB, MVT::i16, MVT::i8); + case M680x0::MOVXd32d8: + return TII->ExpandMOVX_RR(MIB, MVT::i32, MVT::i8); + case M680x0::MOVXd32d16: + return TII->ExpandMOVX_RR(MIB, MVT::i32, MVT::i16); + + case M680x0::MOVSXd16d8: + return TII->ExpandMOVSZX_RR(MIB, true, MVT::i16, MVT::i8); + case M680x0::MOVSXd32d8: + return TII->ExpandMOVSZX_RR(MIB, true, MVT::i32, MVT::i8); + case M680x0::MOVSXd32d16: + return TII->ExpandMOVSZX_RR(MIB, true, MVT::i32, MVT::i16); + + case M680x0::MOVZXd16d8: + return TII->ExpandMOVSZX_RR(MIB, false, MVT::i16, MVT::i8); + case M680x0::MOVZXd32d8: + return TII->ExpandMOVSZX_RR(MIB, false, MVT::i32, MVT::i8); + case M680x0::MOVZXd32d16: + return TII->ExpandMOVSZX_RR(MIB, false, MVT::i32, MVT::i16); + + case M680x0::MOVSXd16j8: + return TII->ExpandMOVSZX_RM(MIB, true, TII->get(M680x0::MOV8dj), MVT::i16, + MVT::i8); + case M680x0::MOVSXd32j8: + return TII->ExpandMOVSZX_RM(MIB, true, TII->get(M680x0::MOV8dj), MVT::i32, + MVT::i8); + case M680x0::MOVSXd32j16: + return TII->ExpandMOVSZX_RM(MIB, true, TII->get(M680x0::MOV16rj), MVT::i32, + MVT::i16); + + case M680x0::MOVZXd16j8: + return TII->ExpandMOVSZX_RM(MIB, false, TII->get(M680x0::MOV8dj), MVT::i16, + MVT::i8); + case M680x0::MOVZXd32j8: + return TII->ExpandMOVSZX_RM(MIB, false, TII->get(M680x0::MOV8dj), MVT::i32, + MVT::i8); + case M680x0::MOVZXd32j16: + return TII->ExpandMOVSZX_RM(MIB, false, TII->get(M680x0::MOV16rj), MVT::i32, + MVT::i16); + + case M680x0::MOVSXd16p8: + return TII->ExpandMOVSZX_RM(MIB, true, TII->get(M680x0::MOV8dp), MVT::i16, + MVT::i8); + case M680x0::MOVSXd32p8: + return TII->ExpandMOVSZX_RM(MIB, true, TII->get(M680x0::MOV8dp), MVT::i32, + MVT::i8); + case M680x0::MOVSXd32p16: + return TII->ExpandMOVSZX_RM(MIB, true, TII->get(M680x0::MOV16rp), MVT::i32, + MVT::i16); + + case M680x0::MOVZXd16p8: + return TII->ExpandMOVSZX_RM(MIB, false, TII->get(M680x0::MOV8dp), MVT::i16, + MVT::i8); + case M680x0::MOVZXd32p8: + return TII->ExpandMOVSZX_RM(MIB, false, TII->get(M680x0::MOV8dp), MVT::i32, + MVT::i8); + case M680x0::MOVZXd32p16: + return TII->ExpandMOVSZX_RM(MIB, false, TII->get(M680x0::MOV16rp), MVT::i32, + MVT::i16); + + case M680x0::MOVSXd16f8: + return TII->ExpandMOVSZX_RM(MIB, true, TII->get(M680x0::MOV8df), MVT::i16, + MVT::i8); + case M680x0::MOVSXd32f8: + return TII->ExpandMOVSZX_RM(MIB, true, TII->get(M680x0::MOV8df), MVT::i32, + MVT::i8); + case M680x0::MOVSXd32f16: + return TII->ExpandMOVSZX_RM(MIB, true, TII->get(M680x0::MOV16rf), MVT::i32, + MVT::i16); + + case M680x0::MOVZXd16f8: + return TII->ExpandMOVSZX_RM(MIB, false, TII->get(M680x0::MOV8df), MVT::i16, + MVT::i8); + case M680x0::MOVZXd32f8: + return TII->ExpandMOVSZX_RM(MIB, false, TII->get(M680x0::MOV8df), MVT::i32, + MVT::i8); + case M680x0::MOVZXd32f16: + return TII->ExpandMOVSZX_RM(MIB, false, TII->get(M680x0::MOV16rf), MVT::i32, + MVT::i16); + + case M680x0::MOV8cd: + return TII->ExpandCCR(MIB, /* isToCCR */ true); + case M680x0::MOV8dc: + return TII->ExpandCCR(MIB, /* isToCCR */ false); + + case M680x0::MOVM8jm_P: + return TII->ExpandMOVEM(MIB, TII->get(M680x0::MOVM32jm), /* isRM */ false); + case M680x0::MOVM16jm_P: + return TII->ExpandMOVEM(MIB, TII->get(M680x0::MOVM32jm), /* isRM */ false); + case M680x0::MOVM32jm_P: + return TII->ExpandMOVEM(MIB, TII->get(M680x0::MOVM32jm), /* isRM */ false); + + case M680x0::MOVM8pm_P: + return TII->ExpandMOVEM(MIB, TII->get(M680x0::MOVM32pm), /* isRM */ false); + case M680x0::MOVM16pm_P: + return TII->ExpandMOVEM(MIB, TII->get(M680x0::MOVM32pm), /* isRM */ false); + case M680x0::MOVM32pm_P: + return TII->ExpandMOVEM(MIB, TII->get(M680x0::MOVM32pm), /* isRM */ false); + + case M680x0::MOVM8mj_P: + return TII->ExpandMOVEM(MIB, TII->get(M680x0::MOVM32mj), /* isRM */ true); + case M680x0::MOVM16mj_P: + return TII->ExpandMOVEM(MIB, TII->get(M680x0::MOVM32mj), /* isRM */ true); + case M680x0::MOVM32mj_P: + return TII->ExpandMOVEM(MIB, TII->get(M680x0::MOVM32mj), /* isRM */ true); + + case M680x0::MOVM8mp_P: + return TII->ExpandMOVEM(MIB, TII->get(M680x0::MOVM32mp), /* isRM */ true); + case M680x0::MOVM16mp_P: + return TII->ExpandMOVEM(MIB, TII->get(M680x0::MOVM32mp), /* isRM */ true); + case M680x0::MOVM32mp_P: + return TII->ExpandMOVEM(MIB, TII->get(M680x0::MOVM32mp), /* isRM */ true); + + case M680x0::TCRETURNq: + case M680x0::TCRETURNj: { + MachineOperand &JumpTarget = MI.getOperand(0); + MachineOperand &StackAdjust = MI.getOperand(1); + assert(StackAdjust.isImm() && "Expecting immediate value."); + + // Adjust stack pointer. + int StackAdj = StackAdjust.getImm(); + int MaxTCDelta = MFI->getTCReturnAddrDelta(); + int Offset = 0; + assert(MaxTCDelta <= 0 && "MaxTCDelta should never be positive"); + + // Incoporate the retaddr area. + Offset = StackAdj - MaxTCDelta; + assert(Offset >= 0 && "Offset should never be negative"); + + if (Offset) { + // Check for possible merge with preceding ADD instruction. + Offset += FL->mergeSPUpdates(MBB, MBBI, true); + FL->emitSPUpdate(MBB, MBBI, Offset, /*InEpilogue=*/true); + } + + // Jump to label or value in register. + if (Opcode == M680x0::TCRETURNq) { + MachineInstrBuilder MIB = + BuildMI(MBB, MBBI, DL, TII->get(M680x0::TAILJMPq)); + if (JumpTarget.isGlobal()) { + MIB.addGlobalAddress(JumpTarget.getGlobal(), JumpTarget.getOffset(), + JumpTarget.getTargetFlags()); + } else { + assert(JumpTarget.isSymbol()); + MIB.addExternalSymbol(JumpTarget.getSymbolName(), + JumpTarget.getTargetFlags()); + } + } else { + BuildMI(MBB, MBBI, DL, TII->get(M680x0::TAILJMPj)) + .addReg(JumpTarget.getReg(), RegState::Kill); + } + + MachineInstr &NewMI = *std::prev(MBBI); + NewMI.copyImplicitOps(*MBBI->getParent()->getParent(), *MBBI); + + // Delete the pseudo instruction TCRETURN. + MBB.erase(MBBI); + + return true; + } + case M680x0::RET: { + // Adjust stack to erase error code + int64_t StackAdj = MBBI->getOperand(0).getImm(); + MachineInstrBuilder MIB; + + if (StackAdj == 0) { + MIB = BuildMI(MBB, MBBI, DL, TII->get(M680x0::RTS)); + } else if (isUInt<16>(StackAdj)) { + + if (STI->isM68020()) { + llvm_unreachable("RTD is not implemented"); + // MIB = BuildMI(MBB, MBBI, DL, TII->get(M680x0::RTD)).addImm(StackAdj); + } else { + // Copy PC from stack to a free address(A0 or A1) register + // TODO check if it is really free + BuildMI(MBB, MBBI, DL, TII->get(M680x0::MOV32aj), M680x0::A1) + .addReg(M680x0::SP); + + // Adjust SP + FL->emitSPUpdate(MBB, MBBI, StackAdj, /*InEpilogue=*/true); + + // Put the return address on stack + BuildMI(MBB, MBBI, DL, TII->get(M680x0::MOV32ja)) + .addReg(M680x0::SP) + .addReg(M680x0::A1); + + // RTS + BuildMI(MBB, MBBI, DL, TII->get(M680x0::RTS)); + } + } else { + assert(false && "Oh really? You need to pop that much?"); + // RTD can only handle immediates as big as 2**16-1. If we need to pop + // off bytes before the return address, we must do it manually. + // + // BuildMI(MBB, MBBI, DL, TII->get(M680x0::POP32r)).addReg(M680x0::ECX, + // RegState::Define); FL->emitSPUpdate(MBB, MBBI, StackAdj, + // #<{(|InEpilogue=|)}>#true); BuildMI(MBB, MBBI, DL, + // TII->get(M680x0::PUSH32r)).addReg(M680x0::ECX); MIB = BuildMI(MBB, + // MBBI, DL, TII->get(M680x0::RETL)); + } + + // ??? The rest can be ignored? + // for (unsigned I = 1, E = MBBI->getNumOperands(); I != E; ++I) + // MIB.addOperand(MBBI->getOperand(I)); + MBB.erase(MBBI); + return true; + } + } + llvm_unreachable("Previous switch has a fallthrough?"); +} + +/// Expand all pseudo instructions contained in \p MBB. +/// \returns true if any expansion occurred for \p MBB. +bool M680x0ExpandPseudo::ExpandMBB(MachineBasicBlock &MBB) { + bool Modified = false; + + // MBBI may be invalidated by the expansion. + MachineBasicBlock::iterator MBBI = MBB.begin(), E = MBB.end(); + while (MBBI != E) { + MachineBasicBlock::iterator NMBBI = std::next(MBBI); + Modified |= ExpandMI(MBB, MBBI); + MBBI = NMBBI; + } + + return Modified; +} + +bool M680x0ExpandPseudo::runOnMachineFunction(MachineFunction &MF) { + STI = &static_cast(MF.getSubtarget()); + TII = STI->getInstrInfo(); + TRI = STI->getRegisterInfo(); + MFI = MF.getInfo(); + FL = STI->getFrameLowering(); + + bool Modified = false; + for (MachineBasicBlock &MBB : MF) + Modified |= ExpandMBB(MBB); + return Modified; +} + +/// Returns an instance of the pseudo instruction expansion pass. +FunctionPass *llvm::createM680x0ExpandPseudoPass() { + return new M680x0ExpandPseudo(); +} Index: lib/Target/M680x0/M680x0FrameLowering.h =================================================================== --- /dev/null +++ lib/Target/M680x0/M680x0FrameLowering.h @@ -0,0 +1,181 @@ +//===- M680x0FrameLowering.h - Define frame lowering for M680x0 -*- 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 the M680x0 declaration of TargetFrameLowering class. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_M680X0_M680X0FRAMELOWERING_H +#define LLVM_LIB_TARGET_M680X0_M680X0FRAMELOWERING_H + +#include "M680x0.h" + +#include "llvm/CodeGen/TargetFrameLowering.h" + +namespace llvm { +class MachineInstrBuilder; +class MCCFIInstruction; +class M680x0Subtarget; +class M680x0RegisterInfo; + +class M680x0FrameLowering : public TargetFrameLowering { + // Cached subtarget predicates. + const M680x0Subtarget &STI; + const TargetInstrInfo &TII; + const M680x0RegisterInfo *TRI; + + /// Stack slot size in bytes. + unsigned SlotSize; + + unsigned StackPtr; + + /// If we're forcing a stack realignment we can't rely on just the frame + /// info, we need to know the ABI stack alignment as well in case we have a + /// call out. Otherwise just make sure we have some alignment - we'll go + /// with the minimum SlotSize. + uint64_t calculateMaxStackAlign(const MachineFunction &MF) const; + + /// Adjusts the stack pointer using LEA, SUB, or ADD. + MachineInstrBuilder BuildStackAdjustment(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, + const DebugLoc &DL, int64_t Offset, + bool InEpilogue) const; + + /// Aligns the stack pointer by ANDing it with -MaxAlign. + void BuildStackAlignAND(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, const DebugLoc &DL, + unsigned Reg, uint64_t MaxAlign) const; + + /// Wraps up getting a CFI index and building a MachineInstr for it. + void BuildCFI(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, + const DebugLoc &DL, const MCCFIInstruction &CFIInst) const; + + void emitCalleeSavedFrameMoves(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, + const DebugLoc &DL) const; + + unsigned getPSPSlotOffsetFromSP(const MachineFunction &MF) const; + +public: + explicit M680x0FrameLowering(const M680x0Subtarget &sti, unsigned Alignment); + + static const M680x0FrameLowering *create(const M680x0Subtarget &ST); + + /// This method is called during prolog/epilog code insertion to eliminate + /// call frame setup and destroy pseudo instructions (but only if the Target + /// is using them). It is responsible for eliminating these instructions, + /// replacing them with concrete instructions. This method need only be + /// implemented if using call frame setup/destroy pseudo instructions. + /// Returns an iterator pointing to the instruction after the replaced one. + MachineBasicBlock::iterator + eliminateCallFramePseudoInstr(MachineFunction &MF, MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI) const override; + + /// Insert prolog code into the function. + void emitPrologue(MachineFunction &MF, MachineBasicBlock &MBB) const override; + + /// Insert epilog code into the function. + void emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const override; + + /// This method determines which of the registers reported by + /// TargetRegisterInfo::getCalleeSavedRegs() should actually get saved. + /// The default implementation checks populates the \p SavedRegs bitset with + /// all registers which are modified in the function, targets may override + /// this function to save additional registers. + /// This method also sets up the register scavenger ensuring there is a free + /// register or a frameindex available. + void determineCalleeSaves(MachineFunction &MF, BitVector &SavedRegs, + RegScavenger *RS = nullptr) const override; + + /// Allows target to override spill slot assignment logic. If implemented, + /// assignCalleeSavedSpillSlots() should assign frame slots to all CSI + /// entries and return true. If this method returns false, spill slots will + /// be assigned using generic implementation. assignCalleeSavedSpillSlots() + /// may add, delete or rearrange elements of CSI. + bool + assignCalleeSavedSpillSlots(MachineFunction &MF, + const TargetRegisterInfo *TRI, + std::vector &CSI) const override; + + /// Issues instruction(s) to spill all callee saved registers and returns + /// true if it isn't possible / profitable to do so by issuing a series of + /// store instructions via storeRegToStackSlot(). Returns false otherwise. + bool spillCalleeSavedRegisters(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + const std::vector &CSI, + const TargetRegisterInfo *TRI) const override; + + /// Issues instruction(s) to restore all callee saved registers and returns + /// true if it isn't possible / profitable to do so by issuing a series of + /// load instructions via loadRegToStackSlot(). Returns false otherwise. + bool + restoreCalleeSavedRegisters(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + std::vector &CSI, + const TargetRegisterInfo *TRI) const override; + + /// Return true if the specified function should have a dedicated frame + /// pointer register. This is true if the function has variable sized + /// allocas, if it needs dynamic stack realignment, if frame pointer + /// elimination is disabled, or if the frame address is taken. + bool hasFP(const MachineFunction &MF) const override; + + /// Under normal circumstances, when a frame pointer is not required, we + /// reserve argument space for call sites in the function immediately on + /// entry to the current function. This eliminates the need for add/sub sp + /// brackets around call sites. Returns true if the call frame is included as + /// part of the stack frame. + bool hasReservedCallFrame(const MachineFunction &MF) const override; + + /// If there is a reserved call frame, the call frame pseudos can be + /// simplified. Having a FP, as in the default implementation, is not + /// sufficient here since we can't always use it. Use a more nuanced + /// condition. + bool canSimplifyCallFramePseudos(const MachineFunction &MF) const override; + + // Do we need to perform FI resolution for this function. Normally, this is + // required only when the function has any stack objects. However, FI + // resolution actually has another job, not apparent from the title - it + // resolves callframe setup/destroy that were not simplified earlier. + // + // So, this is required for M680x0 functions that have push sequences even + // when there are no stack objects. + bool needsFrameIndexResolution(const MachineFunction &MF) const override; + + /// This method should return the base register and offset used to reference + /// a frame index location. The offset is returned directly, and the base + /// register is returned via FrameReg. + int getFrameIndexReference(const MachineFunction &MF, int FI, + unsigned &FrameReg) const override; + + /// Check the instruction before/after the passed instruction. If + /// it is an ADD/SUB/LEA instruction it is deleted argument and the + /// stack adjustment is returned as a positive value for ADD/LEA and + /// a negative for SUB. + int mergeSPUpdates(MachineBasicBlock &MBB, MachineBasicBlock::iterator &MBBI, + bool doMergeWithPrevious) const; + + /// Emit a series of instructions to increment / decrement the stack + /// pointer by a constant value. + void emitSPUpdate(MachineBasicBlock &MBB, MachineBasicBlock::iterator &MBBI, + int64_t NumBytes, bool InEpilogue) const; + + /// TODO + /// Order the symbols in the local stack. + /// We want to place the local stack objects in some sort of sensible order. + /// The heuristic we use is to try and pack them according to static number + /// of uses and size in order to minimize code size. + // void orderFrameObjects(const MachineFunction &MF, + // SmallVectorImpl &ObjectsToAllocate) const + // override; +}; +} // namespace llvm + +#endif Index: lib/Target/M680x0/M680x0FrameLowering.cpp =================================================================== --- /dev/null +++ lib/Target/M680x0/M680x0FrameLowering.cpp @@ -0,0 +1,931 @@ +//===-- M680x0FrameLowering.cpp - M680x0 Frame Information ------*- 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 the M680x0 implementation of TargetFrameLowering class. +/// +//===----------------------------------------------------------------------===// + +#include "M680x0FrameLowering.h" + +#include "M680x0InstrBuilder.h" +#include "M680x0InstrInfo.h" +#include "M680x0MachineFunction.h" +#include "M680x0Subtarget.h" + +#include "llvm/ADT/SmallSet.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/Function.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Target/TargetOptions.h" + +using namespace llvm; + +M680x0FrameLowering::M680x0FrameLowering(const M680x0Subtarget &STI, + unsigned Alignment) + : TargetFrameLowering(StackGrowsDown, Alignment, -4), STI(STI), + TII(*STI.getInstrInfo()), TRI(STI.getRegisterInfo()) { + SlotSize = STI.getSlotSize(); + StackPtr = TRI->getStackRegister(); +} + +bool M680x0FrameLowering::hasFP(const MachineFunction &MF) const { + const MachineFrameInfo &MFI = MF.getFrameInfo(); + const TargetRegisterInfo *TRI = STI.getRegisterInfo(); + + return MF.getTarget().Options.DisableFramePointerElim(MF) || + MFI.hasVarSizedObjects() || MFI.isFrameAddressTaken() || + TRI->needsStackRealignment(MF); +} + +// FIXME not only pushes.... +bool M680x0FrameLowering::hasReservedCallFrame( + const MachineFunction &MF) const { + return !MF.getFrameInfo().hasVarSizedObjects() && + !MF.getInfo()->getHasPushSequences(); +} + +bool M680x0FrameLowering::canSimplifyCallFramePseudos( + const MachineFunction &MF) const { + return hasReservedCallFrame(MF) || + (hasFP(MF) && !TRI->needsStackRealignment(MF)) || + TRI->hasBasePointer(MF); +} + +bool M680x0FrameLowering::needsFrameIndexResolution( + const MachineFunction &MF) const { + return MF.getFrameInfo().hasStackObjects() || + MF.getInfo()->getHasPushSequences(); +} + +// NOTE: this only has a subset of the full frame index logic. In +// particular, the FI < 0 and AfterFPPop logic is handled in +// M680x0RegisterInfo::eliminateFrameIndex, but not here. Possibly +// (probably?) it should be moved into here. +int M680x0FrameLowering::getFrameIndexReference(const MachineFunction &MF, + int FI, + unsigned &FrameReg) const { + const MachineFrameInfo &MFI = MF.getFrameInfo(); + + // We can't calculate offset from frame pointer if the stack is realigned, + // so enforce usage of stack/base pointer. The base pointer is used when we + // have dynamic allocas in addition to dynamic realignment. + if (TRI->hasBasePointer(MF)) + FrameReg = TRI->getBaseRegister(); + else if (TRI->needsStackRealignment(MF)) + FrameReg = TRI->getStackRegister(); + else + FrameReg = TRI->getFrameRegister(MF); + + // Offset will hold the offset from the stack pointer at function entry to the + // object. + // We need to factor in additional offsets applied during the prologue to the + // frame, base, and stack pointer depending on which is used. + int Offset = MFI.getObjectOffset(FI) - getOffsetOfLocalArea(); + const M680x0MachineFunctionInfo *MMFI = + MF.getInfo(); + uint64_t StackSize = MFI.getStackSize(); + bool HasFP = hasFP(MF); + + if (TRI->hasBasePointer(MF)) { + assert(HasFP && "VLAs and dynamic stack realign, but no FP?!"); + if (FI < 0) { + // Skip the saved FP. + return Offset + SlotSize; + } else { + assert((-(Offset + StackSize)) % MFI.getObjectAlignment(FI) == 0); + return Offset + StackSize; + } + } else if (TRI->needsStackRealignment(MF)) { + if (FI < 0) { + // Skip the saved FP. + return Offset + SlotSize; + } else { + assert((-(Offset + StackSize)) % MFI.getObjectAlignment(FI) == 0); + return Offset + StackSize; + } + // FIXME: Support tail calls + } else { + if (!HasFP) + return Offset + StackSize; + + // Skip the saved FP. + Offset += SlotSize; + + // Skip the RETADDR move area + int TailCallReturnAddrDelta = MMFI->getTCReturnAddrDelta(); + if (TailCallReturnAddrDelta < 0) + Offset -= TailCallReturnAddrDelta; + } + + return Offset; +} + +static unsigned getSUBriOpcode(int64_t Imm) { return M680x0::SUB32ri; } + +static unsigned getADDriOpcode(int64_t Imm) { return M680x0::ADD32ri; } + +static unsigned getSUBrrOpcode() { return M680x0::SUB32rr; } + +static unsigned getADDrrOpcode() { return M680x0::ADD32rr; } + +static unsigned getANDriOpcode(int64_t Imm) { return M680x0::AND32di; } + +static unsigned getLEArOpcode() { return M680x0::LEA32p; } + +static unsigned getMOVrrOpcode() { return M680x0::MOV32rr; } + +/// findDeadCallerSavedReg - Return a caller-saved register that isn't live +/// when it reaches the "return" instruction. We can then pop a stack object +/// to this register without worry about clobbering it. +static unsigned findDeadCallerSavedReg(MachineBasicBlock &MBB, + MachineBasicBlock::iterator &MBBI, + const M680x0RegisterInfo *TRI) { + const MachineFunction *MF = MBB.getParent(); + if (MF->callsEHReturn()) + return 0; + + const TargetRegisterClass &AvailableRegs = *TRI->getRegsForTailCall(*MF); + + if (MBBI == MBB.end()) + return 0; + + switch (MBBI->getOpcode()) { + default: + return 0; + case TargetOpcode::PATCHABLE_RET: + case M680x0::RET: { + SmallSet Uses; + + for (unsigned i = 0, e = MBBI->getNumOperands(); i != e; ++i) { + MachineOperand &MO = MBBI->getOperand(i); + if (!MO.isReg() || MO.isDef()) + continue; + unsigned Reg = MO.getReg(); + if (!Reg) + continue; + for (MCRegAliasIterator AI(Reg, TRI, true); AI.isValid(); ++AI) + Uses.insert(*AI); + } + + for (auto CS : AvailableRegs) + if (!Uses.count(CS)) + return CS; + } + } + + return 0; +} + +static bool isRegLiveIn(MachineBasicBlock &MBB, unsigned Reg) { + for (MachineBasicBlock::RegisterMaskPair RegMask : MBB.liveins()) { + if (RegMask.PhysReg == Reg) + return true; + } + + return false; +} + +uint64_t +M680x0FrameLowering::calculateMaxStackAlign(const MachineFunction &MF) const { + const MachineFrameInfo &MFI = MF.getFrameInfo(); + uint64_t MaxAlign = MFI.getMaxAlignment(); // Desired stack alignment. + unsigned StackAlign = getStackAlignment(); // ABI alignment + if (MF.getFunction().hasFnAttribute("stackrealign")) { + if (MFI.hasCalls()) + MaxAlign = (StackAlign > MaxAlign) ? StackAlign : MaxAlign; + else if (MaxAlign < SlotSize) + MaxAlign = SlotSize; + } + return MaxAlign; +} + +void M680x0FrameLowering::BuildStackAlignAND(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, + const DebugLoc &DL, unsigned Reg, + uint64_t MaxAlign) const { + uint64_t Val = -MaxAlign; + unsigned AndOp = getANDriOpcode(Val); + unsigned MovOp = getMOVrrOpcode(); + + // This function is normally used with SP which is Address Register, but AND, + // or any other logical instructions in M680x0 do not support ARs so we need + // to use a temp Data Register to perform the op. + unsigned Tmp = M680x0::D0; + + BuildMI(MBB, MBBI, DL, TII.get(MovOp), Tmp) + .addReg(Reg) + .setMIFlag(MachineInstr::FrameSetup); + + MachineInstr *MI = BuildMI(MBB, MBBI, DL, TII.get(AndOp), Tmp) + .addReg(Tmp) + .addImm(Val) + .setMIFlag(MachineInstr::FrameSetup); + + // The CCR implicit def is dead. + MI->getOperand(3).setIsDead(); + + BuildMI(MBB, MBBI, DL, TII.get(MovOp), Reg) + .addReg(Tmp) + .setMIFlag(MachineInstr::FrameSetup); +} + +MachineBasicBlock::iterator M680x0FrameLowering::eliminateCallFramePseudoInstr( + MachineFunction &MF, MachineBasicBlock &MBB, + MachineBasicBlock::iterator I) const { + bool reserveCallFrame = hasReservedCallFrame(MF); + unsigned Opcode = I->getOpcode(); + bool isDestroy = Opcode == TII.getCallFrameDestroyOpcode(); + DebugLoc DL = I->getDebugLoc(); + uint64_t Amount = !reserveCallFrame ? I->getOperand(0).getImm() : 0; + uint64_t InternalAmt = (isDestroy && Amount) ? I->getOperand(1).getImm() : 0; + I = MBB.erase(I); + + if (!reserveCallFrame) { + // If the stack pointer can be changed after prologue, turn the + // adjcallstackup instruction into a 'sub %SP, ' and the + // adjcallstackdown instruction into 'add %SP, ' + + // We need to keep the stack aligned properly. To do this, we round the + // amount of space needed for the outgoing arguments up to the next + // alignment boundary. + unsigned StackAlign = getStackAlignment(); + Amount = alignTo(Amount, StackAlign); + + MachineModuleInfo &MMI = MF.getMMI(); + const auto &Fn = MF.getFunction(); + bool DwarfCFI = MMI.hasDebugInfo() || Fn.needsUnwindTableEntry(); + + // If we have any exception handlers in this function, and we adjust + // the SP before calls, we may need to indicate this to the unwinder + // using GNU_ARGS_SIZE. Note that this may be necessary even when + // Amount == 0, because the preceding function may have set a non-0 + // GNU_ARGS_SIZE. + // TODO: We don't need to reset this between subsequent functions, + // if it didn't change. + bool HasDwarfEHHandlers = !MF.getLandingPads().empty(); + + if (HasDwarfEHHandlers && !isDestroy && + MF.getInfo()->getHasPushSequences()) { + BuildCFI(MBB, I, DL, + MCCFIInstruction::createGnuArgsSize(nullptr, Amount)); + } + + if (Amount == 0) + return I; + + // Factor out the amount that gets handled inside the sequence + // (Pushes of argument for frame setup, callee pops for frame destroy) + Amount -= InternalAmt; + + // TODO: This is needed only if we require precise CFA. + // If this is a callee-pop calling convention, emit a CFA adjust for + // the amount the callee popped. + if (isDestroy && InternalAmt && DwarfCFI && !hasFP(MF)) + BuildCFI(MBB, I, DL, + MCCFIInstruction::createAdjustCfaOffset(nullptr, -InternalAmt)); + + // Add Amount to SP to destroy a frame, or subtract to setup. + int64_t StackAdjustment = isDestroy ? Amount : -Amount; + int64_t CfaAdjustment = -StackAdjustment; + + if (StackAdjustment) { + // Merge with any previous or following adjustment instruction. Note: the + // instructions merged with here do not have CFI, so their stack + // adjustments do not feed into CfaAdjustment. + StackAdjustment += mergeSPUpdates(MBB, I, true); + StackAdjustment += mergeSPUpdates(MBB, I, false); + + if (StackAdjustment) { + BuildStackAdjustment(MBB, I, DL, StackAdjustment, false); + } + } + + if (DwarfCFI && !hasFP(MF)) { + // If we don't have FP, but need to generate unwind information, + // we need to set the correct CFA offset after the stack adjustment. + // How much we adjust the CFA offset depends on whether we're emitting + // CFI only for EH purposes or for debugging. EH only requires the CFA + // offset to be correct at each call site, while for debugging we want + // it to be more precise. + + // TODO: When not using precise CFA, we also need to adjust for the + // InternalAmt here. + if (CfaAdjustment) { + BuildCFI( + MBB, I, DL, + MCCFIInstruction::createAdjustCfaOffset(nullptr, CfaAdjustment)); + } + } + + return I; + } + + if (isDestroy && InternalAmt) { + // If we are performing frame pointer elimination and if the callee pops + // something off the stack pointer, add it back. We do this until we have + // more advanced stack pointer tracking ability. + // We are not tracking the stack pointer adjustment by the callee, so make + // sure we restore the stack pointer immediately after the call, there may + // be spill code inserted between the CALL and ADJCALLSTACKUP instructions. + MachineBasicBlock::iterator CI = I; + MachineBasicBlock::iterator B = MBB.begin(); + while (CI != B && !std::prev(CI)->isCall()) + --CI; + BuildStackAdjustment(MBB, CI, DL, -InternalAmt, /*InEpilogue=*/false); + } + + return I; +} + +/// Emit a series of instructions to increment / decrement the stack pointer by +/// a constant value. +void M680x0FrameLowering::emitSPUpdate(MachineBasicBlock &MBB, + MachineBasicBlock::iterator &MBBI, + int64_t NumBytes, + bool InEpilogue) const { + bool isSub = NumBytes < 0; + uint64_t Offset = isSub ? -NumBytes : NumBytes; + + uint64_t Chunk = (1LL << 31) - 1; + DebugLoc DL = MBB.findDebugLoc(MBBI); + + while (Offset) { + if (Offset > Chunk) { + // Rather than emit a long series of instructions for large offsets, + // load the offset into a register and do one sub/add + unsigned Reg = 0; + + if (isSub && !isRegLiveIn(MBB, M680x0::D0)) + Reg = (unsigned)(M680x0::D0); + else + Reg = findDeadCallerSavedReg(MBB, MBBI, TRI); + + if (Reg) { + unsigned Opc = M680x0::MOV32ri; + BuildMI(MBB, MBBI, DL, TII.get(Opc), Reg).addImm(Offset); + Opc = isSub ? getSUBrrOpcode() : getADDrrOpcode(); + MachineInstr *MI = BuildMI(MBB, MBBI, DL, TII.get(Opc), StackPtr) + .addReg(StackPtr) + .addReg(Reg); + // ??? still no CCR + MI->getOperand(3).setIsDead(); // The CCR implicit def is dead. + Offset = 0; + continue; + } + } + + uint64_t ThisVal = std::min(Offset, Chunk); + + MachineInstrBuilder MI = BuildStackAdjustment( + MBB, MBBI, DL, isSub ? -ThisVal : ThisVal, InEpilogue); + if (isSub) + MI.setMIFlag(MachineInstr::FrameSetup); + else + MI.setMIFlag(MachineInstr::FrameDestroy); + + Offset -= ThisVal; + } +} + +int M680x0FrameLowering::mergeSPUpdates(MachineBasicBlock &MBB, + MachineBasicBlock::iterator &MBBI, + bool doMergeWithPrevious) const { + if ((doMergeWithPrevious && MBBI == MBB.begin()) || + (!doMergeWithPrevious && MBBI == MBB.end())) + return 0; + + MachineBasicBlock::iterator PI = doMergeWithPrevious ? std::prev(MBBI) : MBBI; + MachineBasicBlock::iterator NI = + doMergeWithPrevious ? nullptr : std::next(MBBI); + unsigned Opc = PI->getOpcode(); + int Offset = 0; + + if (!doMergeWithPrevious && NI != MBB.end() && + NI->getOpcode() == TargetOpcode::CFI_INSTRUCTION) { + // Don't merge with the next instruction if it has CFI. + return Offset; + } + + if (Opc == M680x0::ADD32ri && PI->getOperand(0).getReg() == StackPtr) { + assert(PI->getOperand(1).getReg() == StackPtr); + Offset += PI->getOperand(2).getImm(); + MBB.erase(PI); + if (!doMergeWithPrevious) + MBBI = NI; + // TODO check this + // } else if (Opc == M680x0::LEA32p && + // PI->getOperand(0).getReg() == StackPtr && + // PI->getOperand(2).getReg() == StackPtr) { + // Offset += PI->getOperand(1).getImm(); + // MBB.erase(PI); + // if (!doMergeWithPrevious) MBBI = NI; + } else if (Opc == M680x0::SUB32ri && PI->getOperand(0).getReg() == StackPtr) { + assert(PI->getOperand(1).getReg() == StackPtr); + Offset -= PI->getOperand(2).getImm(); + MBB.erase(PI); + if (!doMergeWithPrevious) + MBBI = NI; + } + + return Offset; +} + +MachineInstrBuilder M680x0FrameLowering::BuildStackAdjustment( + MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, + const DebugLoc &DL, int64_t Offset, bool InEpilogue) const { + assert(Offset != 0 && "zero offset stack adjustment requested"); + + // ??? in the original code for M680x0 Atom uses lea to adjust stack as an + // optimization, can be be this applied for M680x0? + + bool IsSub = Offset < 0; + uint64_t AbsOffset = IsSub ? -Offset : Offset; + unsigned Opc = IsSub ? getSUBriOpcode(AbsOffset) : getADDriOpcode(AbsOffset); + + MachineInstrBuilder MI = BuildMI(MBB, MBBI, DL, TII.get(Opc), StackPtr) + .addReg(StackPtr) + .addImm(AbsOffset); + // FIXME ATM there is no CCR in these inst + MI->getOperand(3).setIsDead(); // The CCR implicit def is dead. + return MI; +} + +void M680x0FrameLowering::BuildCFI(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, + const DebugLoc &DL, + const MCCFIInstruction &CFIInst) const { + MachineFunction &MF = *MBB.getParent(); + unsigned CFIIndex = MF.addFrameInst(CFIInst); + BuildMI(MBB, MBBI, DL, TII.get(TargetOpcode::CFI_INSTRUCTION)) + .addCFIIndex(CFIIndex); +} + +void M680x0FrameLowering::emitCalleeSavedFrameMoves( + MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, + const DebugLoc &DL) const { + MachineFunction &MF = *MBB.getParent(); + MachineFrameInfo &MFI = MF.getFrameInfo(); + MachineModuleInfo &MMI = MF.getMMI(); + const MCRegisterInfo *MRI = MMI.getContext().getRegisterInfo(); + + // Add callee saved registers to move list. + const std::vector &CSI = MFI.getCalleeSavedInfo(); + if (CSI.empty()) + return; + + // Calculate offsets. + for (std::vector::const_iterator I = CSI.begin(), + E = CSI.end(); + I != E; ++I) { + int64_t Offset = MFI.getObjectOffset(I->getFrameIdx()); + unsigned Reg = I->getReg(); + + unsigned DwarfReg = MRI->getDwarfRegNum(Reg, true); + BuildCFI(MBB, MBBI, DL, + MCCFIInstruction::createOffset(nullptr, DwarfReg, Offset)); + } +} + +void M680x0FrameLowering::emitPrologue(MachineFunction &MF, + MachineBasicBlock &MBB) const { + assert(&STI == &MF.getSubtarget() && + "MF used frame lowering for wrong subtarget"); + + MachineBasicBlock::iterator MBBI = MBB.begin(); + MachineFrameInfo &MFI = MF.getFrameInfo(); + const auto &Fn = MF.getFunction(); + MachineModuleInfo &MMI = MF.getMMI(); + M680x0MachineFunctionInfo *MMFI = MF.getInfo(); + uint64_t MaxAlign = calculateMaxStackAlign(MF); // Desired stack alignment. + uint64_t StackSize = MFI.getStackSize(); // Number of bytes to allocate. + EHPersonality Personality = EHPersonality::Unknown; + if (Fn.hasPersonalityFn()) + Personality = classifyEHPersonality(Fn.getPersonalityFn()); + bool HasFP = hasFP(MF); + bool NeedsDwarfCFI = MMI.hasDebugInfo() || Fn.needsUnwindTableEntry(); + unsigned FramePtr = TRI->getFrameRegister(MF); + const unsigned MachineFramePtr = FramePtr; + unsigned BasePtr = TRI->getBaseRegister(); + + // Debug location must be unknown since the first debug location is used + // to determine the end of the prologue. + DebugLoc DL; + + // Add RETADDR move area to callee saved frame size. + int TailCallReturnAddrDelta = MMFI->getTCReturnAddrDelta(); + + if (TailCallReturnAddrDelta < 0) { + MMFI->setCalleeSavedFrameSize(MMFI->getCalleeSavedFrameSize() - + TailCallReturnAddrDelta); + } + + // Insert stack pointer adjustment for later moving of return addr. Only + // applies to tail call optimized functions where the callee argument stack + // size is bigger than the callers. + if (TailCallReturnAddrDelta < 0) { + BuildStackAdjustment(MBB, MBBI, DL, TailCallReturnAddrDelta, + /*InEpilogue=*/false) + .setMIFlag(MachineInstr::FrameSetup); + } + + // Mapping for machine moves: + // + // DST: VirtualFP AND + // SRC: VirtualFP => DW_CFA_def_cfa_offset + // ELSE => DW_CFA_def_cfa + // + // SRC: VirtualFP AND + // DST: Register => DW_CFA_def_cfa_register + // + // ELSE + // OFFSET < 0 => DW_CFA_offset_extended_sf + // REG < 64 => DW_CFA_offset + Reg + // ELSE => DW_CFA_offset_extended + + uint64_t NumBytes = 0; + int stackGrowth = -SlotSize; + + if (HasFP) { + // Calculate required stack adjustment. + uint64_t FrameSize = StackSize - SlotSize; + // If required, include space for extra hidden slot for stashing base + // pointer. + if (MMFI->getRestoreBasePointer()) + FrameSize += SlotSize; + + NumBytes = FrameSize - MMFI->getCalleeSavedFrameSize(); + + // Callee-saved registers are pushed on stack before the stack is realigned. + if (TRI->needsStackRealignment(MF)) + NumBytes = alignTo(NumBytes, MaxAlign); + + // Get the offset of the stack slot for the FP register, which is + // guaranteed to be the last slot by processFunctionBeforeFrameFinalized. + // Update the frame offset adjustment. + MFI.setOffsetAdjustment(-NumBytes); + + // Save FP into the appropriate stack slot. + BuildMI(MBB, MBBI, DL, TII.get(M680x0::PUSH32r)) + .addReg(MachineFramePtr, RegState::Kill) + .setMIFlag(MachineInstr::FrameSetup); + + if (NeedsDwarfCFI) { + // Mark the place where FP was saved. + // Define the current CFA rule to use the provided offset. + assert(StackSize); + BuildCFI(MBB, MBBI, DL, + MCCFIInstruction::createDefCfaOffset(nullptr, 2 * stackGrowth)); + + // Change the rule for the FramePtr to be an "offset" rule. + int DwarfFramePtr = TRI->getDwarfRegNum(MachineFramePtr, true); + assert(DwarfFramePtr > 0); + BuildCFI(MBB, MBBI, DL, + MCCFIInstruction::createOffset(nullptr, DwarfFramePtr, + 2 * stackGrowth)); + } + + // Update FP with the new base value. + BuildMI(MBB, MBBI, DL, TII.get(M680x0::MOV32aa), FramePtr) + .addReg(StackPtr) + .setMIFlag(MachineInstr::FrameSetup); + + if (NeedsDwarfCFI) { + // Mark effective beginning of when frame pointer becomes valid. + // Define the current CFA to use the FP register. + unsigned DwarfFramePtr = TRI->getDwarfRegNum(MachineFramePtr, true); + BuildCFI(MBB, MBBI, DL, + MCCFIInstruction::createDefCfaRegister(nullptr, DwarfFramePtr)); + } + + // Mark the FramePtr as live-in in every block. Don't do this again for + // funclet prologues. + for (MachineBasicBlock &EveryMBB : MF) + EveryMBB.addLiveIn(MachineFramePtr); + } else { + NumBytes = StackSize - MMFI->getCalleeSavedFrameSize(); + } + + // Skip the callee-saved push instructions. + bool PushedRegs = false; + int StackOffset = 2 * stackGrowth; + + while (MBBI != MBB.end() && MBBI->getFlag(MachineInstr::FrameSetup) && + MBBI->getOpcode() == M680x0::PUSH32r) { + PushedRegs = true; + ++MBBI; + + if (!HasFP && NeedsDwarfCFI) { + // Mark callee-saved push instruction. + // Define the current CFA rule to use the provided offset. + assert(StackSize); + BuildCFI(MBB, MBBI, DL, + MCCFIInstruction::createDefCfaOffset(nullptr, StackOffset)); + StackOffset += stackGrowth; + } + } + + // Realign stack after we pushed callee-saved registers (so that we'll be + // able to calculate their offsets from the frame pointer). + if (TRI->needsStackRealignment(MF)) { + assert(HasFP && "There should be a frame pointer if stack is realigned."); + BuildStackAlignAND(MBB, MBBI, DL, StackPtr, MaxAlign); + } + + // If there is an SUB32ri of SP immediately before this instruction, merge + // the two. This can be the case when tail call elimination is enabled and + // the callee has more arguments then the caller. + NumBytes -= mergeSPUpdates(MBB, MBBI, true); + + // Adjust stack pointer: ESP -= numbytes. + emitSPUpdate(MBB, MBBI, -(int64_t)NumBytes, /*InEpilogue=*/false); + + unsigned SPOrEstablisher = StackPtr; + + // If we need a base pointer, set it up here. It's whatever the value + // of the stack pointer is at this point. Any variable size objects + // will be allocated after this, so we can still use the base pointer + // to reference locals. + if (TRI->hasBasePointer(MF)) { + // Update the base pointer with the current stack pointer. + BuildMI(MBB, MBBI, DL, TII.get(M680x0::MOV32aa), BasePtr) + .addReg(SPOrEstablisher) + .setMIFlag(MachineInstr::FrameSetup); + if (MMFI->getRestoreBasePointer()) { + // Stash value of base pointer. Saving SP instead of FP shortens + // dependence chain. Used by SjLj EH. + unsigned Opm = M680x0::MOV32ja; + addRegIndirectWithDisp(BuildMI(MBB, MBBI, DL, TII.get(Opm)), FramePtr, + true, MMFI->getRestoreBasePointerOffset()) + .addReg(SPOrEstablisher) + .setMIFlag(MachineInstr::FrameSetup); + } + } + + if (((!HasFP && NumBytes) || PushedRegs) && NeedsDwarfCFI) { + // Mark end of stack pointer adjustment. + if (!HasFP && NumBytes) { + // Define the current CFA rule to use the provided offset. + assert(StackSize); + BuildCFI(MBB, MBBI, DL, + MCCFIInstruction::createDefCfaOffset(nullptr, + -StackSize + stackGrowth)); + } + + // Emit DWARF info specifying the offsets of the callee-saved registers. + if (PushedRegs) + emitCalleeSavedFrameMoves(MBB, MBBI, DL); + } + + // TODO interrupts... + // M680x0 Interrupt handling function cannot assume anything about the + // direction flag (DF in CCR register). Clear this flag by creating "cld" + // instruction in each prologue of interrupt handler function. + // + // FIXME: Create "cld" instruction only in these cases: + // 1. The interrupt handling function uses any of the "rep" instructions. + // 2. Interrupt handling function calls another function. + // + // if (Fn.getCallingConv() == CallingConv::M680x0_INTR) + // BuildMI(MBB, MBBI, DL, TII.get(M680x0::CLD)) + // .setMIFlag(MachineInstr::FrameSetup); +} + +static bool isTailCallOpcode(unsigned Opc) { + return Opc == M680x0::TCRETURNj || Opc == M680x0::TCRETURNq; +} + +void M680x0FrameLowering::emitEpilogue(MachineFunction &MF, + MachineBasicBlock &MBB) const { + const MachineFrameInfo &MFI = MF.getFrameInfo(); + M680x0MachineFunctionInfo *MMFI = MF.getInfo(); + MachineBasicBlock::iterator MBBI = MBB.getFirstTerminator(); + Optional RetOpcode; + if (MBBI != MBB.end()) + RetOpcode = MBBI->getOpcode(); + DebugLoc DL; + if (MBBI != MBB.end()) + DL = MBBI->getDebugLoc(); + unsigned FramePtr = TRI->getFrameRegister(MF); + unsigned MachineFramePtr = FramePtr; + + // Get the number of bytes to allocate from the FrameInfo. + uint64_t StackSize = MFI.getStackSize(); + uint64_t MaxAlign = calculateMaxStackAlign(MF); + unsigned CSSize = MMFI->getCalleeSavedFrameSize(); + uint64_t NumBytes = 0; + + if (hasFP(MF)) { + // Calculate required stack adjustment. + uint64_t FrameSize = StackSize - SlotSize; + NumBytes = FrameSize - CSSize; + + // Callee-saved registers were pushed on stack before the stack was + // realigned. + if (TRI->needsStackRealignment(MF)) + NumBytes = alignTo(FrameSize, MaxAlign); + + // Pop FP. + BuildMI(MBB, MBBI, DL, TII.get(M680x0::POP32r), MachineFramePtr) + .setMIFlag(MachineInstr::FrameDestroy); + } else { + NumBytes = StackSize - CSSize; + } + + // Skip the callee-saved pop instructions. + while (MBBI != MBB.begin()) { + MachineBasicBlock::iterator PI = std::prev(MBBI); + unsigned Opc = PI->getOpcode(); + + if ((Opc != M680x0::POP32r || !PI->getFlag(MachineInstr::FrameDestroy)) && + Opc != M680x0::DBG_VALUE && !PI->isTerminator()) + break; + + --MBBI; + } + MachineBasicBlock::iterator FirstCSPop = MBBI; + + if (MBBI != MBB.end()) + DL = MBBI->getDebugLoc(); + + // If there is an ADD32ri or SUB32ri of SP immediately before this + // instruction, merge the two instructions. + if (NumBytes || MFI.hasVarSizedObjects()) + NumBytes += mergeSPUpdates(MBB, MBBI, true); + + // If dynamic alloca is used, then reset SP to point to the last callee-saved + // slot before popping them off! Same applies for the case, when stack was + // realigned. Don't do this if this was a funclet epilogue, since the funclets + // will not do realignment or dynamic stack allocation. + if ((TRI->needsStackRealignment(MF) || MFI.hasVarSizedObjects())) { + if (TRI->needsStackRealignment(MF)) + MBBI = FirstCSPop; + uint64_t LEAAmount = -CSSize; + + // 'move %FramePtr, SP' will not be recognized as an epilogue sequence. + // However, we may use this sequence if we have a frame pointer because the + // effects of the prologue can safely be undone. + if (LEAAmount != 0) { + unsigned Opc = getLEArOpcode(); + addRegIndirectWithDisp(BuildMI(MBB, MBBI, DL, TII.get(Opc), StackPtr), + FramePtr, false, LEAAmount); + --MBBI; + } else { + unsigned Opc = (M680x0::MOV32rr); + BuildMI(MBB, MBBI, DL, TII.get(Opc), StackPtr).addReg(FramePtr); + --MBBI; + } + } else if (NumBytes) { + // Adjust stack pointer back: SP += numbytes. + emitSPUpdate(MBB, MBBI, NumBytes, /*InEpilogue=*/true); + --MBBI; + } + + if (!RetOpcode || !isTailCallOpcode(*RetOpcode)) { + // Add the return addr area delta back since we are not tail calling. + int Offset = -1 * MMFI->getTCReturnAddrDelta(); + assert(Offset >= 0 && "TCDelta should never be positive"); + if (Offset) { + MBBI = MBB.getFirstTerminator(); + + // Check for possible merge with preceding ADD instruction. + Offset += mergeSPUpdates(MBB, MBBI, true); + emitSPUpdate(MBB, MBBI, Offset, /*InEpilogue=*/true); + } + } +} + +void M680x0FrameLowering::determineCalleeSaves(MachineFunction &MF, + BitVector &SavedRegs, + RegScavenger *RS) const { + TargetFrameLowering::determineCalleeSaves(MF, SavedRegs, RS); + + MachineFrameInfo &MFI = MF.getFrameInfo(); + + M680x0MachineFunctionInfo *M680x0FI = MF.getInfo(); + int64_t TailCallReturnAddrDelta = M680x0FI->getTCReturnAddrDelta(); + + if (TailCallReturnAddrDelta < 0) { + // create RETURNADDR area + // arg + // arg + // RETADDR + // { ... + // RETADDR area + // ... + // } + // [FP] + MFI.CreateFixedObject(-TailCallReturnAddrDelta, + TailCallReturnAddrDelta - SlotSize, true); + } + + // Spill the BasePtr if it's used. + if (TRI->hasBasePointer(MF)) { + SavedRegs.set(TRI->getBaseRegister()); + } +} + +bool M680x0FrameLowering::assignCalleeSavedSpillSlots( + MachineFunction &MF, const TargetRegisterInfo *TRI, + std::vector &CSI) const { + MachineFrameInfo &MFI = MF.getFrameInfo(); + M680x0MachineFunctionInfo *M680x0FI = MF.getInfo(); + + int SpillSlotOffset = + getOffsetOfLocalArea() + M680x0FI->getTCReturnAddrDelta(); + + if (hasFP(MF)) { + // emitPrologue always spills frame register the first thing. + SpillSlotOffset -= SlotSize; + MFI.CreateFixedSpillStackObject(SlotSize, SpillSlotOffset); + + // Since emitPrologue and emitEpilogue will handle spilling and restoring of + // the frame register, we can delete it from CSI list and not have to worry + // about avoiding it later. + unsigned FPReg = TRI->getFrameRegister(MF); + for (unsigned i = 0; i < CSI.size(); ++i) { + if (TRI->regsOverlap(CSI[i].getReg(), FPReg)) { + CSI.erase(CSI.begin() + i); + break; + } + } + } + + // The rest is fine + return false; +} + +bool M680x0FrameLowering::spillCalleeSavedRegisters( + MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, + const std::vector &CSI, + const TargetRegisterInfo *TRI) const { + auto &MRI = *static_cast(TRI); + auto DL = MBB.findDebugLoc(MI); + + int FI = 0; + unsigned Mask = 0; + for (size_t i = 0; i < CSI.size(); ++i) { + FI = std::max(FI, CSI[i].getFrameIdx()); + unsigned Reg = CSI[i].getReg(); + unsigned Shift = MRI.getSpillRegisterOrder(Reg); + Mask |= 1 << Shift; + } + + auto I = + addFrameReference(BuildMI(MBB, MI, DL, TII.get(M680x0::MOVM32pm)), FI) + .addImm(Mask) + .setMIFlag(MachineInstr::FrameSetup); + + // Append implicit registers and mem locations + const MachineFunction &MF = *MBB.getParent(); + const MachineRegisterInfo &RI = MF.getRegInfo(); + for (size_t i = 0; i < CSI.size(); ++i) { + unsigned Reg = CSI[i].getReg(); + bool isLiveIn = RI.isLiveIn(Reg); + if (!isLiveIn) + MBB.addLiveIn(Reg); + I.addReg(Reg, isLiveIn ? RegState::Implicit : RegState::ImplicitKill); + addMemOperand(I, CSI[i].getFrameIdx(), 0); + } + + return true; +} + +bool M680x0FrameLowering::restoreCalleeSavedRegisters( + MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, + std::vector &CSI, const TargetRegisterInfo *TRI) const { + auto &MRI = *static_cast(TRI); + auto DL = MBB.findDebugLoc(MI); + + int FI = 0; + unsigned Mask = 0; + for (size_t i = 0; i < CSI.size(); ++i) { + FI = std::max(FI, CSI[i].getFrameIdx()); + unsigned Reg = CSI[i].getReg(); + unsigned Shift = MRI.getSpillRegisterOrder(Reg); + Mask |= 1 << Shift; + } + + auto I = addFrameReference( + BuildMI(MBB, MI, DL, TII.get(M680x0::MOVM32mp)).addImm(Mask), FI) + .setMIFlag(MachineInstr::FrameDestroy); + + // Append implicit registers and mem locations + for (size_t i = 0; i < CSI.size(); ++i) { + I.addReg(CSI[i].getReg(), RegState::ImplicitDefine); + addMemOperand(I, CSI[i].getFrameIdx(), 0); + } + + return true; +} Index: lib/Target/M680x0/M680x0ISelDAGToDAG.cpp =================================================================== --- /dev/null +++ lib/Target/M680x0/M680x0ISelDAGToDAG.cpp @@ -0,0 +1,944 @@ +//===- M680x0ISelDAGToDAG.cpp - M680x0 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. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file defines an instruction selector for the M680X0 target. +/// +//===----------------------------------------------------------------------===// + +#include "M680x0.h" + +#include "M680x0MachineFunction.h" +#include "M680x0RegisterInfo.h" +#include "M680x0TargetMachine.h" + +#include "llvm/CodeGen/MachineConstantPool.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/SelectionDAGISel.h" +#include "llvm/CodeGen/SelectionDAGNodes.h" +#include "llvm/IR/CFG.h" +#include "llvm/IR/GlobalValue.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/IR/Type.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Target/TargetMachine.h" + +using namespace llvm; + +#define DEBUG_TYPE "m680x0-isel" + +namespace { + +/// isInt - Checks if an integer fits into the given bit width. +/// non-templated version +/// FIXME move it somewhere +inline bool isInt(unsigned N, int64_t x) { + return N >= 64 || + (-(INT64_C(1) << (N - 1)) <= x && x < (INT64_C(1) << (N - 1))); +} + +// For reference, the full order of operands for memory references is: +// (Operand), Displacement, Base, Index, Scale +struct M680x0ISelAddressMode { + enum AddrType { + ARI, // Address Register Indirect + ARIPI, // Address Register Indirect with Postincrement + ARIPD, // Address Register Indirect with Postdecrement + ARID, // Address Register Indirect with Displacement + ARII, // Address Register Indirect with Index + PCD, // Program Counter Indirect with Displacement + PCI, // Program Counter Indirect with Index + AL, // Absolute + } AM; + + enum { RegBase, FrameIndexBase } BaseType; + + int64_t Disp; + + // This is really a union, discriminated by BaseType! + SDValue BaseReg; + int BaseFrameIndex; + + SDValue IndexReg; + unsigned Scale; + + const GlobalValue *GV; + const Constant *CP; + const BlockAddress *BlockAddr; + const char *ES; + MCSymbol *MCSym; + int JT; + unsigned Align; // CP alignment. + + unsigned char SymbolFlags; // M680x0II::MO_* + + M680x0ISelAddressMode(AddrType AT) + : AM(AT), BaseType(RegBase), Disp(0), BaseFrameIndex(0), IndexReg(), + Scale(1), GV(nullptr), CP(nullptr), BlockAddr(nullptr), ES(nullptr), + MCSym(nullptr), JT(-1), Align(0), SymbolFlags(M680x0II::MO_NO_FLAG) {} + + bool hasSymbolicDisplacement() const { + return GV != nullptr || CP != nullptr || ES != nullptr || + MCSym != nullptr || JT != -1 || BlockAddr != nullptr; + } + + bool hasBase() const { + return BaseType == FrameIndexBase || BaseReg.getNode() != nullptr; + } + + bool hasFrameIndex() const { return BaseType == FrameIndexBase; } + + bool hasBaseReg() const { + return BaseType == RegBase && BaseReg.getNode() != nullptr; + } + + bool hasIndexReg() const { + return BaseType == RegBase && IndexReg.getNode() != nullptr; + } + + /// True if address mode type supports displacement + bool isDispAddrType() const { + return AM == ARII || AM == PCI || AM == ARID || AM == PCD || AM == AL; + } + + unsigned getDispSize() const { + switch (AM) { + default: + return 0; + case ARII: + case PCI: + return 8; + // These two in the next chip generations can hold upto 32 bit + case ARID: + case PCD: + return 16; + case AL: + return 32; + } + } + + bool hasDisp() const { return getDispSize() != 0; } + bool isDisp8() const { return getDispSize() == 8; } + bool isDisp16() const { return getDispSize() == 16; } + bool isDisp32() const { return getDispSize() == 32; } + + /// Return true if this addressing mode is already PC-relative. + bool isPCRelative() const { + if (BaseType != RegBase) + return false; + if (RegisterSDNode *RegNode = + dyn_cast_or_null(BaseReg.getNode())) + return RegNode->getReg() == M680x0::PC; + return false; + } + + void setBaseReg(SDValue Reg) { + BaseType = RegBase; + BaseReg = Reg; + } + + void setIndexReg(SDValue Reg) { IndexReg = Reg; } + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + void dump() { + dbgs() << "M680x0ISelAddressMode " << this; + dbgs() << "\nDisp: " << Disp; + dbgs() << ", BaseReg: "; + if (BaseReg.getNode()) + BaseReg.getNode()->dump(); + else + dbgs() << "null"; + dbgs() << ", BaseFI: " << BaseFrameIndex; + dbgs() << ", IndexReg: "; + if (IndexReg.getNode()) { + IndexReg.getNode()->dump(); + } else { + dbgs() << "null"; + dbgs() << ", Scale: " << Scale; + } + dbgs() << '\n'; + } +#endif +}; +} // end anonymous namespace + +namespace { + +class M680x0DAGToDAGISel : public SelectionDAGISel { +public: + explicit M680x0DAGToDAGISel(M680x0TargetMachine &TM) + : SelectionDAGISel(TM), Subtarget(nullptr) {} + + StringRef getPassName() const override { + return "M680X0 DAG->DAG Pattern Instruction Selection"; + } + + bool runOnMachineFunction(MachineFunction &MF) override; + +private: + /// Keep a pointer to the M680x0Subtarget around so that we can + /// make the right decision when generating code for different targets. + const M680x0Subtarget *Subtarget; + +// Include the pieces autogenerated from the target description. +#include "M680x0GenDAGISel.inc" + + /// getTargetMachine - Return a reference to the TargetMachine, casted + /// to the target-specific type. + const M680x0TargetMachine &getTargetMachine() { + return static_cast(TM); + } + + void Select(SDNode *N) override; + + // Insert instructions to initialize the global base register in the + // first MBB of the function. + // HMM... do i need this? + void initGlobalBaseReg(MachineFunction &MF); + + bool foldOffsetIntoAddress(uint64_t Offset, M680x0ISelAddressMode &AM); + + bool matchLoadInAddress(LoadSDNode *N, M680x0ISelAddressMode &AM); + bool matchAddress(SDValue N, M680x0ISelAddressMode &AM); + bool matchAddressBase(SDValue N, M680x0ISelAddressMode &AM); + bool matchAddressRecursively(SDValue N, M680x0ISelAddressMode &AM, + unsigned Depth); + bool matchADD(SDValue N, M680x0ISelAddressMode &AM, unsigned Depth); + bool matchWrapper(SDValue N, M680x0ISelAddressMode &AM); + + std::pair selectNode(SDNode *Node); + + bool SelectARI(SDNode *Parent, SDValue N, SDValue &Base); + bool SelectARIPI(SDNode *Parent, SDValue N, SDValue &Base); + bool SelectARIPD(SDNode *Parent, SDValue N, SDValue &Base); + bool SelectARID(SDNode *Parent, SDValue N, SDValue &Imm, SDValue &Base); + bool SelectARII(SDNode *Parent, SDValue N, SDValue &Imm, SDValue &Base, + SDValue &Index); + bool SelectAL(SDNode *Parent, SDValue N, SDValue &Sym); + bool SelectPCD(SDNode *Parent, SDValue N, SDValue &Imm); + bool SelectPCI(SDNode *Parent, SDValue N, SDValue &Imm, SDValue &Index); + + // If Address Mode represents Frame Index store FI in Disp and + // Displacement bit size in Base. These values are read symmetrically by + // M680x0RegisterInfo::eliminateFrameIndex method + inline bool getFrameIndexAddress(M680x0ISelAddressMode &AM, const SDLoc &DL, + SDValue &Disp, SDValue &Base) { + if (AM.BaseType == M680x0ISelAddressMode::FrameIndexBase) { + Disp = getI32Imm(AM.Disp, DL); + Base = CurDAG->getTargetFrameIndex( + AM.BaseFrameIndex, TLI->getPointerTy(CurDAG->getDataLayout())); + return true; + } + + return false; + } + + // Gets a symbol plus optional displacement + inline bool getSymbolicDisplacement(M680x0ISelAddressMode &AM, + const SDLoc &DL, SDValue &Sym) { + if (AM.GV) { + Sym = CurDAG->getTargetGlobalAddress(AM.GV, SDLoc(), MVT::i32, AM.Disp, + AM.SymbolFlags); + return true; + } else if (AM.CP) { + Sym = CurDAG->getTargetConstantPool(AM.CP, MVT::i32, AM.Align, AM.Disp, + AM.SymbolFlags); + return true; + } else if (AM.ES) { + assert(!AM.Disp && "Non-zero displacement is ignored with ES."); + Sym = CurDAG->getTargetExternalSymbol(AM.ES, MVT::i32, AM.SymbolFlags); + return true; + } else if (AM.MCSym) { + assert(!AM.Disp && "Non-zero displacement is ignored with MCSym."); + assert(AM.SymbolFlags == 0 && "oo"); + Sym = CurDAG->getMCSymbol(AM.MCSym, MVT::i32); + return true; + } else if (AM.JT != -1) { + assert(!AM.Disp && "Non-zero displacement is ignored with JT."); + Sym = CurDAG->getTargetJumpTable(AM.JT, MVT::i32, AM.SymbolFlags); + return true; + } else if (AM.BlockAddr) { + Sym = CurDAG->getTargetBlockAddress(AM.BlockAddr, MVT::i32, AM.Disp, + AM.SymbolFlags); + return true; + } + + return false; + } + + /// Return a target constant with the specified value of type i8. + inline SDValue getI8Imm(int64_t Imm, const SDLoc &DL) { + return CurDAG->getTargetConstant(Imm, DL, MVT::i8); + } + + /// Return a target constant with the specified value of type i8. + inline SDValue getI16Imm(int64_t Imm, const SDLoc &DL) { + return CurDAG->getTargetConstant(Imm, DL, MVT::i16); + } + + /// Return a target constant with the specified value, of type i32. + inline SDValue getI32Imm(int64_t Imm, const SDLoc &DL) { + return CurDAG->getTargetConstant(Imm, DL, MVT::i32); + } + + /// Return a reference to the TargetInstrInfo, casted to the target-specific + /// type. + const M680x0InstrInfo *getInstrInfo() const { + return Subtarget->getInstrInfo(); + } + + /// Return an SDNode that returns the value of the global base register. + /// Output instructions required to initialize the global base register, + /// if necessary. + SDNode *getGlobalBaseReg(); +}; +} // namespace + +bool M680x0DAGToDAGISel::runOnMachineFunction(MachineFunction &MF) { + Subtarget = &static_cast(MF.getSubtarget()); + return SelectionDAGISel::runOnMachineFunction(MF); +} + +/// This pass converts a legalized DAG into a M680x0-specific DAG, +/// ready for instruction scheduling. +FunctionPass *llvm::createM680x0ISelDag(M680x0TargetMachine &TM) { + return new M680x0DAGToDAGISel(TM); +} + +static bool doesDispFitFI(M680x0ISelAddressMode &AM) { + if (!AM.isDispAddrType()) + return false; + // -1 to make sure that resolved FI will fit into Disp field + return isInt(AM.getDispSize() - 1, AM.Disp); +} + +static bool doesDispFit(M680x0ISelAddressMode &AM, int64_t Val) { + if (!AM.isDispAddrType()) + return false; + return isInt(AM.getDispSize(), Val); +} + +/// Return an SDNode that returns the value of the global base register. +/// Output instructions required to initialize the global base register, +/// if necessary. +SDNode *M680x0DAGToDAGISel::getGlobalBaseReg() { + unsigned GlobalBaseReg = getInstrInfo()->getGlobalBaseReg(MF); + auto &DL = MF->getDataLayout(); + return CurDAG->getRegister(GlobalBaseReg, TLI->getPointerTy(DL)).getNode(); +} + +bool M680x0DAGToDAGISel::foldOffsetIntoAddress(uint64_t Offset, + M680x0ISelAddressMode &AM) { + // Cannot combine ExternalSymbol displacements with integer offsets. + if (Offset != 0 && (AM.ES || AM.MCSym)) + return false; + + int64_t Val = AM.Disp + Offset; + + if (doesDispFit(AM, Val)) { + AM.Disp = Val; + return true; + } + + return false; +} + +//===----------------------------------------------------------------------===// +// Matchers +//===----------------------------------------------------------------------===// + +/// Helper for MatchAddress. Add the specified node to the +/// specified addressing mode without any further recursion. +bool M680x0DAGToDAGISel::matchAddressBase(SDValue N, + M680x0ISelAddressMode &AM) { + // Is the base register already occupied? + if (AM.hasBase()) { + // If so, check to see if the scale index register is set. + if (!AM.hasIndexReg()) { + AM.IndexReg = N; + AM.Scale = 1; + return true; + } + + // Otherwise, we cannot select it. + return false; + } + + // Default, generate it as a register. + AM.BaseType = M680x0ISelAddressMode::RegBase; + AM.BaseReg = N; + return true; +} + +/// TODO +/// Have no idea how it is node with M680x0 ATM +/// Here is some description: +/// https://lists.debian.org/debian-68k/2007/11/msg00071.html +bool M680x0DAGToDAGISel::matchLoadInAddress(LoadSDNode *N, + M680x0ISelAddressMode &AM) { + // SDValue Address = N->getOperand(1); + + // load gs:0 -> GS segment register. + // load fs:0 -> FS segment register. + // + // This optimization is valid because the GNU TLS model defines that + // gs:0 (or fs:0 on M680x0-64) contains its own address. + // For more information see http://people.redhat.com/drepper/tls.pdf + // if (ConstantSDNode *C = dyn_cast(Address)) + // if (C->getSExtValue() == 0 && + // Subtarget->isTargetGlibc()) + // switch (N->getPointerInfo().getAddrSpace()) { + // case 256: + // AM.Segment = CurDAG->getRegister(M680x0::GS, MVT::i16); + // return false; + // case 257: + // AM.Segment = CurDAG->getRegister(M680x0::FS, MVT::i16); + // return false; + // // Address space 258 is not handled here, because it is not used to + // // address TLS areas. + // } + + return false; +} + +bool M680x0DAGToDAGISel::matchAddressRecursively(SDValue N, + M680x0ISelAddressMode &AM, + unsigned Depth) { + SDLoc DL(N); + + // Limit recursion. + if (Depth > 5) + return matchAddressBase(N, AM); + + // If this is already a %PC relative address, we can only merge immediates + // into it. Instead of handling this in every case, we handle it here. + // PC relative addressing: %PC + 16-bit displacement! + if (AM.isPCRelative()) { + // FIXME: JumpTable and ExternalSymbol address currently don't like + // displacements. It isn't very important, but this should be fixed for + // consistency. + // if (!(AM.ES || AM.MCSym) && AM.JT != -1) + // return true; + + if (ConstantSDNode *Cst = dyn_cast(N)) + if (foldOffsetIntoAddress(Cst->getSExtValue(), AM)) + return true; + return false; + } + + switch (N.getOpcode()) { + default: + break; + + case ISD::Constant: { + uint64_t Val = cast(N)->getSExtValue(); + if (foldOffsetIntoAddress(Val, AM)) + return true; + break; + } + + case M680x0ISD::Wrapper: + case M680x0ISD::WrapperPC: + if (matchWrapper(N, AM)) + return true; + break; + + case ISD::LOAD: + if (matchLoadInAddress(cast(N), AM)) + return true; + break; + + case ISD::OR: + // We want to look through a transform in InstCombine and DAGCombiner that + // turns 'add' into 'or', so we can treat this 'or' exactly like an 'add'. + // Example: (or (and x, 1), (shl y, 3)) --> (add (and x, 1), (shl y, 3)) + // An 'lea' can then be used to match the shift (multiply) and add: + // and $1, %esi + // lea (%rsi, %rdi, 8), %rax + if (CurDAG->haveNoCommonBitsSet(N.getOperand(0), N.getOperand(1)) && + matchADD(N, AM, Depth)) + return true; + break; + + case ISD::ADD: + if (matchADD(N, AM, Depth)) + return true; + break; + + case ISD::FrameIndex: + if (AM.isDispAddrType() && AM.BaseType == M680x0ISelAddressMode::RegBase && + AM.BaseReg.getNode() == nullptr && doesDispFitFI(AM)) { + AM.BaseType = M680x0ISelAddressMode::FrameIndexBase; + AM.BaseFrameIndex = cast(N)->getIndex(); + return true; + } + break; + } + + return matchAddressBase(N, AM); +} + +/// Add the specified node to the specified addressing mode, returning true if +/// it cannot be done. This just pattern matches for the addressing mode. +bool M680x0DAGToDAGISel::matchAddress(SDValue N, M680x0ISelAddressMode &AM) { + if (!matchAddressRecursively(N, AM, 0)) + return false; + + // Post-processing: Convert lea(,%reg,2) to lea(%reg,%reg), which has + // a smaller encoding and avoids a scaled-index. + // TODO make sure it is an indexed mode + // if (AM.Scale == 2 && + // AM.BaseType == M680x0ISelAddressMode::RegBase && + // AM.BaseReg.getNode() == nullptr) { + // AM.BaseReg = AM.IndexReg; + // AM.Scale = 1; + // } + + // Post-processing: Convert foo to foo(%pc), even in non-PIC mode, + // because it has a smaller encoding. + // TODO: Which other code models can use this? + // FIXME this must be done only if PC* modes are currently being matched + // if (TM.getCodeModel() == CodeModel::Small && + // Subtarget->is64Bit() && + // AM.Scale == 1 && + // AM.BaseType == M680x0ISelAddressMode::RegBase && + // AM.BaseReg.getNode() == nullptr && + // AM.IndexReg.getNode() == nullptr && + // AM.SymbolFlags == M680x0II::MO_NO_FLAG && + // AM.hasSymbolicDisplacement()) + // AM.BaseReg = CurDAG->getRegister(M680x0::PC, MVT::i64); + + return true; +} + +bool M680x0DAGToDAGISel::matchADD(SDValue N, M680x0ISelAddressMode &AM, + unsigned Depth) { + // Add an artificial use to this node so that we can keep track of + // it if it gets CSE'd with a different node. + HandleSDNode Handle(N); + + M680x0ISelAddressMode Backup = AM; + if (matchAddressRecursively(N.getOperand(0), AM, Depth + 1) && + matchAddressRecursively(Handle.getValue().getOperand(1), AM, Depth + 1)) { + return true; + } + AM = Backup; + + // Try again after commuting the operands. + if (matchAddressRecursively(Handle.getValue().getOperand(1), AM, Depth + 1) && + matchAddressRecursively(Handle.getValue().getOperand(0), AM, Depth + 1)) { + return true; + } + AM = Backup; + + // If we couldn't fold both operands into the address at the same time, + // see if we can just put each operand into a register and fold at least + // the add. + if (!AM.hasBase() && !AM.hasIndexReg()) { + N = Handle.getValue(); + AM.BaseReg = N.getOperand(0); + AM.IndexReg = N.getOperand(1); + AM.Scale = 1; + return true; + } + + N = Handle.getValue(); + return false; +} + +/// Try to match M680x0ISD::Wrapper and M680x0ISD::WrapperPC nodes into an +/// addressing mode. These wrap things that will resolve down into a symbol +/// reference. If no match is possible, this returns true, otherwise it returns +/// false. +bool M680x0DAGToDAGISel::matchWrapper(SDValue N, M680x0ISelAddressMode &AM) { + // If the addressing mode already has a symbol as the displacement, we can + // never match another symbol. + if (AM.hasSymbolicDisplacement()) + return false; + + SDValue N0 = N.getOperand(0); + + if (N.getOpcode() == M680x0ISD::WrapperPC) { + + // If cannot match here just restore the old version + M680x0ISelAddressMode Backup = AM; + + if (AM.hasBase()) { + return false; + } + + if (GlobalAddressSDNode *G = dyn_cast(N0)) { + AM.GV = G->getGlobal(); + AM.SymbolFlags = G->getTargetFlags(); + if (!foldOffsetIntoAddress(G->getOffset(), AM)) { + AM = Backup; + return false; + } + } else if (ConstantPoolSDNode *CP = dyn_cast(N0)) { + AM.CP = CP->getConstVal(); + AM.Align = CP->getAlignment(); + AM.SymbolFlags = CP->getTargetFlags(); + if (!foldOffsetIntoAddress(CP->getOffset(), AM)) { + AM = Backup; + return false; + } + } else if (ExternalSymbolSDNode *S = dyn_cast(N0)) { + AM.ES = S->getSymbol(); + AM.SymbolFlags = S->getTargetFlags(); + } else if (auto *S = dyn_cast(N0)) { + AM.MCSym = S->getMCSymbol(); + } else if (JumpTableSDNode *J = dyn_cast(N0)) { + AM.JT = J->getIndex(); + AM.SymbolFlags = J->getTargetFlags(); + } else if (BlockAddressSDNode *BA = dyn_cast(N0)) { + AM.BlockAddr = BA->getBlockAddress(); + AM.SymbolFlags = BA->getTargetFlags(); + if (!foldOffsetIntoAddress(BA->getOffset(), AM)) { + AM = Backup; + return false; + } + } else + llvm_unreachable("Unhandled symbol reference node."); + + AM.setBaseReg(CurDAG->getRegister(M680x0::PC, MVT::i32)); + return true; + } + + // This wrapper requires 32bit disp/imm field for Medium CM + if (!AM.isDisp32()) { + return false; + } + + if (N.getOpcode() == M680x0ISD::Wrapper) { + if (GlobalAddressSDNode *G = dyn_cast(N0)) { + AM.GV = G->getGlobal(); + AM.Disp += G->getOffset(); + AM.SymbolFlags = G->getTargetFlags(); + } else if (ConstantPoolSDNode *CP = dyn_cast(N0)) { + AM.CP = CP->getConstVal(); + AM.Align = CP->getAlignment(); + AM.Disp += CP->getOffset(); + AM.SymbolFlags = CP->getTargetFlags(); + } else if (ExternalSymbolSDNode *S = dyn_cast(N0)) { + AM.ES = S->getSymbol(); + AM.SymbolFlags = S->getTargetFlags(); + } else if (auto *S = dyn_cast(N0)) { + AM.MCSym = S->getMCSymbol(); + } else if (JumpTableSDNode *J = dyn_cast(N0)) { + AM.JT = J->getIndex(); + AM.SymbolFlags = J->getTargetFlags(); + } else if (BlockAddressSDNode *BA = dyn_cast(N0)) { + AM.BlockAddr = BA->getBlockAddress(); + AM.Disp += BA->getOffset(); + AM.SymbolFlags = BA->getTargetFlags(); + } else + llvm_unreachable("Unhandled symbol reference node."); + return true; + } + + return false; +} + +//===----------------------------------------------------------------------===// +// Selectors +//===----------------------------------------------------------------------===// + +void M680x0DAGToDAGISel::Select(SDNode *Node) { + unsigned Opcode = Node->getOpcode(); + SDLoc DL(Node); + + LLVM_DEBUG(dbgs() << "Selecting: "; Node->dump(CurDAG); dbgs() << '\n'); + + if (Node->isMachineOpcode()) { + LLVM_DEBUG(dbgs() << "== "; Node->dump(CurDAG); dbgs() << '\n'); + Node->setNodeId(-1); + return; // Already selected. + } + + switch (Opcode) { + default: + break; + + case M680x0ISD::GlobalBaseReg: + ReplaceNode(Node, getGlobalBaseReg()); + return; + } + + SelectCode(Node); +} + +bool M680x0DAGToDAGISel::SelectARIPI(SDNode *Parent, SDValue N, SDValue &Base) { + LLVM_DEBUG(dbgs() << "Selecting ARIPI: "); + LLVM_DEBUG(dbgs() << "NOT IMPLEMENTED\n"); + return false; +} + +bool M680x0DAGToDAGISel::SelectARIPD(SDNode *Parent, SDValue N, SDValue &Base) { + LLVM_DEBUG(dbgs() << "Selecting ARIPD: "); + LLVM_DEBUG(dbgs() << "NOT IMPLEMENTED\n"); + return false; +} + +bool M680x0DAGToDAGISel::SelectARID(SDNode *Parent, SDValue N, SDValue &Disp, + SDValue &Base) { + LLVM_DEBUG(dbgs() << "Selecting ARID: "); + M680x0ISelAddressMode AM(M680x0ISelAddressMode::ARID); + + if (!matchAddress(N, AM)) + return false; + + if (AM.isPCRelative()) { + LLVM_DEBUG(dbgs() << "REJECT: Cannot match PC relative address\n"); + return false; + } + + // If this is a frame index, grab it + if (getFrameIndexAddress(AM, SDLoc(N), Disp, Base)) { + LLVM_DEBUG(dbgs() << "SUCCESS matched FI\n"); + return true; + } + + if (AM.hasIndexReg()) { + LLVM_DEBUG(dbgs() << "REJECT: Cannot match Index\n"); + return false; + } + + if (!AM.hasBaseReg()) { + LLVM_DEBUG(dbgs() << "REJECT: No Base reg\n"); + return false; + } + + if (getSymbolicDisplacement(AM, SDLoc(N), Disp)) { + assert(!AM.Disp && "Should not be any displacement"); + LLVM_DEBUG(dbgs() << "SUCCESS, matched Symbol\n"); + return true; + } + + // Give a chance to ARI + if (AM.Disp == 0) { + LLVM_DEBUG(dbgs() << "REJECT: No displacement\n"); + return false; + } + + Base = AM.BaseReg; + Disp = getI16Imm(AM.Disp, SDLoc(N)); + + LLVM_DEBUG(dbgs() << "SUCCESS\n"); + return true; +} + +static bool isAddressBase(const SDValue &N) { + unsigned Op = N.getOpcode(); + if (Op == M680x0ISD::Wrapper || Op == M680x0ISD::WrapperPC || + Op == M680x0ISD::GlobalBaseReg) { + return true; + } + + if (Op == ISD::ADD || Op == ISD::ADDC) { + for (unsigned i = 0; i < N.getNumOperands(); ++i) { + if (isAddressBase(N.getOperand(i))) { + return true; + } + } + } + + return false; +} + +bool M680x0DAGToDAGISel::SelectARII(SDNode *Parent, SDValue N, SDValue &Disp, + SDValue &Base, SDValue &Index) { + M680x0ISelAddressMode AM(M680x0ISelAddressMode::ARII); + LLVM_DEBUG(dbgs() << "Selecting ARII: "); + + if (!matchAddress(N, AM)) + return false; + + if (AM.isPCRelative()) { + LLVM_DEBUG(dbgs() << "REJECT: PC relative\n"); + return false; + } + + if (!AM.hasIndexReg()) { + LLVM_DEBUG(dbgs() << "REJECT: No Index\n"); + return false; + } + + if (!AM.hasBaseReg()) { + LLVM_DEBUG(dbgs() << "REJECT: No Base\n"); + return false; + } + + if (!isAddressBase(AM.BaseReg) && isAddressBase(AM.IndexReg)) { + Base = AM.IndexReg; + Index = AM.BaseReg; + } else { + Base = AM.BaseReg; + Index = AM.IndexReg; + } + + if (AM.hasSymbolicDisplacement()) { + LLVM_DEBUG(dbgs() << "REJECT, Cannot match symbolic displacement\n"); + return false; + } + + // The idea here is that we want to use ARII without displacement only if + // necessary like memory operations, otherwise this must be lowered into + // addition + if (AM.Disp == 0 && (!Parent || (Parent->getOpcode() != ISD::LOAD && + Parent->getOpcode() != ISD::STORE))) { + LLVM_DEBUG(dbgs() << "REJECT: Displacement is Zero\n"); + return false; + } + + Disp = getI8Imm(AM.Disp, SDLoc(N)); + + LLVM_DEBUG(dbgs() << "SUCCESS\n"); + return true; +} + +bool M680x0DAGToDAGISel::SelectAL(SDNode *Parent, SDValue N, SDValue &Sym) { + LLVM_DEBUG(dbgs() << "Selecting AL: "); + M680x0ISelAddressMode AM(M680x0ISelAddressMode::AL); + + if (!matchAddress(N, AM)) { + LLVM_DEBUG(dbgs() << "REJECT: Match failed\n"); + return false; + } + + if (AM.isPCRelative()) { + LLVM_DEBUG(dbgs() << "REJECT: Cannot match PC relative address\n"); + return false; + } + + if (AM.hasBase()) { + LLVM_DEBUG(dbgs() << "REJECT: Cannot match Base\n"); + return false; + } + + if (AM.hasIndexReg()) { + LLVM_DEBUG(dbgs() << "REJECT: Cannot match Index\n"); + return false; + } + + if (getSymbolicDisplacement(AM, SDLoc(N), Sym)) { + LLVM_DEBUG(dbgs() << "SUCCESS: Matched symbol\n"); + return true; + } + + if (AM.Disp) { + Sym = getI32Imm(AM.Disp, SDLoc(N)); + LLVM_DEBUG(dbgs() << "SUCCESS\n"); + return true; + } + + LLVM_DEBUG(dbgs() << "REJECT: Not Symbol or Disp\n"); + return false; + ; +} + +bool M680x0DAGToDAGISel::SelectPCD(SDNode *Parent, SDValue N, SDValue &Disp) { + LLVM_DEBUG(dbgs() << "Selecting PCD: "); + M680x0ISelAddressMode AM(M680x0ISelAddressMode::PCD); + + if (!matchAddress(N, AM)) + return false; + + if (!AM.isPCRelative()) { + LLVM_DEBUG(dbgs() << "REJECT: Not PC relative\n"); + return false; + } + + if (AM.hasIndexReg()) { + LLVM_DEBUG(dbgs() << "REJECT: Cannot match Index\n"); + return false; + } + + if (getSymbolicDisplacement(AM, SDLoc(N), Disp)) { + LLVM_DEBUG(dbgs() << "SUCCESS, matched Symbol\n"); + return true; + } + + Disp = getI16Imm(AM.Disp, SDLoc(N)); + + LLVM_DEBUG(dbgs() << "SUCCESS\n"); + return true; +} + +bool M680x0DAGToDAGISel::SelectPCI(SDNode *Parent, SDValue N, SDValue &Disp, + SDValue &Index) { + LLVM_DEBUG(dbgs() << "Selecting PCI: "); + M680x0ISelAddressMode AM(M680x0ISelAddressMode::PCI); + + if (!matchAddress(N, AM)) + return false; + + if (!AM.isPCRelative()) { + LLVM_DEBUG(dbgs() << "REJECT: Not PC relative\n"); + return false; + } + + if (!AM.hasIndexReg()) { + LLVM_DEBUG(dbgs() << "REJECT: No Index\n"); + return false; + } + + Index = AM.IndexReg; + + if (getSymbolicDisplacement(AM, SDLoc(N), Disp)) { + assert(!AM.Disp && "Should not be any displacement"); + LLVM_DEBUG(dbgs() << "SUCCESS, matched Symbol\n"); + return true; + } + + Disp = getI8Imm(AM.Disp, SDLoc(N)); + + LLVM_DEBUG(dbgs() << "SUCCESS\n"); + return true; +} + +bool M680x0DAGToDAGISel::SelectARI(SDNode *Parent, SDValue N, SDValue &Base) { + LLVM_DEBUG(dbgs() << "Selecting ARI: "); + M680x0ISelAddressMode AM(M680x0ISelAddressMode::ARI); + + if (!matchAddress(N, AM)) { + LLVM_DEBUG(dbgs() << "REJECT: Match failed\n"); + return false; + } + + if (AM.isPCRelative()) { + LLVM_DEBUG(dbgs() << "REJECT: Cannot match PC relative address\n"); + return false; + } + + // ARI does not use these + if (AM.hasIndexReg() || AM.Disp != 0) { + LLVM_DEBUG(dbgs() << "REJECT: Cannot match Index or Disp\n"); + return false; + } + + // Must be matched by AL + if (AM.hasSymbolicDisplacement()) { + LLVM_DEBUG(dbgs() << "REJECT: Cannot match Symbolic Disp\n"); + return false; + } + + if (AM.hasBaseReg()) { + Base = AM.BaseReg; + LLVM_DEBUG(dbgs() << "SUCCESS\n"); + return true; + } + + return false; +} Index: lib/Target/M680x0/M680x0ISelLowering.h =================================================================== --- /dev/null +++ lib/Target/M680x0/M680x0ISelLowering.h @@ -0,0 +1,270 @@ +//===-- M680x0ISelLowering.h - M680x0 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. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file defines the interfaces that M680x0 uses to lower LLVM code into a +/// selection DAG. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_M680X0_M680X0ISELLOWERING_H +#define LLVM_LIB_TARGET_M680X0_M680X0ISELLOWERING_H + +#include "M680x0.h" + +#include "llvm/CodeGen/CallingConvLower.h" +#include "llvm/CodeGen/SelectionDAG.h" +#include "llvm/CodeGen/TargetLowering.h" +#include "llvm/IR/Function.h" + +#include + +namespace llvm { +namespace M680x0ISD { + +/// M680x0 Specific DAG nodes +enum NodeType { + /// Start the numbering from where ISD NodeType finishes. + FIRST_NUMBER = ISD::BUILTIN_OP_END, + + CALL, + RET, + TAIL_CALL, + TC_RETURN, + + /// M680x0 compare and logical compare instructions. Subtracts the source + /// operand from the destination data register and sets the condition + /// codes according to the result. Immediate always goes first. + CMP, + + /// M680x0 bit-test instructions. + BT, + + /// M680x0 Select + SELECT, + + /// M680x0 SetCC. Operand 0 is condition code, and operand 1 is the CCR + /// operand, usually produced by a CMP instruction. + SETCC, + + // Same as SETCC except it's materialized with a subx and the value is all + // one's or all zero's. + SETCC_CARRY, // R = carry_bit ? ~0 : 0 + + /// M680x0 conditional moves. Operand 0 and operand 1 are the two values + /// to select from. Operand 2 is the condition code, and operand 3 is the + /// flag operand produced by a CMP or TEST instruction. It also writes a + /// flag result. + CMOV, + + /// M680x0 conditional branches. Operand 0 is the chain operand, operand 1 + /// is the block to branch if condition is true, operand 2 is the + /// condition code, and operand 3 is the flag operand produced by a CMP + /// or TEST instruction. + BRCOND, + + // Arithmetic operations with CCR results. + ADD, + SUB, + ADDX, + SUBX, + SMUL, + UMUL, + OR, + XOR, + AND, + + GlobalBaseReg, + + /// A wrapper node for TargetConstantPool, + /// TargetExternalSymbol, and TargetGlobalAddress. + Wrapper, + + /// Special wrapper used under M680x0 PIC mode for PC + /// relative displacements. + WrapperPC, + + // For allocating variable amounts of stack space when using + // segmented stacks. Check if the current stacklet has enough space, and + // falls back to heap allocation if not. + SEG_ALLOCA, +}; +} // namespace M680x0ISD + +/// Define some predicates that are used for node matching. +namespace M680x0 { + +/// Determines whether the callee is required to pop its +/// own arguments. Callee pop is necessary to support tail calls. +bool isCalleePop(CallingConv::ID CallingConv, bool IsVarArg, bool GuaranteeTCO); + +} // end namespace M680x0 + +//===--------------------------------------------------------------------===// +// TargetLowering Implementation +//===--------------------------------------------------------------------===// + +class M680x0MachineFunctionInfo; +class M680x0Subtarget; + +class M680x0TargetLowering : public TargetLowering { + const M680x0Subtarget &Subtarget; + const M680x0TargetMachine &TM; + +public: + explicit M680x0TargetLowering(const M680x0TargetMachine &TM, + const M680x0Subtarget &STI); + + static const M680x0TargetLowering *create(const M680x0TargetMachine &TM, + const M680x0Subtarget &STI); + + const char *getTargetNodeName(unsigned Opcode) const override; + + /// Return the value type to use for ISD::SETCC. + EVT getSetCCResultType(const DataLayout &DL, LLVMContext &Context, + EVT VT) const override; + + /// EVT is not used in-tree, but is used by out-of-tree target. + virtual MVT getScalarShiftAmountTy(const DataLayout &, EVT) const override; + + /// Provide custom lowering hooks for some operations. + SDValue LowerOperation(SDValue Op, SelectionDAG &DAG) const override; + + /// Return the entry encoding for a jump table in the current function. + /// The returned value is a member of the MachineJumpTableInfo::JTEntryKind + /// enum. + unsigned getJumpTableEncoding() const override; + + const MCExpr *LowerCustomJumpTableEntry(const MachineJumpTableInfo *MJTI, + const MachineBasicBlock *MBB, + unsigned uid, + MCContext &Ctx) const override; + + /// Returns relocation base for the given PIC jumptable. + SDValue getPICJumpTableRelocBase(SDValue Table, + SelectionDAG &DAG) const override; + + /// This returns the relocation base for the given PIC jumptable, + /// the same as getPICJumpTableRelocBase, but as an MCExpr. + const MCExpr *getPICJumpTableRelocBaseExpr(const MachineFunction *MF, + unsigned JTI, + MCContext &Ctx) const override; + + /// Replace the results of node with an illegal result type with new values + /// built out of custom code. + // void ReplaceNodeResults(SDNode *N, SmallVectorImpl&Results, + // SelectionDAG &DAG) const override; + + // SDValue PerformDAGCombine(SDNode *N, DAGCombinerInfo &DCI) const override; + + MachineBasicBlock * + EmitInstrWithCustomInserter(MachineInstr &MI, + MachineBasicBlock *MBB) const override; + +private: + unsigned GetAlignedArgumentStackSize(unsigned StackSize, + SelectionDAG &DAG) const; + + SDValue getReturnAddressFrameIndex(SelectionDAG &DAG) const; + + /// Emit a load of return address if tail call + /// optimization is performed and it is required. + SDValue EmitTailCallLoadRetAddr(SelectionDAG &DAG, SDValue &OutRetAddr, + SDValue Chain, bool IsTailCall, int FPDiff, + const SDLoc &DL) const; + + /// Emit a store of the return address if tail call + /// optimization is performed and it is required (FPDiff!=0). + SDValue EmitTailCallStoreRetAddr(SelectionDAG &DAG, MachineFunction &MF, + SDValue Chain, SDValue RetAddrFrIdx, + EVT PtrVT, unsigned SlotSize, int FPDiff, + const SDLoc &DL) const; + + SDValue LowerMemArgument(SDValue Chain, CallingConv::ID CallConv, + const SmallVectorImpl &ArgInfo, + const SDLoc &DL, SelectionDAG &DAG, + const CCValAssign &VA, MachineFrameInfo &MFI, + unsigned i) const; + + SDValue LowerMemOpCallTo(SDValue Chain, SDValue StackPtr, SDValue Arg, + const SDLoc &DL, SelectionDAG &DAG, + const CCValAssign &VA, ISD::ArgFlagsTy Flags) const; + + SDValue LowerMUL(SDValue &N, SelectionDAG &DAG) const; + SDValue LowerXALUO(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerToBT(SDValue And, ISD::CondCode CC, const SDLoc &DL, + SelectionDAG &DAG) const; + SDValue LowerSETCC(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerSETCCCARRY(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerSELECT(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerBRCOND(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerADDC_ADDE_SUBC_SUBE(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerConstantPool(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerJumpTable(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerExternalSymbol(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerBlockAddress(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerGlobalAddress(const GlobalValue *GV, const SDLoc &DL, + int64_t Offset, SelectionDAG &DAG) const; + SDValue LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerVASTART(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerDYNAMIC_STACKALLOC(SDValue Op, SelectionDAG &DAG) const; + + SDValue LowerCallResult(SDValue Chain, SDValue InFlag, + CallingConv::ID CallConv, bool isVarArg, + const SmallVectorImpl &Ins, + const SDLoc &DL, SelectionDAG &DAG, + SmallVectorImpl &InVals) const; + + /// LowerFormalArguments - transform physical registers into virtual + /// registers and generate load operations for arguments places on the stack. + SDValue LowerFormalArguments(SDValue Chain, CallingConv::ID CCID, + bool isVarArg, + const SmallVectorImpl &Ins, + const SDLoc &DL, SelectionDAG &DAG, + SmallVectorImpl &InVals) const override; + + SDValue LowerCall(CallLoweringInfo &CLI, + SmallVectorImpl &InVals) const override; + + /// Lower the result values of a call into the + /// appropriate copies out of appropriate physical registers. + SDValue LowerReturn(SDValue Chain, CallingConv::ID CCID, bool IsVarArg, + const SmallVectorImpl &Outs, + const SmallVectorImpl &OutVals, const SDLoc &DL, + SelectionDAG &DAG) const override; + + MachineBasicBlock *EmitLoweredSelect(MachineInstr &I, + MachineBasicBlock *BB) const; + MachineBasicBlock *EmitLoweredSegAlloca(MachineInstr &MI, + MachineBasicBlock *BB) const; + + /// Emit nodes that will be selected as "test Op0,Op0", or something + /// equivalent, for use with the given M680x0 condition code. + SDValue EmitTest(SDValue Op0, unsigned M680x0CC, const SDLoc &dl, + SelectionDAG &DAG) const; + + /// Emit nodes that will be selected as "cmp Op0,Op1", or something + /// equivalent, for use with the given M680x0 condition code. + SDValue EmitCmp(SDValue Op0, SDValue Op1, unsigned M680x0CC, const SDLoc &dl, + SelectionDAG &DAG) const; + + /// Check whether the call is eligible for tail call optimization. Targets + /// that want to do tail call optimization should implement this function. + bool IsEligibleForTailCallOptimization( + SDValue Callee, CallingConv::ID CalleeCC, bool isVarArg, + bool isCalleeStructRet, bool isCallerStructRet, Type *RetTy, + const SmallVectorImpl &Outs, + const SmallVectorImpl &OutVals, + const SmallVectorImpl &Ins, SelectionDAG &DAG) const; + + SDValue PerformDAGCombine(SDNode *N, DAGCombinerInfo &DCI) const override; +}; +} // namespace llvm + +#endif // M680x0ISELLOWERING_H Index: lib/Target/M680x0/M680x0ISelLowering.cpp =================================================================== --- /dev/null +++ lib/Target/M680x0/M680x0ISelLowering.cpp @@ -0,0 +1,3741 @@ +//===-- M680x0ISelLowering.cpp - M680x0 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. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file defines the interfaces that M680x0 uses to lower LLVM code into a +/// selection DAG. +/// +//===----------------------------------------------------------------------===// + +#include "M680x0CallingConv.h" +#include "M680x0ISelLowering.h" +#include "M680x0MachineFunction.h" +#include "M680x0Subtarget.h" +#include "M680x0TargetMachine.h" +#include "M680x0TargetObjectFile.h" + +#include "llvm/ADT/Statistic.h" +#include "llvm/CodeGen/CallingConvLower.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineJumpTableInfo.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/SelectionDAG.h" +#include "llvm/CodeGen/ValueTypes.h" +#include "llvm/IR/CallingConv.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/GlobalVariable.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/KnownBits.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +#define DEBUG_TYPE "M680x0-isel" + +STATISTIC(NumTailCalls, "Number of tail calls"); + +M680x0TargetLowering::M680x0TargetLowering(const M680x0TargetMachine &TM, + const M680x0Subtarget &STI) + : TargetLowering(TM), Subtarget(STI), TM(TM) { + + MVT PtrVT = MVT::i32; + + setBooleanContents(ZeroOrOneBooleanContent); + + auto *RegInfo = Subtarget.getRegisterInfo(); + setStackPointerRegisterToSaveRestore(RegInfo->getStackRegister()); + + ValueTypeActions.setTypeAction(MVT::i64, TypeExpandInteger); + + // Use _setjmp/_longjmp instead of setjmp/longjmp. + setUseUnderscoreSetJmp(true); + setUseUnderscoreLongJmp(true); + + // NOTE The stuff that follows is true for M68000 + + // Set up the register classes. + addRegisterClass(MVT::i8, &M680x0::DR8RegClass); + addRegisterClass(MVT::i16, &M680x0::XR16RegClass); + addRegisterClass(MVT::i32, &M680x0::XR32RegClass); + + for (auto VT : MVT::integer_valuetypes()) { + setLoadExtAction(ISD::SEXTLOAD, VT, MVT::i1, Promote); + setLoadExtAction(ISD::ZEXTLOAD, VT, MVT::i1, Promote); + setLoadExtAction(ISD::EXTLOAD, VT, MVT::i1, Promote); + } + + // We don't accept any truncstore of integer registers. + setTruncStoreAction(MVT::i64, MVT::i32, Expand); + setTruncStoreAction(MVT::i64, MVT::i16, Expand); + setTruncStoreAction(MVT::i64, MVT::i8, Expand); + setTruncStoreAction(MVT::i32, MVT::i16, Expand); + setTruncStoreAction(MVT::i32, MVT::i8, Expand); + setTruncStoreAction(MVT::i16, MVT::i8, Expand); + + setOperationAction(ISD::MUL, MVT::i8, Legal); + setOperationAction(ISD::MUL, MVT::i16, Legal); + setOperationAction(ISD::MUL, MVT::i32, Custom); + setOperationAction(ISD::MUL, MVT::i64, LibCall); + + for (auto OP : + {ISD::SDIV, ISD::UDIV, ISD::SREM, ISD::UREM, ISD::UDIVREM, ISD::SDIVREM, + ISD::MULHS, ISD::MULHU, ISD::UMUL_LOHI, ISD::SMUL_LOHI}) { + setOperationAction(OP, MVT::i8, Legal); + setOperationAction(OP, MVT::i16, Legal); + setOperationAction(OP, MVT::i32, LibCall); + } + + for (auto OP : {ISD::UMUL_LOHI, ISD::SMUL_LOHI}) { + setOperationAction(OP, MVT::i8, Expand); + setOperationAction(OP, MVT::i16, Expand); + } + + for (auto OP : {ISD::SMULO, ISD::UMULO}) { + setOperationAction(OP, MVT::i8, Expand); + setOperationAction( + OP, MVT::i16, + Expand); // FIXME something wrong with custom lowering here + setOperationAction(OP, MVT::i32, Expand); + } + + // Add/Sub overflow ops with MVT::Glues are lowered to CCR dependences. + for (auto VT : {MVT::i8, MVT::i16, MVT::i32}) { + setOperationAction(ISD::ADDC, VT, Custom); + setOperationAction(ISD::ADDE, VT, Custom); + setOperationAction(ISD::SUBC, VT, Custom); + setOperationAction(ISD::SUBE, VT, Custom); + } + + // SADDO and friends are legal with this setup, i hope + for (auto VT : {MVT::i8, MVT::i16, MVT::i32}) { + setOperationAction(ISD::SADDO, VT, Custom); + setOperationAction(ISD::UADDO, VT, Custom); + setOperationAction(ISD::SSUBO, VT, Custom); + setOperationAction(ISD::USUBO, VT, Custom); + } + + setOperationAction(ISD::BR_JT, MVT::Other, Expand); + setOperationAction(ISD::BRCOND, MVT::Other, Custom); + + for (auto VT : {MVT::i8, MVT::i16, MVT::i32}) { + setOperationAction(ISD::BR_CC, VT, Expand); + setOperationAction(ISD::SELECT, VT, Custom); + setOperationAction(ISD::SELECT_CC, VT, Expand); + setOperationAction(ISD::SETCC, VT, Custom); + setOperationAction(ISD::SETCCCARRY, VT, Custom); + } + + for (auto VT : {MVT::i8, MVT::i16, MVT::i32}) { + setOperationAction(ISD::BSWAP, VT, Expand); + setOperationAction(ISD::CTTZ, VT, Expand); + setOperationAction(ISD::CTLZ, VT, Expand); + setOperationAction(ISD::CTPOP, VT, Expand); + } + + setOperationAction(ISD::ConstantPool, MVT::i32, Custom); + setOperationAction(ISD::JumpTable, MVT::i32, Custom); + setOperationAction(ISD::GlobalAddress, MVT::i32, Custom); + setOperationAction(ISD::GlobalTLSAddress, MVT::i32, Custom); + setOperationAction(ISD::ExternalSymbol, MVT::i32, Custom); + setOperationAction(ISD::BlockAddress, MVT::i32, Custom); + + setOperationAction(ISD::VASTART, MVT::Other, Custom); + setOperationAction(ISD::VAEND, MVT::Other, Expand); + setOperationAction(ISD::VAARG, MVT::Other, Expand); + setOperationAction(ISD::VACOPY, MVT::Other, Expand); + + setOperationAction(ISD::STACKSAVE, MVT::Other, Expand); + setOperationAction(ISD::STACKRESTORE, MVT::Other, Expand); + + setOperationAction(ISD::DYNAMIC_STACKALLOC, PtrVT, Custom); + + computeRegisterProperties(STI.getRegisterInfo()); + + setMinFunctionAlignment(2); // 2^2 bytes // ??? can it be just 2^1? +} + +EVT M680x0TargetLowering::getSetCCResultType(const DataLayout &DL, + LLVMContext &Context, + EVT VT) const { + // M680x0 SETcc producess either 0x00 or 0xFF + return MVT::i8; +} + +MVT M680x0TargetLowering::getScalarShiftAmountTy(const DataLayout &DL, + EVT Ty) const { + if (Ty.isSimple()) { + return Ty.getSimpleVT(); + } + return MVT::getIntegerVT(8 * DL.getPointerSize(0)); +} + +#include "M680x0GenCallingConv.inc" + +enum StructReturnType { NotStructReturn, RegStructReturn, StackStructReturn }; + +static StructReturnType +callIsStructReturn(const SmallVectorImpl &Outs) { + if (Outs.empty()) + return NotStructReturn; + + const ISD::ArgFlagsTy &Flags = Outs[0].Flags; + if (!Flags.isSRet()) + return NotStructReturn; + if (Flags.isInReg()) + return RegStructReturn; + return StackStructReturn; +} + +/// Determines whether a function uses struct return semantics. +static StructReturnType +argsAreStructReturn(const SmallVectorImpl &Ins) { + if (Ins.empty()) + return NotStructReturn; + + const ISD::ArgFlagsTy &Flags = Ins[0].Flags; + if (!Flags.isSRet()) + return NotStructReturn; + if (Flags.isInReg()) + return RegStructReturn; + return StackStructReturn; +} + +/// Make a copy of an aggregate at address specified by "Src" to address +/// "Dst" with size and alignment information specified by the specific +/// parameter attribute. The copy will be passed as a byval function parameter. +static SDValue CreateCopyOfByValArgument(SDValue Src, SDValue Dst, + SDValue Chain, ISD::ArgFlagsTy Flags, + SelectionDAG &DAG, const SDLoc &DL) { + SDValue SizeNode = DAG.getConstant(Flags.getByValSize(), DL, MVT::i32); + + return DAG.getMemcpy(Chain, DL, Dst, Src, SizeNode, Flags.getByValAlign(), + /*isVolatile*/ false, /*AlwaysInline=*/true, + /*isTailCall*/ false, MachinePointerInfo(), + MachinePointerInfo()); +} + +/// Return true if the calling convention is one that we can guarantee TCO for. +static bool canGuaranteeTCO(CallingConv::ID CC) { + return false; + // return CC == CallingConv::Fast; // TODO Since M68010 only +} + +/// Return true if we might ever do TCO for calls with this calling convention. +static bool mayTailCallThisCC(CallingConv::ID CC) { + switch (CC) { + // C calling conventions: + case CallingConv::C: + return true; + default: + return canGuaranteeTCO(CC); + } +} + +/// Return true if the function is being made into a tailcall target by +/// changing its ABI. +static bool shouldGuaranteeTCO(CallingConv::ID CC, bool GuaranteedTailCallOpt) { + return GuaranteedTailCallOpt && canGuaranteeTCO(CC); +} + +/// Return true if the given stack call argument is already available in the +/// same position (relatively) of the caller's incoming argument stack. +static bool MatchingStackOffset(SDValue Arg, unsigned Offset, + ISD::ArgFlagsTy Flags, MachineFrameInfo &MFI, + const MachineRegisterInfo *MRI, + const M680x0InstrInfo *TII, + const CCValAssign &VA) { + unsigned Bytes = Arg.getValueType().getSizeInBits() / 8; + + for (;;) { + // Look through nodes that don't alter the bits of the incoming value. + unsigned Op = Arg.getOpcode(); + if (Op == ISD::ZERO_EXTEND || Op == ISD::ANY_EXTEND || Op == ISD::BITCAST) { + Arg = Arg.getOperand(0); + continue; + } + if (Op == ISD::TRUNCATE) { + const SDValue &TruncInput = Arg.getOperand(0); + if (TruncInput.getOpcode() == ISD::AssertZext && + cast(TruncInput.getOperand(1))->getVT() == + Arg.getValueType()) { + Arg = TruncInput.getOperand(0); + continue; + } + } + break; + } + + int FI = INT_MAX; + if (Arg.getOpcode() == ISD::CopyFromReg) { + unsigned VR = cast(Arg.getOperand(1))->getReg(); + if (!TargetRegisterInfo::isVirtualRegister(VR)) + return false; + MachineInstr *Def = MRI->getVRegDef(VR); + if (!Def) + return false; + if (!Flags.isByVal()) { + if (!TII->isLoadFromStackSlot(*Def, FI)) + return false; + } else { + unsigned Opcode = Def->getOpcode(); + if ((Opcode == M680x0::LEA32p || Opcode == M680x0::LEA32f) && + Def->getOperand(1).isFI()) { + FI = Def->getOperand(1).getIndex(); + Bytes = Flags.getByValSize(); + } else + return false; + } + } else if (LoadSDNode *Ld = dyn_cast(Arg)) { + if (Flags.isByVal()) + // ByVal argument is passed in as a pointer but it's now being + // dereferenced. e.g. + // define @foo(%struct.X* %A) { + // tail call @bar(%struct.X* byval %A) + // } + return false; + SDValue Ptr = Ld->getBasePtr(); + FrameIndexSDNode *FINode = dyn_cast(Ptr); + if (!FINode) + return false; + FI = FINode->getIndex(); + } else if (Arg.getOpcode() == ISD::FrameIndex && Flags.isByVal()) { + FrameIndexSDNode *FINode = cast(Arg); + FI = FINode->getIndex(); + Bytes = Flags.getByValSize(); + } else + return false; + + assert(FI != INT_MAX); + if (!MFI.isFixedObjectIndex(FI)) + return false; + + if (Offset != MFI.getObjectOffset(FI)) + return false; + + if (VA.getLocVT().getSizeInBits() > Arg.getValueType().getSizeInBits()) { + // If the argument location is wider than the argument type, check that any + // extension flags match. + if (Flags.isZExt() != MFI.isObjectZExt(FI) || + Flags.isSExt() != MFI.isObjectSExt(FI)) { + return false; + } + } + + return Bytes == MFI.getObjectSize(FI); +} + +SDValue +M680x0TargetLowering::getReturnAddressFrameIndex(SelectionDAG &DAG) const { + MachineFunction &MF = DAG.getMachineFunction(); + M680x0MachineFunctionInfo *FuncInfo = MF.getInfo(); + int ReturnAddrIndex = FuncInfo->getRAIndex(); + + if (ReturnAddrIndex == 0) { + // Set up a frame object for the return address. + unsigned SlotSize = Subtarget.getSlotSize(); + ReturnAddrIndex = MF.getFrameInfo().CreateFixedObject( + SlotSize, -(int64_t)SlotSize, false); + FuncInfo->setRAIndex(ReturnAddrIndex); + } + + return DAG.getFrameIndex(ReturnAddrIndex, getPointerTy(DAG.getDataLayout())); +} + +SDValue M680x0TargetLowering::EmitTailCallLoadRetAddr( + SelectionDAG &DAG, SDValue &OutRetAddr, SDValue Chain, bool IsTailCall, + int FPDiff, const SDLoc &DL) const { + EVT VT = getPointerTy(DAG.getDataLayout()); + OutRetAddr = getReturnAddressFrameIndex(DAG); + + // Load the "old" Return address. + OutRetAddr = DAG.getLoad(VT, DL, Chain, OutRetAddr, MachinePointerInfo()); + return SDValue(OutRetAddr.getNode(), 1); +} + +SDValue M680x0TargetLowering::EmitTailCallStoreRetAddr( + SelectionDAG &DAG, MachineFunction &MF, SDValue Chain, SDValue RetFI, + EVT PtrVT, unsigned SlotSize, int FPDiff, const SDLoc &DL) const { + if (!FPDiff) + return Chain; + + // Calculate the new stack slot for the return address. + int NewFO = MF.getFrameInfo().CreateFixedObject( + SlotSize, (int64_t)FPDiff - SlotSize, false); + + SDValue NewFI = DAG.getFrameIndex(NewFO, PtrVT); + // Store the return address to the appropriate stack slot. + Chain = DAG.getStore( + Chain, DL, RetFI, NewFI, + MachinePointerInfo::getFixedStack(DAG.getMachineFunction(), NewFO)); + return Chain; +} + +SDValue M680x0TargetLowering::LowerMemArgument( + SDValue Chain, CallingConv::ID CallConv, + const SmallVectorImpl &Ins, const SDLoc &DL, + SelectionDAG &DAG, const CCValAssign &VA, MachineFrameInfo &MFI, + unsigned i) const { + // Create the nodes corresponding to a load from this parameter slot. + ISD::ArgFlagsTy Flags = Ins[i].Flags; + EVT ValVT; + + // If value is passed by pointer we have address passed instead of the value + // itself. + if (VA.getLocInfo() == CCValAssign::Indirect) + ValVT = VA.getLocVT(); + else + ValVT = VA.getValVT(); + + // Because we are dealing with BE architecture we need to offset loading of + // partial types + int Offset = VA.getLocMemOffset(); + if (VA.getValVT() == MVT::i8) { + Offset += 3; + } else if (VA.getValVT() == MVT::i16) { + Offset += 2; + } + + // Calculate SP offset of interrupt parameter, re-arrange the slot normally + // taken by a return address. + // TODO interrupts + // if (CallConv == CallingConv::M680x0_INTR) { + // const M680x0Subtarget& Subtarget = + // static_cast(DAG.getSubtarget()); + // // M680x0 interrupts may take one or two arguments. + // // On the stack there will be no return address as in regular call. + // // Offset of last argument need to be set to -4/-8 bytes. + // // Where offset of the first argument out of two, should be set to 0 + // bytes. Offset = (Subtarget.is64Bit() ? 8 : 4) * ((i + 1) % Ins.size() - + // 1); + // } + + // FIXME: For now, all byval parameter objects are marked mutable. This can be + // changed with more analysis. + // In case of tail call optimization mark all arguments mutable. Since they + // could be overwritten by lowering of arguments in case of a tail call. + bool AlwaysUseMutable = shouldGuaranteeTCO( + CallConv, DAG.getTarget().Options.GuaranteedTailCallOpt); + bool isImmutable = !AlwaysUseMutable && !Flags.isByVal(); + + if (Flags.isByVal()) { + unsigned Bytes = Flags.getByValSize(); + if (Bytes == 0) + Bytes = 1; // Don't create zero-sized stack objects. + int FI = MFI.CreateFixedObject(Bytes, Offset, isImmutable); + // Adjust SP offset of interrupt parameter. + // TODO interrupts + // if (CallConv == CallingConv::M680x0_INTR) { + // MFI.setObjectOffset(FI, Offset); + // } + return DAG.getFrameIndex(FI, getPointerTy(DAG.getDataLayout())); + } else { + int FI = + MFI.CreateFixedObject(ValVT.getSizeInBits() / 8, Offset, isImmutable); + + // Set SExt or ZExt flag. + if (VA.getLocInfo() == CCValAssign::ZExt) { + MFI.setObjectZExt(FI, true); + } else if (VA.getLocInfo() == CCValAssign::SExt) { + MFI.setObjectSExt(FI, true); + } + + // Adjust SP offset of interrupt parameter. + // TODO interrupts + // if (CallConv == CallingConv::M680x0_INTR) { + // MFI.setObjectOffset(FI, Offset); + // } + + SDValue FIN = DAG.getFrameIndex(FI, getPointerTy(DAG.getDataLayout())); + SDValue Val = DAG.getLoad( + ValVT, DL, Chain, FIN, + MachinePointerInfo::getFixedStack(DAG.getMachineFunction(), FI)); + return VA.isExtInLoc() ? DAG.getNode(ISD::TRUNCATE, DL, VA.getValVT(), Val) + : Val; + } +} + +SDValue M680x0TargetLowering::LowerMemOpCallTo(SDValue Chain, SDValue StackPtr, + SDValue Arg, const SDLoc &DL, + SelectionDAG &DAG, + const CCValAssign &VA, + ISD::ArgFlagsTy Flags) const { + unsigned LocMemOffset = VA.getLocMemOffset(); + SDValue PtrOff = DAG.getIntPtrConstant(LocMemOffset, DL); + PtrOff = DAG.getNode(ISD::ADD, DL, getPointerTy(DAG.getDataLayout()), + StackPtr, PtrOff); + if (Flags.isByVal()) + return CreateCopyOfByValArgument(Arg, PtrOff, Chain, Flags, DAG, DL); + + return DAG.getStore( + Chain, DL, Arg, PtrOff, + MachinePointerInfo::getStack(DAG.getMachineFunction(), LocMemOffset)); +} + +//===----------------------------------------------------------------------===// +// Call +//===----------------------------------------------------------------------===// + +SDValue +M680x0TargetLowering::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 &isTailCall = CLI.IsTailCall; + bool isVarArg = CLI.IsVarArg; + + MachineFunction &MF = DAG.getMachineFunction(); + StructReturnType SR = callIsStructReturn(Outs); + bool IsSibcall = false; + M680x0MachineFunctionInfo *MFI = MF.getInfo(); + // const M680x0RegisterInfo *TRI = Subtarget.getRegisterInfo(); + + // TODO interrupts + // if (CallConv == CallingConv::M680x0_INTR) + // report_fatal_error("M680x0 interrupts may not be called directly"); + + auto Attr = MF.getFunction().getFnAttribute("disable-tail-calls"); + if (Attr.getValueAsString() == "true") + isTailCall = false; + + // FIXME check this + // if (Subtarget.isPICStyleGOT() && + // !MF.getTarget().Options.GuaranteedTailCallOpt) { + // // TODO reqd more about this stuff + // // If we are using a GOT, disable tail calls to external symbols with + // // default visibility. Tail calling such a symbol requires using a GOT + // // relocation, which forces early binding of the symbol. This breaks code + // // that require lazy function symbol resolution. Using musttail or + // // GuaranteedTailCallOpt will override this. + // GlobalAddressSDNode *G = dyn_cast(Callee); + // if (!G || (!G->getGlobal()->hasLocalLinkage() && + // G->getGlobal()->hasDefaultVisibility())) + // isTailCall = false; + // } + + bool IsMustTail = CLI.CS && CLI.CS.isMustTailCall(); + if (IsMustTail) { + // Force this to be a tail call. The verifier rules are enough to ensure + // that we can lower this successfully without moving the return address + // around. + isTailCall = true; + } else if (isTailCall) { + // Check if it's really possible to do a tail call. + isTailCall = IsEligibleForTailCallOptimization( + Callee, CallConv, isVarArg, SR != NotStructReturn, + MF.getFunction().hasStructRetAttr(), CLI.RetTy, Outs, OutVals, Ins, + DAG); + + // Sibcalls are automatically detected tailcalls which do not require + // ABI changes. + if (!MF.getTarget().Options.GuaranteedTailCallOpt && isTailCall) + IsSibcall = true; + + if (isTailCall) + ++NumTailCalls; + } + + assert(!(isVarArg && canGuaranteeTCO(CallConv)) && + "Var args not supported with calling convention fastcc"); + + // Analyze operands of the call, assigning locations to each operand. + SmallVector ArgLocs; + // It is empty for LibCall + const Function *CalleeFunc = CLI.CS ? CLI.CS.getCalledFunction() : nullptr; + M680x0CCState CCInfo(*CalleeFunc, CallConv, isVarArg, MF, ArgLocs, + *DAG.getContext()); + CCInfo.AnalyzeCallOperands(Outs, CC_M680x0); + + // Get a count of how many bytes are to be pushed on the stack. + unsigned NumBytes = CCInfo.getAlignedCallFrameSize(); + if (IsSibcall) { + // This is a sibcall. The memory operands are available in caller's + // own caller's stack. + NumBytes = 0; + } else if (MF.getTarget().Options.GuaranteedTailCallOpt && + canGuaranteeTCO(CallConv)) { + NumBytes = GetAlignedArgumentStackSize(NumBytes, DAG); + } + + // TODO debug this: + int FPDiff = 0; + if (isTailCall && !IsSibcall && !IsMustTail) { + // Lower arguments at fp - stackoffset + fpdiff. + unsigned NumBytesCallerPushed = MFI->getBytesToPopOnReturn(); + + FPDiff = NumBytesCallerPushed - NumBytes; + + // Set the delta of movement of the returnaddr stackslot. + // But only set if delta is greater than previous delta. + if (FPDiff < MFI->getTCReturnAddrDelta()) + MFI->setTCReturnAddrDelta(FPDiff); + } + + unsigned NumBytesToPush = NumBytes; + unsigned NumBytesToPop = NumBytes; + + // If we have an inalloca argument, all stack space has already been allocated + // for us and be right at the top of the stack. We don't support multiple + // arguments passed in memory when using inalloca. + if (!Outs.empty() && Outs.back().Flags.isInAlloca()) { + NumBytesToPush = 0; + if (!ArgLocs.back().isMemLoc()) + report_fatal_error("cannot use inalloca attribute on a register " + "parameter"); + if (ArgLocs.back().getLocMemOffset() != 0) + report_fatal_error("any parameter with the inalloca attribute must be " + "the only memory argument"); + } + + if (!IsSibcall) + Chain = DAG.getCALLSEQ_START(Chain, NumBytesToPush, + NumBytes - NumBytesToPush, DL); + + SDValue RetFI; + // Load return address for tail calls. + if (isTailCall && FPDiff) + Chain = EmitTailCallLoadRetAddr(DAG, RetFI, Chain, isTailCall, FPDiff, DL); + + SmallVector, 8> RegsToPass; + SmallVector MemOpChains; + SDValue StackPtr; + + // Walk the register/memloc assignments, inserting copies/loads. In the case + // of tail call optimization arguments are handle later. + const M680x0RegisterInfo *RegInfo = Subtarget.getRegisterInfo(); + for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) { + ISD::ArgFlagsTy Flags = Outs[i].Flags; + + // Skip inalloca arguments, they have already been written. + if (Flags.isInAlloca()) + continue; + + CCValAssign &VA = ArgLocs[i]; + EVT RegVT = VA.getLocVT(); + SDValue Arg = OutVals[i]; + bool isByVal = Flags.isByVal(); + + // 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, RegVT, Arg); + break; + case CCValAssign::ZExt: + Arg = DAG.getNode(ISD::ZERO_EXTEND, DL, RegVT, Arg); + break; + case CCValAssign::AExt: + Arg = DAG.getNode(ISD::ANY_EXTEND, DL, RegVT, Arg); + break; + case CCValAssign::BCvt: + Arg = DAG.getBitcast(RegVT, Arg); + break; + case CCValAssign::Indirect: { + // Store the argument. + SDValue SpillSlot = DAG.CreateStackTemporary(VA.getValVT()); + int FI = cast(SpillSlot)->getIndex(); + Chain = DAG.getStore( + Chain, DL, Arg, SpillSlot, + MachinePointerInfo::getFixedStack(DAG.getMachineFunction(), FI)); + Arg = SpillSlot; + break; + } + } + + if (VA.isRegLoc()) { + RegsToPass.push_back(std::make_pair(VA.getLocReg(), Arg)); + } else if (!IsSibcall && (!isTailCall || isByVal)) { + assert(VA.isMemLoc()); + if (!StackPtr.getNode()) { + StackPtr = DAG.getCopyFromReg(Chain, DL, RegInfo->getStackRegister(), + getPointerTy(DAG.getDataLayout())); + } + MemOpChains.push_back( + LowerMemOpCallTo(Chain, StackPtr, Arg, DL, DAG, VA, Flags)); + } + } + + if (!MemOpChains.empty()) + Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, MemOpChains); + + // FIXME check this + // ??? The only time GOT is really needed is for Medium-PIC static data + // ??? otherwise we are happy with pc-rel or static references + // if (Subtarget.isPICStyleGOT()) { + // // ELF / PIC requires GOT in the %BP register before function calls via + // PLT + // // GOT pointer. + // if (!isTailCall) { + // RegsToPass.push_back(std::make_pair( + // unsigned(TRI->getBaseRegister()), + // DAG.getNode(M680x0ISD::GlobalBaseReg, SDLoc(), + // getPointerTy(DAG.getDataLayout())))); + // } else { + // // ??? WUT, debug this + // // If we are tail calling and generating PIC/GOT style code load the + // // address of the callee into %A1. The value in %A1 is used as target + // of + // // the tail jump. This is done to circumvent the %BP/callee-saved + // problem + // // for tail calls on PIC/GOT architectures. Normally we would just put + // the + // // address of GOT into %BP and then call target@PLT. But for tail calls + // // %BP would be restored (since %BP is callee saved) before jumping to + // the + // // target@PLT. + // + // // NOTE: The actual moving to %A1 is done further down. + // GlobalAddressSDNode *G = dyn_cast(Callee); + // if (G && !G->getGlobal()->hasLocalLinkage() && + // G->getGlobal()->hasDefaultVisibility()) + // Callee = LowerGlobalAddress(Callee, DAG); + // else if (isa(Callee)) + // Callee = LowerExternalSymbol(Callee, DAG); + // } + // } + + if (isVarArg && IsMustTail) { + const auto &Forwards = MFI->getForwardedMustTailRegParms(); + for (const auto &F : Forwards) { + SDValue Val = DAG.getCopyFromReg(Chain, DL, F.VReg, F.VT); + RegsToPass.push_back(std::make_pair(unsigned(F.PReg), Val)); + } + } + + // For tail calls lower the arguments to the 'real' stack slots. Sibcalls + // don't need this because the eligibility check rejects calls that require + // shuffling arguments passed in memory. + if (!IsSibcall && isTailCall) { + // Force all the incoming stack arguments to be loaded from the stack + // before any new outgoing arguments are stored to the stack, because the + // outgoing stack slots may alias the incoming argument stack slots, and + // the alias isn't otherwise explicit. This is slightly more conservative + // than necessary, because it means that each store effectively depends + // on every argument instead of just those arguments it would clobber. + SDValue ArgChain = DAG.getStackArgumentTokenFactor(Chain); + + SmallVector MemOpChains2; + SDValue FIN; + int FI = 0; + for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) { + CCValAssign &VA = ArgLocs[i]; + if (VA.isRegLoc()) + continue; + assert(VA.isMemLoc()); + SDValue Arg = OutVals[i]; + ISD::ArgFlagsTy Flags = Outs[i].Flags; + // Skip inalloca arguments. They don't require any work. + if (Flags.isInAlloca()) + continue; + // Create frame index. + int32_t Offset = VA.getLocMemOffset() + FPDiff; + uint32_t OpSize = (VA.getLocVT().getSizeInBits() + 7) / 8; + FI = MF.getFrameInfo().CreateFixedObject(OpSize, Offset, true); + FIN = DAG.getFrameIndex(FI, getPointerTy(DAG.getDataLayout())); + + if (Flags.isByVal()) { + // Copy relative to framepointer. + SDValue Source = DAG.getIntPtrConstant(VA.getLocMemOffset(), DL); + if (!StackPtr.getNode()) { + StackPtr = DAG.getCopyFromReg(Chain, DL, RegInfo->getStackRegister(), + getPointerTy(DAG.getDataLayout())); + } + Source = DAG.getNode(ISD::ADD, DL, getPointerTy(DAG.getDataLayout()), + StackPtr, Source); + + MemOpChains2.push_back( + CreateCopyOfByValArgument(Source, FIN, ArgChain, Flags, DAG, DL)); + } else { + // Store relative to framepointer. + MemOpChains2.push_back(DAG.getStore( + ArgChain, DL, Arg, FIN, + MachinePointerInfo::getFixedStack(DAG.getMachineFunction(), FI))); + } + } + + if (!MemOpChains2.empty()) + Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, MemOpChains2); + + // Store the return address to the appropriate stack slot. + Chain = EmitTailCallStoreRetAddr(DAG, MF, Chain, RetFI, + getPointerTy(DAG.getDataLayout()), + Subtarget.getSlotSize(), FPDiff, DL); + } + + // Build a sequence of copy-to-reg nodes chained together with token chain + // and flag operands which copy the outgoing args into registers. + SDValue InFlag; + for (unsigned i = 0, e = RegsToPass.size(); i != e; ++i) { + Chain = DAG.getCopyToReg(Chain, DL, RegsToPass[i].first, + RegsToPass[i].second, InFlag); + InFlag = Chain.getValue(1); + } + + if (Callee->getOpcode() == ISD::GlobalAddress) { + // 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. + GlobalAddressSDNode *G = cast(Callee); + + // We should use extra load for direct calls to dllimported functions in + // non-JIT mode. + const GlobalValue *GV = G->getGlobal(); + if (!GV->hasDLLImportStorageClass()) { + unsigned char OpFlags = Subtarget.classifyGlobalFunctionReference(GV); + + Callee = DAG.getTargetGlobalAddress( + GV, DL, getPointerTy(DAG.getDataLayout()), G->getOffset(), OpFlags); + + if (OpFlags == M680x0II::MO_GOTPCREL) { + + // Add a wrapper. + Callee = DAG.getNode(M680x0ISD::WrapperPC, DL, + getPointerTy(DAG.getDataLayout()), Callee); + + // Add extra indirection + Callee = DAG.getLoad( + getPointerTy(DAG.getDataLayout()), DL, DAG.getEntryNode(), Callee, + MachinePointerInfo::getGOT(DAG.getMachineFunction())); + } + } + } else if (ExternalSymbolSDNode *S = dyn_cast(Callee)) { + const Module *Mod = DAG.getMachineFunction().getFunction().getParent(); + unsigned char OpFlags = + Subtarget.classifyGlobalFunctionReference(nullptr, *Mod); + + Callee = DAG.getTargetExternalSymbol( + S->getSymbol(), getPointerTy(DAG.getDataLayout()), OpFlags); + } + + // Returns a chain & a flag for retval copy to use. + SDVTList NodeTys = DAG.getVTList(MVT::Other, MVT::Glue); + SmallVector Ops; + + if (!IsSibcall && isTailCall) { + Chain = DAG.getCALLSEQ_END(Chain, + DAG.getIntPtrConstant(NumBytesToPop, DL, true), + DAG.getIntPtrConstant(0, DL, true), InFlag, DL); + InFlag = Chain.getValue(1); + } + + Ops.push_back(Chain); + Ops.push_back(Callee); + + if (isTailCall) + Ops.push_back(DAG.getConstant(FPDiff, DL, MVT::i32)); + + // Add argument registers to the end of the list so that they are known live + // into the call. + 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 uint32_t *Mask = RegInfo->getCallPreservedMask(MF, CallConv); + assert(Mask && "Missing call preserved mask for calling convention"); + + Ops.push_back(DAG.getRegisterMask(Mask)); + + if (InFlag.getNode()) + Ops.push_back(InFlag); + + if (isTailCall) { + MF.getFrameInfo().setHasTailCall(); + return DAG.getNode(M680x0ISD::TC_RETURN, DL, NodeTys, Ops); + } + + Chain = DAG.getNode(M680x0ISD::CALL, DL, NodeTys, Ops); + InFlag = Chain.getValue(1); + + // Create the CALLSEQ_END node. + unsigned NumBytesForCalleeToPop; + if (M680x0::isCalleePop(CallConv, isVarArg, + DAG.getTarget().Options.GuaranteedTailCallOpt)) { + NumBytesForCalleeToPop = NumBytes; // Callee pops everything + } else if (!canGuaranteeTCO(CallConv) && SR == StackStructReturn) { + // If this is a call to a struct-return function, the callee + // pops the hidden struct pointer, so we have to push it back. + NumBytesForCalleeToPop = 4; + } else { + NumBytesForCalleeToPop = 0; // Callee pops nothing. + } + + if (CLI.DoesNotReturn && !getTargetMachine().Options.TrapUnreachable) { + // No need to reset the stack after the call if the call doesn't return. To + // make the MI verify, we'll pretend the callee does it for us. + NumBytesForCalleeToPop = NumBytes; + } + + // Returns a flag for retval copy to use. + if (!IsSibcall) { + Chain = DAG.getCALLSEQ_END( + Chain, DAG.getIntPtrConstant(NumBytesToPop, DL, true), + DAG.getIntPtrConstant(NumBytesForCalleeToPop, DL, true), InFlag, DL); + InFlag = Chain.getValue(1); + } + + // Handle result values, copying them out of physregs into vregs that we + // return. + return LowerCallResult(Chain, InFlag, CallConv, isVarArg, Ins, DL, DAG, + InVals); +} + +SDValue M680x0TargetLowering::LowerCallResult( + SDValue Chain, SDValue InFlag, CallingConv::ID CallConv, bool isVarArg, + const SmallVectorImpl &Ins, const SDLoc &DL, + SelectionDAG &DAG, SmallVectorImpl &InVals) const { + + // Assign locations to each value returned by this call. + SmallVector RVLocs; + CCState CCInfo(CallConv, isVarArg, DAG.getMachineFunction(), RVLocs, + *DAG.getContext()); + CCInfo.AnalyzeCallResult(Ins, RetCC_M680x0); + + // Copy all of the result registers out of their specified physreg. + for (unsigned i = 0, e = RVLocs.size(); i != e; ++i) { + CCValAssign &VA = RVLocs[i]; + EVT CopyVT = VA.getLocVT(); + + /// ??? is this correct? + Chain = DAG.getCopyFromReg(Chain, DL, VA.getLocReg(), CopyVT, InFlag) + .getValue(1); + SDValue Val = Chain.getValue(0); + + if (VA.isExtInLoc() && VA.getValVT().getScalarType() == MVT::i1) + Val = DAG.getNode(ISD::TRUNCATE, DL, VA.getValVT(), Val); + + InFlag = Chain.getValue(2); + InVals.push_back(Val); + } + + return Chain; +} + +//===----------------------------------------------------------------------===// +// Formal Arguments Calling Convention Implementation +//===----------------------------------------------------------------------===// + +SDValue M680x0TargetLowering::LowerFormalArguments( + SDValue Chain, CallingConv::ID CCID, bool isVarArg, + const SmallVectorImpl &Ins, const SDLoc &DL, + SelectionDAG &DAG, SmallVectorImpl &InVals) const { + MachineFunction &MF = DAG.getMachineFunction(); + M680x0MachineFunctionInfo *MMFI = MF.getInfo(); + // const TargetFrameLowering &TFL = *Subtarget.getFrameLowering(); + + MachineFrameInfo &MFI = MF.getFrameInfo(); + + // TODO interrupts... + // if (CCID == CallingConv::M680x0_INTR) { + // bool isLegal = Ins.size() == 1 || + // (Ins.size() == 2 && ((Is64Bit && Ins[1].VT == MVT::i64) || + // (!Is64Bit && Ins[1].VT == + // MVT::i32))); + // if (!isLegal) + // report_fatal_error("M680x0 interrupts may take one or two arguments"); + // } + + // Assign locations to all of the incoming arguments. + SmallVector ArgLocs; + M680x0CCState CCInfo(MF.getFunction(), CCID, isVarArg, MF, ArgLocs, + *DAG.getContext()); + + CCInfo.AnalyzeFormalArguments(Ins, CC_M680x0); + + unsigned LastVal = ~0U; + SDValue ArgValue; + for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) { + CCValAssign &VA = ArgLocs[i]; + assert(VA.getValNo() != LastVal && "Same value in different locations"); + + LastVal = VA.getValNo(); + + if (VA.isRegLoc()) { + EVT RegVT = VA.getLocVT(); + const TargetRegisterClass *RC; + if (RegVT == MVT::i32) + RC = &M680x0::XR32RegClass; + else + llvm_unreachable("Unknown argument type!"); + + unsigned Reg = MF.addLiveIn(VA.getLocReg(), RC); + ArgValue = DAG.getCopyFromReg(Chain, DL, Reg, RegVT); + + // If this is an 8 or 16-bit value, it is really passed promoted to 32 + // bits. Insert an assert[sz]ext to capture this, then truncate to the + // right size. + if (VA.getLocInfo() == CCValAssign::SExt) { + ArgValue = DAG.getNode(ISD::AssertSext, DL, RegVT, ArgValue, + DAG.getValueType(VA.getValVT())); + } else if (VA.getLocInfo() == CCValAssign::ZExt) { + ArgValue = DAG.getNode(ISD::AssertZext, DL, RegVT, ArgValue, + DAG.getValueType(VA.getValVT())); + } else if (VA.getLocInfo() == CCValAssign::BCvt) { + ArgValue = DAG.getBitcast(VA.getValVT(), ArgValue); + } + + if (VA.isExtInLoc()) { + ArgValue = DAG.getNode(ISD::TRUNCATE, DL, VA.getValVT(), ArgValue); + } + } else { + assert(VA.isMemLoc()); + ArgValue = LowerMemArgument(Chain, CCID, Ins, DL, DAG, VA, MFI, i); + } + + // If value is passed via pointer - do a load. + // TODO debug how this really works + // ??? May I remove this indirect shizzle? + if (VA.getLocInfo() == CCValAssign::Indirect) + ArgValue = + DAG.getLoad(VA.getValVT(), DL, Chain, ArgValue, MachinePointerInfo()); + + InVals.push_back(ArgValue); + } + + for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) { + // Swift calling convention does not require we copy the sret argument + // into %D0 for the return. We don't set SRetReturnReg for Swift. + if (CCID == CallingConv::Swift) + continue; + + // ABI require that for returning structs by value we copy the sret argument + // into %D0 for the return. Save the argument into a virtual register so + // that we can access it from the return points. + if (Ins[i].Flags.isSRet()) { + unsigned Reg = MMFI->getSRetReturnReg(); + if (!Reg) { + MVT PtrTy = getPointerTy(DAG.getDataLayout()); + Reg = MF.getRegInfo().createVirtualRegister(getRegClassFor(PtrTy)); + MMFI->setSRetReturnReg(Reg); + } + SDValue Copy = DAG.getCopyToReg(DAG.getEntryNode(), DL, Reg, InVals[i]); + Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, Copy, Chain); + break; + } + } + + unsigned StackSize = CCInfo.getNextStackOffset(); + // Align stack specially for tail calls. + if (shouldGuaranteeTCO(CCID, MF.getTarget().Options.GuaranteedTailCallOpt)) + StackSize = GetAlignedArgumentStackSize(StackSize, DAG); + + // If the function takes variable number of arguments, make a frame index for + // the start of the first vararg value... for expansion of llvm.va_start. We + // can skip this if there are no va_start calls. + if (MFI.hasVAStart()) { + MMFI->setVarArgsFrameIndex(MFI.CreateFixedObject(1, StackSize, true)); + } + + if (isVarArg && MFI.hasMustTailInVarArgFunc()) { + // We forward some GPRs and some vector types. + SmallVector RegParmTypes; + MVT IntVT = MVT::i32; + RegParmTypes.push_back(IntVT); + + // Compute the set of forwarded registers. The rest are scratch. + // ??? what is this for? + SmallVectorImpl &Forwards = + MMFI->getForwardedMustTailRegParms(); + CCInfo.analyzeMustTailForwardedRegisters(Forwards, RegParmTypes, CC_M680x0); + + // Copy all forwards from physical to virtual registers. + for (ForwardedRegister &F : Forwards) { + // FIXME: Can we use a less constrained schedule? + SDValue RegVal = DAG.getCopyFromReg(Chain, DL, F.VReg, F.VT); + F.VReg = MF.getRegInfo().createVirtualRegister(getRegClassFor(F.VT)); + Chain = DAG.getCopyToReg(Chain, DL, F.VReg, RegVal); + } + } + + // Some CCs need callee pop. + if (M680x0::isCalleePop(CCID, isVarArg, + MF.getTarget().Options.GuaranteedTailCallOpt)) { + MMFI->setBytesToPopOnReturn(StackSize); // Callee pops everything. + // } else if (CCID == CallingConv::M680x0_INTR && Ins.size() == 2) { + // // M680x0 interrupts must pop the error code if present + // MMFI->setBytesToPopOnReturn(4); + } else { + MMFI->setBytesToPopOnReturn(0); // Callee pops nothing. + // If this is an sret function, the return should pop the hidden pointer. + if (!canGuaranteeTCO(CCID) && argsAreStructReturn(Ins) == StackStructReturn) + MMFI->setBytesToPopOnReturn(4); + } + + // if (!Is64Bit) { + // // RegSaveFrameIndex is M680x0-64 only. + // MMFI->setRegSaveFrameIndex(0xAAAAAAA); + // if (CCID == CallingConv::M680x0_FastCall || + // CCID == CallingConv::M680x0_ThisCall) + // // fastcc functions can't have varargs. + // MMFI->setVarArgsFrameIndex(0xAAAAAAA); + // } + + MMFI->setArgumentStackSize(StackSize); + + return Chain; +} + +//===----------------------------------------------------------------------===// +// Return Value Calling Convention Implementation +//===----------------------------------------------------------------------===// + +SDValue +M680x0TargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CCID, + bool isVarArg, + const SmallVectorImpl &Outs, + const SmallVectorImpl &OutVals, + const SDLoc &DL, SelectionDAG &DAG) const { + MachineFunction &MF = DAG.getMachineFunction(); + M680x0MachineFunctionInfo *MFI = MF.getInfo(); + + SmallVector RVLocs; + CCState CCInfo(CCID, isVarArg, MF, RVLocs, *DAG.getContext()); + CCInfo.AnalyzeReturn(Outs, RetCC_M680x0); + + SDValue Flag; + SmallVector RetOps; + // Operand #0 = Chain (updated below) + RetOps.push_back(Chain); + // Operand #1 = Bytes To Pop + RetOps.push_back( + DAG.getTargetConstant(MFI->getBytesToPopOnReturn(), DL, MVT::i32)); + + // Copy the result values into the output registers. + for (unsigned i = 0, e = RVLocs.size(); i != e; ++i) { + CCValAssign &VA = RVLocs[i]; + assert(VA.isRegLoc() && "Can only return in registers!"); + SDValue ValToCopy = OutVals[i]; + EVT ValVT = ValToCopy.getValueType(); + + // Promote values to the appropriate types. + if (VA.getLocInfo() == CCValAssign::SExt) + ValToCopy = DAG.getNode(ISD::SIGN_EXTEND, DL, VA.getLocVT(), ValToCopy); + else if (VA.getLocInfo() == CCValAssign::ZExt) + ValToCopy = DAG.getNode(ISD::ZERO_EXTEND, DL, VA.getLocVT(), ValToCopy); + else if (VA.getLocInfo() == CCValAssign::AExt) { + if (ValVT.isVector() && ValVT.getVectorElementType() == MVT::i1) + ValToCopy = DAG.getNode(ISD::SIGN_EXTEND, DL, VA.getLocVT(), ValToCopy); + else + ValToCopy = DAG.getNode(ISD::ANY_EXTEND, DL, VA.getLocVT(), ValToCopy); + } else if (VA.getLocInfo() == CCValAssign::BCvt) + ValToCopy = DAG.getBitcast(VA.getLocVT(), ValToCopy); + + Chain = DAG.getCopyToReg(Chain, DL, VA.getLocReg(), ValToCopy, Flag); + Flag = Chain.getValue(1); + RetOps.push_back(DAG.getRegister(VA.getLocReg(), VA.getLocVT())); + } + + // Swift calling convention does not require we copy the sret argument + // into %d0 for the return, and SRetReturnReg is not set for Swift. + + // ABI require that for returning structs by value we copy the sret argument + // into %D0 for the return. Save the argument into a virtual register so that + // we can access it from the return points. + // + // Checking Function.hasStructRetAttr() here is insufficient because the IR + // may not have an explicit sret argument. If MFI.CanLowerReturn is + // false, then an sret argument may be implicitly inserted in the SelDAG. In + // either case MFI->setSRetReturnReg() will have been called. + if (unsigned SRetReg = MFI->getSRetReturnReg()) { + // ??? Can i just move this to the top and escape this explanation? + // When we have both sret and another return value, we should use the + // original Chain stored in RetOps[0], instead of the current Chain updated + // in the above loop. If we only have sret, RetOps[0] equals to Chain. + + // For the case of sret and another return value, we have + // Chain_0 at the function entry + // Chain_1 = getCopyToReg(Chain_0) in the above loop + // If we use Chain_1 in getCopyFromReg, we will have + // Val = getCopyFromReg(Chain_1) + // Chain_2 = getCopyToReg(Chain_1, Val) from below + + // getCopyToReg(Chain_0) will be glued together with + // getCopyToReg(Chain_1, Val) into Unit A, getCopyFromReg(Chain_1) will be + // in Unit B, and we will have cyclic dependency between Unit A and Unit B: + // Data dependency from Unit B to Unit A due to usage of Val in + // getCopyToReg(Chain_1, Val) + // Chain dependency from Unit A to Unit B + + // So here, we use RetOps[0] (i.e Chain_0) for getCopyFromReg. + SDValue Val = DAG.getCopyFromReg(RetOps[0], DL, SRetReg, + getPointerTy(MF.getDataLayout())); + + // ??? How will this work if CC does not use registers for args passing? + // ??? What if I return multiple structs? + unsigned RetValReg = M680x0::D0; + Chain = DAG.getCopyToReg(Chain, DL, RetValReg, Val, Flag); + Flag = Chain.getValue(1); + + RetOps.push_back( + DAG.getRegister(RetValReg, getPointerTy(DAG.getDataLayout()))); + } + + // ??? What is it doing? + // const M680x0RegisterInfo *TRI = Subtarget.getRegisterInfo(); + // const MCPhysReg *I = + // TRI->getCalleeSavedRegsViaCopy(&DAG.getMachineFunction()); + // if (I) { + // for (; *I; ++I) { + // if (M680x0::GR64RegClass.contains(*I)) + // RetOps.push_back(DAG.getRegister(*I, MVT::i64)); + // else + // llvm_unreachable("Unexpected register class in CSRsViaCopy!"); + // } + // } + + RetOps[0] = Chain; // Update chain. + + // Add the flag if we have it. + if (Flag.getNode()) + RetOps.push_back(Flag); + + return DAG.getNode(M680x0ISD::RET, DL, MVT::Other, RetOps); +} + +//===----------------------------------------------------------------------===// +// Fast Calling Convention (tail call) implementation +//===----------------------------------------------------------------------===// + +// Like std call, callee cleans arguments, convention except that ECX is +// reserved for storing the tail called function address. Only 2 registers are +// free for argument passing (inreg). Tail call optimization is performed +// provided: +// * tailcallopt is enabled +// * caller/callee are fastcc +// On M680x0_64 architecture with GOT-style position independent code only +// local (within module) calls are supported at the moment. To keep the stack +// aligned according to platform abi the function GetAlignedArgumentStackSize +// ensures that argument delta is always multiples of stack alignment. (Dynamic +// linkers need this - darwin's dyld for example) If a tail called function +// callee has more arguments than the caller the caller needs to make sure that +// there is room to move the RETADDR to. This is achieved by reserving an area +// the size of the argument delta right after the original RETADDR, but before +// the saved framepointer or the spilled registers e.g. caller(arg1, arg2) +// calls callee(arg1, arg2,arg3,arg4) stack layout: +// arg1 +// arg2 +// RETADDR +// [ new RETADDR +// move area ] +// (possible EBP) +// ESI +// EDI +// local1 .. + +/// Make the stack size align e.g 16n + 12 aligned for a 16-byte align +/// requirement. +unsigned +M680x0TargetLowering::GetAlignedArgumentStackSize(unsigned StackSize, + SelectionDAG &DAG) const { + const TargetFrameLowering &TFI = *Subtarget.getFrameLowering(); + unsigned StackAlignment = TFI.getStackAlignment(); + uint64_t AlignMask = StackAlignment - 1; + int64_t Offset = StackSize; + unsigned SlotSize = Subtarget.getSlotSize(); + if ((Offset & AlignMask) <= (StackAlignment - SlotSize)) { + // Number smaller than 12 so just add the difference. + Offset += ((StackAlignment - SlotSize) - (Offset & AlignMask)); + } else { + // Mask out lower bits, add stackalignment once plus the 12 bytes. + Offset = + ((~AlignMask) & Offset) + StackAlignment + (StackAlignment - SlotSize); + } + return Offset; +} + +/// Check whether the call is eligible for tail call optimization. Targets +/// that want to do tail call optimization should implement this function. +bool M680x0TargetLowering::IsEligibleForTailCallOptimization( + SDValue Callee, CallingConv::ID CalleeCC, bool isVarArg, + bool isCalleeStructRet, bool isCallerStructRet, Type *RetTy, + const SmallVectorImpl &Outs, + const SmallVectorImpl &OutVals, + const SmallVectorImpl &Ins, SelectionDAG &DAG) const { + if (!mayTailCallThisCC(CalleeCC)) + return false; + + // If -tailcallopt is specified, make fastcc functions tail-callable. + MachineFunction &MF = DAG.getMachineFunction(); + const auto &CallerF = MF.getFunction(); + + CallingConv::ID CallerCC = CallerF.getCallingConv(); + bool CCMatch = CallerCC == CalleeCC; + + if (DAG.getTarget().Options.GuaranteedTailCallOpt) { + if (canGuaranteeTCO(CalleeCC) && CCMatch) + return true; + return false; + } + + // Look for obvious safe cases to perform tail call optimization that do not + // require ABI changes. This is what gcc calls sibcall. + + // Can't do sibcall if stack needs to be dynamically re-aligned. PEI needs to + // emit a special epilogue. + const M680x0RegisterInfo *RegInfo = Subtarget.getRegisterInfo(); + if (RegInfo->needsStackRealignment(MF)) + return false; + + // Also avoid sibcall optimization if either caller or callee uses struct + // return semantics. + if (isCalleeStructRet || isCallerStructRet) + return false; + + // Do not sibcall optimize vararg calls unless all arguments are passed via + // registers. + LLVMContext &C = *DAG.getContext(); + if (isVarArg && !Outs.empty()) { + + SmallVector ArgLocs; + CCState CCInfo(CalleeCC, isVarArg, MF, ArgLocs, C); + + CCInfo.AnalyzeCallOperands(Outs, CC_M680x0); + for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) + if (!ArgLocs[i].isRegLoc()) + return false; + } + + // Check that the call results are passed in the same way. + if (!CCState::resultsCompatible(CalleeCC, CallerCC, MF, C, Ins, RetCC_M680x0, + RetCC_M680x0)) + return false; + + // The callee has to preserve all registers the caller needs to preserve. + const M680x0RegisterInfo *TRI = Subtarget.getRegisterInfo(); + const uint32_t *CallerPreserved = TRI->getCallPreservedMask(MF, CallerCC); + if (!CCMatch) { + const uint32_t *CalleePreserved = TRI->getCallPreservedMask(MF, CalleeCC); + if (!TRI->regmaskSubsetEqual(CallerPreserved, CalleePreserved)) + return false; + } + + unsigned StackArgsSize = 0; + + // If the callee takes no arguments then go on to check the results of the + // call. + if (!Outs.empty()) { + // Check if stack adjustment is needed. For now, do not do this if any + // argument is passed on the stack. + SmallVector ArgLocs; + CCState CCInfo(CalleeCC, isVarArg, MF, ArgLocs, C); + + CCInfo.AnalyzeCallOperands(Outs, CC_M680x0); + StackArgsSize = CCInfo.getNextStackOffset(); + + if (CCInfo.getNextStackOffset()) { + // Check if the arguments are already laid out in the right way as + // the caller's fixed stack objects. + MachineFrameInfo &MFI = MF.getFrameInfo(); + const MachineRegisterInfo *MRI = &MF.getRegInfo(); + const M680x0InstrInfo *TII = Subtarget.getInstrInfo(); + for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) { + CCValAssign &VA = ArgLocs[i]; + SDValue Arg = OutVals[i]; + ISD::ArgFlagsTy Flags = Outs[i].Flags; + if (VA.getLocInfo() == CCValAssign::Indirect) + return false; + if (!VA.isRegLoc()) { + if (!MatchingStackOffset(Arg, VA.getLocMemOffset(), Flags, MFI, MRI, + TII, VA)) + return false; + } + } + } + + bool PositionIndependent = isPositionIndependent(); + // If the tailcall address may be in a register, then make sure it's + // possible to register allocate for it. The call address can + // only target %A0 or %A1 since the tail call must be scheduled after + // callee-saved registers are restored. These happen to be the same + // registers used to pass 'inreg' arguments so watch out for those. + if ((!isa(Callee) && + !isa(Callee)) || + PositionIndependent) { + unsigned NumInRegs = 0; + // In PIC we need an extra register to formulate the address computation + // for the callee. + unsigned MaxInRegs = PositionIndependent ? 1 : 2; + + for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) { + CCValAssign &VA = ArgLocs[i]; + if (!VA.isRegLoc()) + continue; + unsigned Reg = VA.getLocReg(); + switch (Reg) { + default: + break; + case M680x0::A0: + case M680x0::A1: + if (++NumInRegs == MaxInRegs) + return false; + break; + } + } + } + + const MachineRegisterInfo &MRI = MF.getRegInfo(); + if (!parametersInCSRMatch(MRI, CallerPreserved, ArgLocs, OutVals)) + return false; + } + + bool CalleeWillPop = M680x0::isCalleePop( + CalleeCC, isVarArg, MF.getTarget().Options.GuaranteedTailCallOpt); + + if (unsigned BytesToPop = + MF.getInfo()->getBytesToPopOnReturn()) { + // If we have bytes to pop, the callee must pop them. + bool CalleePopMatches = CalleeWillPop && BytesToPop == StackArgsSize; + if (!CalleePopMatches) + return false; + } else if (CalleeWillPop && StackArgsSize > 0) { + // If we don't have bytes to pop, make sure the callee doesn't pop any. + return false; + } + + return true; +} + +//===----------------------------------------------------------------------===// +// Custom Lower +//===----------------------------------------------------------------------===// + +SDValue M680x0TargetLowering::LowerOperation(SDValue Op, + SelectionDAG &DAG) const { + switch (Op.getOpcode()) { + default: + llvm_unreachable("Should not custom lower this!"); + case ISD::MUL: + return LowerMUL(Op, DAG); + case ISD::SADDO: + case ISD::UADDO: + case ISD::SSUBO: + case ISD::USUBO: + case ISD::SMULO: + case ISD::UMULO: + return LowerXALUO(Op, DAG); + case ISD::SETCC: + return LowerSETCC(Op, DAG); + case ISD::SETCCCARRY: + return LowerSETCCCARRY(Op, DAG); + case ISD::SELECT: + return LowerSELECT(Op, DAG); + case ISD::BRCOND: + return LowerBRCOND(Op, DAG); + case ISD::ADDC: + case ISD::ADDE: + case ISD::SUBC: + case ISD::SUBE: + return LowerADDC_ADDE_SUBC_SUBE(Op, DAG); + case ISD::ConstantPool: + return LowerConstantPool(Op, DAG); + case ISD::GlobalAddress: + return LowerGlobalAddress(Op, DAG); + case ISD::ExternalSymbol: + return LowerExternalSymbol(Op, DAG); + case ISD::BlockAddress: + return LowerBlockAddress(Op, DAG); + case ISD::JumpTable: + return LowerJumpTable(Op, DAG); + case ISD::VASTART: + return LowerVASTART(Op, DAG); + case ISD::DYNAMIC_STACKALLOC: + return LowerDYNAMIC_STACKALLOC(Op, DAG); + } +} + +SDValue M680x0TargetLowering::LowerMUL(SDValue &N, SelectionDAG &DAG) const { + EVT VT = N->getValueType(0); + SDLoc DL(N); + + ConstantSDNode *C = dyn_cast(N->getOperand(1)); + + if (C && isPowerOf2_64(C->getZExtValue())) { + uint64_t MulAmt = C->getZExtValue(); + + if (isPowerOf2_64(MulAmt)) + return DAG.getNode(ISD::SHL, DL, VT, N->getOperand(0), + DAG.getConstant(Log2_64(MulAmt), DL, MVT::i8)); + + if (isPowerOf2_64(MulAmt - 1)) { + // (mul x, 2^N + 1) => (add (shl x, N), x) + return DAG.getNode( + ISD::ADD, DL, VT, N->getOperand(0), + DAG.getNode(ISD::SHL, DL, VT, N->getOperand(0), + DAG.getConstant(Log2_64(MulAmt - 1), DL, MVT::i8))); + } + + if (isPowerOf2_64(MulAmt + 1)) { + // (mul x, 2^N - 1) => (sub (shl x, N), x) + return DAG.getNode( + ISD::SUB, DL, VT, + DAG.getNode(ISD::SHL, DL, VT, N->getOperand(0), + DAG.getConstant(Log2_64(MulAmt + 1), DL, MVT::i8)), + N->getOperand(0)); + } + } + + // These cannot be handle by M68000 and M68010 + if (!Subtarget.isM68020()) { + SDValue LHS = N->getOperand(0); + SDValue RHS = N->getOperand(1); + if (VT == MVT::i32) { + SDValue Args[] = {LHS, RHS}; + return makeLibCall(DAG, RTLIB::MUL_I32, VT, Args, true, DL).first; + } else if (VT == MVT::i64) { + unsigned LoSize = VT.getSizeInBits(); + SDValue HiLHS = DAG.getNode( + ISD::SRA, DL, VT, LHS, + DAG.getConstant(LoSize - 1, DL, getPointerTy(DAG.getDataLayout()))); + SDValue HiRHS = DAG.getNode( + ISD::SRA, DL, VT, RHS, + DAG.getConstant(LoSize - 1, DL, getPointerTy(DAG.getDataLayout()))); + SDValue Args[] = {HiLHS, LHS, HiRHS, RHS}; + SDValue Ret = makeLibCall(DAG, RTLIB::MUL_I64, VT, Args, true, DL).first; + + // We are intereseted in Lo part + return DAG.getNode(ISD::EXTRACT_ELEMENT, DL, VT, Ret, + DAG.getIntPtrConstant(0, DL)); + } + } + + // The rest is considered legal + return SDValue(); +} + +SDValue M680x0TargetLowering::LowerXALUO(SDValue Op, SelectionDAG &DAG) const { + // Lower the "add/sub/mul with overflow" instruction into a regular ins plus + // a "setcc" instruction that checks the overflow flag. The "brcond" lowering + // looks for this combo and may remove the "setcc" instruction if the "setcc" + // has only one use. + SDNode *N = Op.getNode(); + SDValue LHS = N->getOperand(0); + SDValue RHS = N->getOperand(1); + unsigned BaseOp = 0; + unsigned Cond = 0; + SDLoc DL(Op); + switch (Op.getOpcode()) { + default: + llvm_unreachable("Unknown ovf instruction!"); + case ISD::SADDO: + BaseOp = M680x0ISD::ADD; + Cond = M680x0::COND_VS; + break; + case ISD::UADDO: + BaseOp = M680x0ISD::ADD; + Cond = M680x0::COND_CS; + break; + case ISD::SSUBO: + BaseOp = M680x0ISD::SUB; + Cond = M680x0::COND_VS; + break; + case ISD::USUBO: + BaseOp = M680x0ISD::SUB; + Cond = M680x0::COND_CS; + break; + // case ISD::SMULO: + // BaseOp = M680x0ISD::SMUL; + // Cond = M680x0::COND_VS; + // break; + // case ISD::UMULO: { // i64, i8 = umulo lhs, rhs --> i64, i64, i32 umul + // lhs,rhs + // SDVTList VTs = + // DAG.getVTList(N->getValueType(0), N->getValueType(0), MVT::i8); + // SDValue Sum = DAG.getNode(M680x0ISD::UMUL, DL, VTs, LHS, RHS); + // + // SDValue SetCC = + // DAG.getNode(M680x0ISD::SETCC, DL, N->getValueType(1), + // DAG.getConstant(M680x0::COND_VS, DL, MVT::i8), + // SDValue(Sum.getNode(), 2)); + // + // return DAG.getNode(ISD::MERGE_VALUES, DL, N->getVTList(), Sum, SetCC); + // } + } + + // Also sets CCR. + SDVTList VTs = DAG.getVTList(N->getValueType(0), MVT::i8); + SDValue Sum = DAG.getNode(BaseOp, DL, VTs, LHS, RHS); + + SDValue SetCC = DAG.getNode(M680x0ISD::SETCC, DL, N->getValueType(1), + DAG.getConstant(Cond, DL, MVT::i8), + SDValue(Sum.getNode(), 1)); + + return DAG.getNode(ISD::MERGE_VALUES, DL, N->getVTList(), Sum, SetCC); +} + +/// Create a BT (Bit Test) node - Test bit \p BitNo in \p Src and set condition +/// according to equal/not-equal condition code \p CC. +static SDValue getBitTestCondition(SDValue Src, SDValue BitNo, ISD::CondCode CC, + const SDLoc &DL, SelectionDAG &DAG) { + // If Src is i8, promote it to i32 with any_extend. There is no i8 BT + // instruction. Since the shift amount is in-range-or-undefined, we know + // that doing a bittest on the i32 value is ok. + if (Src.getValueType() == MVT::i8 || Src.getValueType() == MVT::i16) + Src = DAG.getNode(ISD::ANY_EXTEND, DL, MVT::i32, Src); + + // If the operand types disagree, extend the shift amount to match. Since + // BT ignores high bits (like shifts) we can use anyextend. + if (Src.getValueType() != BitNo.getValueType()) + BitNo = DAG.getNode(ISD::ANY_EXTEND, DL, Src.getValueType(), BitNo); + + SDValue BT = DAG.getNode(M680x0ISD::BT, DL, MVT::i32, Src, BitNo); + + // NOTE BTST sets CCR.Z flag + M680x0::CondCode Cond = CC == ISD::SETEQ ? M680x0::COND_NE : M680x0::COND_EQ; + return DAG.getNode(M680x0ISD::SETCC, DL, MVT::i8, + DAG.getConstant(Cond, DL, MVT::i8), BT); +} + +/// Result of 'and' is compared against zero. Change to a BT node if possible. +static SDValue LowerAndToBT(SDValue And, ISD::CondCode CC, const SDLoc &DL, + SelectionDAG &DAG) { + SDValue Op0 = And.getOperand(0); + SDValue Op1 = And.getOperand(1); + if (Op0.getOpcode() == ISD::TRUNCATE) + Op0 = Op0.getOperand(0); + if (Op1.getOpcode() == ISD::TRUNCATE) + Op1 = Op1.getOperand(0); + + SDValue LHS, RHS; + if (Op1.getOpcode() == ISD::SHL) + std::swap(Op0, Op1); + if (Op0.getOpcode() == ISD::SHL) { + if (isOneConstant(Op0.getOperand(0))) { + // If we looked past a truncate, check that it's only truncating away + // known zeros. + unsigned BitWidth = Op0.getValueSizeInBits(); + unsigned AndBitWidth = And.getValueSizeInBits(); + if (BitWidth > AndBitWidth) { + KnownBits Known; + DAG.computeKnownBits(Op0, Known); + if (Known.countMinLeadingZeros() < BitWidth - AndBitWidth) + return SDValue(); + } + LHS = Op1; + RHS = Op0.getOperand(1); + } + } else if (Op1.getOpcode() == ISD::Constant) { + ConstantSDNode *AndRHS = cast(Op1); + uint64_t AndRHSVal = AndRHS->getZExtValue(); + SDValue AndLHS = Op0; + + if (AndRHSVal == 1 && AndLHS.getOpcode() == ISD::SRL) { + LHS = AndLHS.getOperand(0); + RHS = AndLHS.getOperand(1); + } + + // Use BT if the immediate can't be encoded in a TEST instruction. + if (!isUInt<32>(AndRHSVal) && isPowerOf2_64(AndRHSVal)) { + LHS = AndLHS; + RHS = DAG.getConstant(Log2_64_Ceil(AndRHSVal), DL, LHS.getValueType()); + } + } + + if (LHS.getNode()) + return getBitTestCondition(LHS, RHS, CC, DL, DAG); + + return SDValue(); +} + +static M680x0::CondCode TranslateIntegerM680x0CC(ISD::CondCode SetCCOpcode) { + switch (SetCCOpcode) { + default: + llvm_unreachable("Invalid integer condition!"); + case ISD::SETEQ: + return M680x0::COND_EQ; + case ISD::SETGT: + return M680x0::COND_GT; + case ISD::SETGE: + return M680x0::COND_GE; + case ISD::SETLT: + return M680x0::COND_LT; + case ISD::SETLE: + return M680x0::COND_LE; + case ISD::SETNE: + return M680x0::COND_NE; + case ISD::SETULT: + return M680x0::COND_CS; + case ISD::SETUGE: + return M680x0::COND_CC; + case ISD::SETUGT: + return M680x0::COND_HI; + case ISD::SETULE: + return M680x0::COND_LS; + } +} + +/// Do a one-to-one translation of a ISD::CondCode to the M680x0-specific +/// condition code, returning the condition code and the LHS/RHS of the +/// comparison to make. +static unsigned TranslateM680x0CC(ISD::CondCode SetCCOpcode, const SDLoc &DL, + bool isFP, SDValue &LHS, SDValue &RHS, + SelectionDAG &DAG) { + if (!isFP) { + if (ConstantSDNode *RHSC = dyn_cast(RHS)) { + if (SetCCOpcode == ISD::SETGT && RHSC->isAllOnesValue()) { + // X > -1 -> X == 0, jump !sign. + RHS = DAG.getConstant(0, DL, RHS.getValueType()); + return M680x0::COND_PL; + } + if (SetCCOpcode == ISD::SETLT && RHSC->isNullValue()) { + // X < 0 -> X == 0, jump on sign. + return M680x0::COND_MI; + } + if (SetCCOpcode == ISD::SETLT && RHSC->getZExtValue() == 1) { + // X < 1 -> X <= 0 + RHS = DAG.getConstant(0, DL, RHS.getValueType()); + return M680x0::COND_LE; + } + } + + return TranslateIntegerM680x0CC(SetCCOpcode); + } + + // First determine if it is required or is profitable to flip the operands. + + // If LHS is a foldable load, but RHS is not, flip the condition. + if (ISD::isNON_EXTLoad(LHS.getNode()) && !ISD::isNON_EXTLoad(RHS.getNode())) { + SetCCOpcode = getSetCCSwappedOperands(SetCCOpcode); + std::swap(LHS, RHS); + } + + switch (SetCCOpcode) { + default: + break; + case ISD::SETOLT: + case ISD::SETOLE: + case ISD::SETUGT: + case ISD::SETUGE: + std::swap(LHS, RHS); + break; + } + + // On a floating point condition, the flags are set as follows: + // ZF PF CF op + // 0 | 0 | 0 | X > Y + // 0 | 0 | 1 | X < Y + // 1 | 0 | 0 | X == Y + // 1 | 1 | 1 | unordered + switch (SetCCOpcode) { + default: + llvm_unreachable("Condcode should be pre-legalized away"); + case ISD::SETUEQ: + case ISD::SETEQ: + return M680x0::COND_EQ; + case ISD::SETOLT: // flipped + case ISD::SETOGT: + case ISD::SETGT: + return M680x0::COND_HI; + case ISD::SETOLE: // flipped + case ISD::SETOGE: + case ISD::SETGE: + return M680x0::COND_CC; + case ISD::SETUGT: // flipped + case ISD::SETULT: + case ISD::SETLT: + return M680x0::COND_CS; + case ISD::SETUGE: // flipped + case ISD::SETULE: + case ISD::SETLE: + return M680x0::COND_LS; + case ISD::SETONE: + case ISD::SETNE: + return M680x0::COND_NE; + // case ISD::SETUO: return M680x0::COND_P; + // case ISD::SETO: return M680x0::COND_NP; + case ISD::SETOEQ: + case ISD::SETUNE: + return M680x0::COND_INVALID; + } +} + +// Convert (truncate (srl X, N) to i1) to (bt X, N) +static SDValue LowerTruncateToBT(SDValue Op, ISD::CondCode CC, const SDLoc &DL, + SelectionDAG &DAG) { + + assert(Op.getOpcode() == ISD::TRUNCATE && Op.getValueType() == MVT::i1 && + "Expected TRUNCATE to i1 node"); + + if (Op.getOperand(0).getOpcode() != ISD::SRL) + return SDValue(); + + SDValue ShiftRight = Op.getOperand(0); + return getBitTestCondition(ShiftRight.getOperand(0), ShiftRight.getOperand(1), + CC, DL, DAG); +} + +/// \brief return true if \c Op has a use that doesn't just read flags. +static bool hasNonFlagsUse(SDValue Op) { + for (SDNode::use_iterator UI = Op->use_begin(), UE = Op->use_end(); UI != UE; + ++UI) { + SDNode *User = *UI; + unsigned UOpNo = UI.getOperandNo(); + if (User->getOpcode() == ISD::TRUNCATE && User->hasOneUse()) { + // Look pass truncate. + UOpNo = User->use_begin().getOperandNo(); + User = *User->use_begin(); + } + + if (User->getOpcode() != ISD::BRCOND && User->getOpcode() != ISD::SETCC && + !(User->getOpcode() == ISD::SELECT && UOpNo == 0)) + return true; + } + return false; +} + +SDValue M680x0TargetLowering::EmitTest(SDValue Op, unsigned M680x0CC, + const SDLoc &DL, + SelectionDAG &DAG) const { + + // CF and OF aren't always set the way we want. Determine which + // of these we need. + bool NeedCF = false; + bool NeedOF = false; + switch (M680x0CC) { + default: + break; + case M680x0::COND_HI: + case M680x0::COND_CC: + case M680x0::COND_CS: + case M680x0::COND_LS: + NeedCF = true; + break; + case M680x0::COND_GT: + case M680x0::COND_GE: + case M680x0::COND_LT: + case M680x0::COND_LE: + case M680x0::COND_VS: + case M680x0::COND_VC: { + // Check if we really need to set the + // Overflow flag. If NoSignedWrap is present + // that is not actually needed. + switch (Op->getOpcode()) { + case ISD::ADD: + case ISD::SUB: + case ISD::MUL: + case ISD::SHL: { + if (Op.getNode()->getFlags().hasNoSignedWrap()) + break; + LLVM_FALLTHROUGH; + } + default: + NeedOF = true; + break; + } + break; + } + } + // See if we can use the CCR value from the operand instead of + // doing a separate TEST. TEST always sets OF and CF to 0, so unless + // we prove that the arithmetic won't overflow, we can't use OF or CF. + if (Op.getResNo() != 0 || NeedOF || NeedCF) { + // Emit a CMP with 0, which is the TEST pattern. + return DAG.getNode(M680x0ISD::CMP, DL, MVT::i8, + DAG.getConstant(0, DL, Op.getValueType()), Op); + } + unsigned Opcode = 0; + unsigned NumOperands = 0; + + // Truncate operations may prevent the merge of the SETCC instruction + // and the arithmetic instruction before it. Attempt to truncate the operands + // of the arithmetic instruction and use a reduced bit-width instruction. + bool NeedTruncation = false; + SDValue ArithOp = Op; + if (Op->getOpcode() == ISD::TRUNCATE && Op->hasOneUse()) { + SDValue Arith = Op->getOperand(0); + // Both the trunc and the arithmetic op need to have one user each. + if (Arith->hasOneUse()) + switch (Arith.getOpcode()) { + default: + break; + case ISD::ADD: + case ISD::SUB: + case ISD::AND: + case ISD::OR: + case ISD::XOR: { + NeedTruncation = true; + ArithOp = Arith; + } + } + } + + // NOTICE: In the code below we use ArithOp to hold the arithmetic operation + // which may be the result of a CAST. We use the variable 'Op', which is the + // non-casted variable when we check for possible users. + switch (ArithOp.getOpcode()) { + case ISD::ADD: + Opcode = M680x0ISD::ADD; + NumOperands = 2; + break; + case ISD::SHL: + case ISD::SRL: + // If we have a constant logical shift that's only used in a comparison + // against zero turn it into an equivalent AND. This allows turning it into + // a TEST instruction later. + if ((M680x0CC == M680x0::COND_EQ || M680x0CC == M680x0::COND_NE) && + Op->hasOneUse() && isa(Op->getOperand(1)) && + !hasNonFlagsUse(Op)) { + EVT VT = Op.getValueType(); + unsigned BitWidth = VT.getSizeInBits(); + unsigned ShAmt = Op->getConstantOperandVal(1); + if (ShAmt >= BitWidth) // Avoid undefined shifts. + break; + APInt Mask = ArithOp.getOpcode() == ISD::SRL + ? APInt::getHighBitsSet(BitWidth, BitWidth - ShAmt) + : APInt::getLowBitsSet(BitWidth, BitWidth - ShAmt); + if (!Mask.isSignedIntN(32)) // Avoid large immediates. + break; + Op = DAG.getNode(ISD::AND, DL, VT, Op->getOperand(0), + DAG.getConstant(Mask, DL, VT)); + } + break; + + case ISD::AND: + // If the primary 'and' result isn't used, don't bother using + // M680x0ISD::AND, because a TEST instruction will be better. + if (!hasNonFlagsUse(Op)) { + SDValue Op0 = ArithOp->getOperand(0); + SDValue Op1 = ArithOp->getOperand(1); + EVT VT = ArithOp.getValueType(); + bool isAndn = isBitwiseNot(Op0) || isBitwiseNot(Op1); + bool isLegalAndnType = VT == MVT::i32 || VT == MVT::i64; + + // But if we can combine this into an ANDN operation, then create an AND + // now and allow it to be pattern matched into an ANDN. + if (/*!Subtarget.hasBMI() ||*/ !isAndn || !isLegalAndnType) + break; + } + LLVM_FALLTHROUGH; + case ISD::SUB: + case ISD::OR: + case ISD::XOR: + // Due to the ISEL shortcoming noted above, be conservative if this op is + // likely to be selected as part of a load-modify-store instruction. + for (SDNode::use_iterator UI = Op.getNode()->use_begin(), + UE = Op.getNode()->use_end(); + UI != UE; ++UI) + if (UI->getOpcode() == ISD::STORE) + goto default_case; + + // Otherwise use a regular CCR-setting instruction. + switch (ArithOp.getOpcode()) { + default: + llvm_unreachable("unexpected operator!"); + case ISD::SUB: + Opcode = M680x0ISD::SUB; + break; + case ISD::XOR: + Opcode = M680x0ISD::XOR; + break; + case ISD::AND: + Opcode = M680x0ISD::AND; + break; + case ISD::OR: + Opcode = M680x0ISD::OR; + break; + } + + NumOperands = 2; + break; + case M680x0ISD::ADD: + case M680x0ISD::SUB: + case M680x0ISD::OR: + case M680x0ISD::XOR: + case M680x0ISD::AND: + return SDValue(Op.getNode(), 1); + default: + default_case: + break; + } + + // If we found that truncation is beneficial, perform the truncation and + // update 'Op'. + if (NeedTruncation) { + EVT VT = Op.getValueType(); + SDValue WideVal = Op->getOperand(0); + EVT WideVT = WideVal.getValueType(); + unsigned ConvertedOp = 0; + // Use a target machine opcode to prevent further DAGCombine + // optimizations that may separate the arithmetic operations + // from the setcc node. + switch (WideVal.getOpcode()) { + default: + break; + case ISD::ADD: + ConvertedOp = M680x0ISD::ADD; + break; + case ISD::SUB: + ConvertedOp = M680x0ISD::SUB; + break; + case ISD::AND: + ConvertedOp = M680x0ISD::AND; + break; + case ISD::OR: + ConvertedOp = M680x0ISD::OR; + break; + case ISD::XOR: + ConvertedOp = M680x0ISD::XOR; + break; + } + + if (ConvertedOp) { + const TargetLowering &TLI = DAG.getTargetLoweringInfo(); + if (TLI.isOperationLegal(WideVal.getOpcode(), WideVT)) { + SDValue V0 = DAG.getNode(ISD::TRUNCATE, DL, VT, WideVal.getOperand(0)); + SDValue V1 = DAG.getNode(ISD::TRUNCATE, DL, VT, WideVal.getOperand(1)); + Op = DAG.getNode(ConvertedOp, DL, VT, V0, V1); + } + } + } + + if (Opcode == 0) { + // Emit a CMP with 0, which is the TEST pattern. + return DAG.getNode(M680x0ISD::CMP, DL, MVT::i8, + DAG.getConstant(0, DL, Op.getValueType()), Op); + } + SDVTList VTs = DAG.getVTList(Op.getValueType(), MVT::i8); + SmallVector Ops(Op->op_begin(), Op->op_begin() + NumOperands); + + SDValue New = DAG.getNode(Opcode, DL, VTs, Ops); + DAG.ReplaceAllUsesWith(Op, New); + return SDValue(New.getNode(), 1); +} + +/// \brief Return true if the condition is an unsigned comparison operation. +static bool isM680x0CCUnsigned(unsigned M680x0CC) { + switch (M680x0CC) { + default: + llvm_unreachable("Invalid integer condition!"); + case M680x0::COND_EQ: + case M680x0::COND_NE: + case M680x0::COND_CS: + case M680x0::COND_HI: + case M680x0::COND_LS: + case M680x0::COND_CC: + return true; + case M680x0::COND_GT: + case M680x0::COND_GE: + case M680x0::COND_LT: + case M680x0::COND_LE: + return false; + } +} + +SDValue M680x0TargetLowering::EmitCmp(SDValue Op0, SDValue Op1, + unsigned M680x0CC, const SDLoc &DL, + SelectionDAG &DAG) const { + if (isNullConstant(Op1)) + return EmitTest(Op0, M680x0CC, DL, DAG); + + assert(!(isa(Op1) && Op0.getValueType() == MVT::i1) && + "Unexpected comparison operation for MVT::i1 operands"); + + if ((Op0.getValueType() == MVT::i8 || Op0.getValueType() == MVT::i16 || + Op0.getValueType() == MVT::i32 || Op0.getValueType() == MVT::i64)) { + // Only promote the compare up to I32 if it is a 16 bit operation + // with an immediate. 16 bit immediates are to be avoided. + if ((Op0.getValueType() == MVT::i16 && + (isa(Op0) || isa(Op1))) && + !DAG.getMachineFunction().getFunction().optForMinSize()) { + unsigned ExtendOp = + isM680x0CCUnsigned(M680x0CC) ? ISD::ZERO_EXTEND : ISD::SIGN_EXTEND; + Op0 = DAG.getNode(ExtendOp, DL, MVT::i32, Op0); + Op1 = DAG.getNode(ExtendOp, DL, MVT::i32, Op1); + } + // Use SUB instead of CMP to enable CSE between SUB and CMP. + SDVTList VTs = DAG.getVTList(Op0.getValueType(), MVT::i8); + SDValue Sub = DAG.getNode(M680x0ISD::SUB, DL, VTs, Op0, Op1); + return SDValue(Sub.getNode(), 1); + } + return DAG.getNode(M680x0ISD::CMP, DL, MVT::i8, Op0, Op1); +} + +/// Result of 'and' or 'trunc to i1' is compared against zero. +/// Change to a BT node if possible. +SDValue M680x0TargetLowering::LowerToBT(SDValue Op, ISD::CondCode CC, + const SDLoc &DL, + SelectionDAG &DAG) const { + if (Op.getOpcode() == ISD::AND) + return LowerAndToBT(Op, CC, DL, DAG); + if (Op.getOpcode() == ISD::TRUNCATE && Op.getValueType() == MVT::i1) + return LowerTruncateToBT(Op, CC, DL, DAG); + return SDValue(); +} + +SDValue M680x0TargetLowering::LowerSETCC(SDValue Op, SelectionDAG &DAG) const { + MVT VT = Op.getSimpleValueType(); + assert(VT == MVT::i8 && "SetCC type must be 8-bit integer"); + + SDValue Op0 = Op.getOperand(0); + SDValue Op1 = Op.getOperand(1); + SDLoc DL(Op); + ISD::CondCode CC = cast(Op.getOperand(2))->get(); + + // Optimize to BT if possible. + // Lower (X & (1 << N)) == 0 to BT(X, N). + // Lower ((X >>u N) & 1) != 0 to BT(X, N). + // Lower ((X >>s N) & 1) != 0 to BT(X, N). + // Lower (trunc (X >> N) to i1) to BT(X, N). + if (Op0.hasOneUse() && isNullConstant(Op1) && + (CC == ISD::SETEQ || CC == ISD::SETNE)) { + if (SDValue NewSetCC = LowerToBT(Op0, CC, DL, DAG)) { + if (VT == MVT::i1) + return DAG.getNode(ISD::TRUNCATE, DL, MVT::i1, NewSetCC); + return NewSetCC; + } + } + + // Look for X == 0, X == 1, X != 0, or X != 1. We can simplify some forms of + // these. + if ((isOneConstant(Op1) || isNullConstant(Op1)) && + (CC == ISD::SETEQ || CC == ISD::SETNE)) { + + // If the input is a setcc, then reuse the input setcc or use a new one with + // the inverted condition. + if (Op0.getOpcode() == M680x0ISD::SETCC) { + M680x0::CondCode CCode = (M680x0::CondCode)Op0.getConstantOperandVal(0); + bool Invert = (CC == ISD::SETNE) ^ isNullConstant(Op1); + if (!Invert) + return Op0; + + CCode = M680x0::GetOppositeBranchCondition(CCode); + SDValue SetCC = + DAG.getNode(M680x0ISD::SETCC, DL, MVT::i8, + DAG.getConstant(CCode, DL, MVT::i8), Op0.getOperand(1)); + if (VT == MVT::i1) + return DAG.getNode(ISD::TRUNCATE, DL, MVT::i1, SetCC); + return SetCC; + } + } + if (Op0.getValueType() == MVT::i1 && (CC == ISD::SETEQ || CC == ISD::SETNE)) { + if (isOneConstant(Op1)) { + ISD::CondCode NewCC = ISD::getSetCCInverse(CC, true); + return DAG.getSetCC(DL, VT, Op0, DAG.getConstant(0, DL, MVT::i1), NewCC); + } + if (!isNullConstant(Op1)) { + SDValue Xor = DAG.getNode(ISD::XOR, DL, MVT::i1, Op0, Op1); + return DAG.getSetCC(DL, VT, Xor, DAG.getConstant(0, DL, MVT::i1), CC); + } + } + + bool isFP = Op1.getSimpleValueType().isFloatingPoint(); + unsigned M680x0CC = TranslateM680x0CC(CC, DL, isFP, Op0, Op1, DAG); + if (M680x0CC == M680x0::COND_INVALID) + return SDValue(); + + SDValue CCR = EmitCmp(Op0, Op1, M680x0CC, DL, DAG); + // CCR = ConvertCmpIfNecessary(CCR, DAG); + return DAG.getNode(M680x0ISD::SETCC, DL, MVT::i8, + DAG.getConstant(M680x0CC, DL, MVT::i8), CCR); +} + +SDValue M680x0TargetLowering::LowerSETCCCARRY(SDValue Op, + SelectionDAG &DAG) const { + SDValue LHS = Op.getOperand(0); + SDValue RHS = Op.getOperand(1); + SDValue Carry = Op.getOperand(2); + SDValue Cond = Op.getOperand(3); + SDLoc DL(Op); + + assert(LHS.getSimpleValueType().isInteger() && "SETCCCARRY is integer only."); + M680x0::CondCode CC = + TranslateIntegerM680x0CC(cast(Cond)->get()); + + EVT CarryVT = Carry.getValueType(); + APInt NegOne = APInt::getAllOnesValue(CarryVT.getScalarSizeInBits()); + Carry = DAG.getNode(M680x0ISD::ADD, DL, DAG.getVTList(CarryVT, MVT::i32), + Carry, DAG.getConstant(NegOne, DL, CarryVT)); + + SDVTList VTs = DAG.getVTList(LHS.getValueType(), MVT::i32); + SDValue Cmp = + DAG.getNode(M680x0ISD::SUBX, DL, VTs, LHS, RHS, Carry.getValue(1)); + + return DAG.getNode(M680x0ISD::SETCC, DL, MVT::i8, + DAG.getConstant(CC, DL, MVT::i8), Cmp.getValue(1)); +} + +/// Return true if opcode is a M680x0 logical comparison. +static bool isM680x0LogicalCmp(SDValue Op) { + unsigned Opc = Op.getNode()->getOpcode(); + if (Opc == M680x0ISD::CMP) + return true; + if (Op.getResNo() == 1 && + (Opc == M680x0ISD::ADD || Opc == M680x0ISD::SUB || + Opc == M680x0ISD::ADDX || Opc == M680x0ISD::SUBX || + Opc == M680x0ISD::SMUL || Opc == M680x0ISD::UMUL || + Opc == M680x0ISD::OR || Opc == M680x0ISD::XOR || Opc == M680x0ISD::AND)) + return true; + + if (Op.getResNo() == 2 && Opc == M680x0ISD::UMUL) + return true; + + return false; +} + +static bool isTruncWithZeroHighBitsInput(SDValue V, SelectionDAG &DAG) { + if (V.getOpcode() != ISD::TRUNCATE) + return false; + + SDValue VOp0 = V.getOperand(0); + unsigned InBits = VOp0.getValueSizeInBits(); + unsigned Bits = V.getValueSizeInBits(); + return DAG.MaskedValueIsZero(VOp0, + APInt::getHighBitsSet(InBits, InBits - Bits)); +} + +SDValue M680x0TargetLowering::LowerSELECT(SDValue Op, SelectionDAG &DAG) const { + bool addTest = true; + SDValue Cond = Op.getOperand(0); + SDValue Op1 = Op.getOperand(1); + SDValue Op2 = Op.getOperand(2); + SDLoc DL(Op); + SDValue CC; + + if (Cond.getOpcode() == ISD::SETCC) { + if (SDValue NewCond = LowerSETCC(Cond, DAG)) + Cond = NewCond; + } + + // (select (x == 0), -1, y) -> (sign_bit (x - 1)) | y + // (select (x == 0), y, -1) -> ~(sign_bit (x - 1)) | y + // (select (x != 0), y, -1) -> (sign_bit (x - 1)) | y + // (select (x != 0), -1, y) -> ~(sign_bit (x - 1)) | y + if (Cond.getOpcode() == M680x0ISD::SETCC && + Cond.getOperand(1).getOpcode() == M680x0ISD::CMP && + isNullConstant(Cond.getOperand(1).getOperand(0))) { + SDValue Cmp = Cond.getOperand(1); + + unsigned CondCode = + cast(Cond.getOperand(0))->getZExtValue(); + + if ((isAllOnesConstant(Op1) || isAllOnesConstant(Op2)) && + (CondCode == M680x0::COND_EQ || CondCode == M680x0::COND_NE)) { + SDValue Y = isAllOnesConstant(Op2) ? Op1 : Op2; + + SDValue CmpOp0 = Cmp.getOperand(1); + // Apply further optimizations for special cases + // (select (x != 0), -1, 0) -> neg & sbb + // (select (x == 0), 0, -1) -> neg & sbb + if (isNullConstant(Y) && + (isAllOnesConstant(Op1) == (CondCode == M680x0::COND_NE))) { + + SDVTList VTs = DAG.getVTList(CmpOp0.getValueType(), MVT::i32); + + SDValue Neg = + DAG.getNode(M680x0ISD::SUB, DL, VTs, + DAG.getConstant(0, DL, CmpOp0.getValueType()), CmpOp0); + + SDValue Res = DAG.getNode(M680x0ISD::SETCC_CARRY, DL, Op.getValueType(), + DAG.getConstant(M680x0::COND_CS, DL, MVT::i8), + SDValue(Neg.getNode(), 1)); + return Res; + } + + Cmp = DAG.getNode(M680x0ISD::CMP, DL, MVT::i8, + DAG.getConstant(1, DL, CmpOp0.getValueType()), CmpOp0); + // Cmp = ConvertCmpIfNecessary(Cmp, DAG); + + SDValue Res = // Res = 0 or -1. + DAG.getNode(M680x0ISD::SETCC_CARRY, DL, Op.getValueType(), + DAG.getConstant(M680x0::COND_CS, DL, MVT::i8), Cmp); + + if (isAllOnesConstant(Op1) != (CondCode == M680x0::COND_EQ)) + Res = DAG.getNOT(DL, Res, Res.getValueType()); + + if (!isNullConstant(Op2)) + Res = DAG.getNode(ISD::OR, DL, Res.getValueType(), Res, Y); + return Res; + } + } + + // Look past (and (setcc_carry (cmp ...)), 1). + if (Cond.getOpcode() == ISD::AND && + Cond.getOperand(0).getOpcode() == M680x0ISD::SETCC_CARRY && + isOneConstant(Cond.getOperand(1))) + Cond = Cond.getOperand(0); + + // If condition flag is set by a M680x0ISD::CMP, then use it as the condition + // setting operand in place of the M680x0ISD::SETCC. + unsigned CondOpcode = Cond.getOpcode(); + if (CondOpcode == M680x0ISD::SETCC || CondOpcode == M680x0ISD::SETCC_CARRY) { + CC = Cond.getOperand(0); + + SDValue Cmp = Cond.getOperand(1); + unsigned Opc = Cmp.getOpcode(); + + bool IllegalFPCMov = false; + + if ((isM680x0LogicalCmp(Cmp) && !IllegalFPCMov) || + Opc == M680x0ISD::BT) { // FIXME + Cond = Cmp; + addTest = false; + } + } else if (CondOpcode == ISD::USUBO || CondOpcode == ISD::SSUBO || + CondOpcode == ISD::UADDO || CondOpcode == ISD::SADDO || + CondOpcode == ISD::UMULO || CondOpcode == ISD::SMULO) { + SDValue LHS = Cond.getOperand(0); + SDValue RHS = Cond.getOperand(1); + unsigned MxOpcode; + unsigned MxCond; + SDVTList VTs; + switch (CondOpcode) { + case ISD::UADDO: + MxOpcode = M680x0ISD::ADD; + MxCond = M680x0::COND_CS; + break; + case ISD::SADDO: + MxOpcode = M680x0ISD::ADD; + MxCond = M680x0::COND_VS; + break; + case ISD::USUBO: + MxOpcode = M680x0ISD::SUB; + MxCond = M680x0::COND_CS; + break; + case ISD::SSUBO: + MxOpcode = M680x0ISD::SUB; + MxCond = M680x0::COND_VS; + break; + case ISD::UMULO: + MxOpcode = M680x0ISD::UMUL; + MxCond = M680x0::COND_VS; + break; + case ISD::SMULO: + MxOpcode = M680x0ISD::SMUL; + MxCond = M680x0::COND_VS; + break; + default: + llvm_unreachable("unexpected overflowing operator"); + } + if (CondOpcode == ISD::UMULO) + VTs = DAG.getVTList(LHS.getValueType(), LHS.getValueType(), MVT::i32); + else + VTs = DAG.getVTList(LHS.getValueType(), MVT::i32); + + SDValue MxOp = DAG.getNode(MxOpcode, DL, VTs, LHS, RHS); + + if (CondOpcode == ISD::UMULO) + Cond = MxOp.getValue(2); + else + Cond = MxOp.getValue(1); + + CC = DAG.getConstant(MxCond, DL, MVT::i8); + addTest = false; + } + + if (addTest) { + // Look past the truncate if the high bits are known zero. + if (isTruncWithZeroHighBitsInput(Cond, DAG)) + Cond = Cond.getOperand(0); + + // We know the result of AND is compared against zero. Try to match + // it to BT. + if (Cond.getOpcode() == ISD::AND && Cond.hasOneUse()) { + if (SDValue NewSetCC = LowerToBT(Cond, ISD::SETNE, DL, DAG)) { + CC = NewSetCC.getOperand(0); + Cond = NewSetCC.getOperand(1); + addTest = false; + } + } + } + + if (addTest) { + CC = DAG.getConstant(M680x0::COND_NE, DL, MVT::i8); + Cond = EmitTest(Cond, M680x0::COND_NE, DL, DAG); + } + + // a < b ? -1 : 0 -> RES = ~setcc_carry + // a < b ? 0 : -1 -> RES = setcc_carry + // a >= b ? -1 : 0 -> RES = setcc_carry + // a >= b ? 0 : -1 -> RES = ~setcc_carry + if (Cond.getOpcode() == M680x0ISD::SUB) { + // Cond = ConvertCmpIfNecessary(Cond, DAG); + unsigned CondCode = cast(CC)->getZExtValue(); + + if ((CondCode == M680x0::COND_CC || CondCode == M680x0::COND_CS) && + (isAllOnesConstant(Op1) || isAllOnesConstant(Op2)) && + (isNullConstant(Op1) || isNullConstant(Op2))) { + SDValue Res = + DAG.getNode(M680x0ISD::SETCC_CARRY, DL, Op.getValueType(), + DAG.getConstant(M680x0::COND_CS, DL, MVT::i8), Cond); + if (isAllOnesConstant(Op1) != (CondCode == M680x0::COND_CS)) + return DAG.getNOT(DL, Res, Res.getValueType()); + return Res; + } + } + + // M680x0 doesn't have an i8 cmov. If both operands are the result of a + // truncate widen the cmov and push the truncate through. This avoids + // introducing a new branch during isel and doesn't add any extensions. + if (Op.getValueType() == MVT::i8 && Op1.getOpcode() == ISD::TRUNCATE && + Op2.getOpcode() == ISD::TRUNCATE) { + SDValue T1 = Op1.getOperand(0), T2 = Op2.getOperand(0); + if (T1.getValueType() == T2.getValueType() && + // Blacklist CopyFromReg to avoid partial register stalls. + T1.getOpcode() != ISD::CopyFromReg && + T2.getOpcode() != ISD::CopyFromReg) { + SDVTList VTs = DAG.getVTList(T1.getValueType(), MVT::Glue); + SDValue Cmov = DAG.getNode(M680x0ISD::CMOV, DL, VTs, T2, T1, CC, Cond); + return DAG.getNode(ISD::TRUNCATE, DL, Op.getValueType(), Cmov); + } + } + + // M680x0ISD::CMOV means set the result (which is operand 1) to the RHS if + // condition is true. + SDVTList VTs = DAG.getVTList(Op.getValueType(), MVT::Glue); + SDValue Ops[] = {Op2, Op1, CC, Cond}; + return DAG.getNode(M680x0ISD::CMOV, DL, VTs, Ops); +} + +/// Return true if node is an ISD::AND or ISD::OR of two M680x0::SETcc nodes +/// each of which has no other use apart from the AND / OR. +static bool isAndOrOfSetCCs(SDValue Op, unsigned &Opc) { + Opc = Op.getOpcode(); + if (Opc != ISD::OR && Opc != ISD::AND) + return false; + return (M680x0::IsSETCC(Op.getOperand(0).getOpcode()) && + Op.getOperand(0).hasOneUse() && + M680x0::IsSETCC(Op.getOperand(1).getOpcode()) && + Op.getOperand(1).hasOneUse()); +} + +/// Return true if node is an ISD::XOR of a M680x0ISD::SETCC and 1 and that the +/// SETCC node has a single use. +static bool isXor1OfSetCC(SDValue Op) { + if (Op.getOpcode() != ISD::XOR) + return false; + if (isOneConstant(Op.getOperand(1))) + return Op.getOperand(0).getOpcode() == M680x0ISD::SETCC && + Op.getOperand(0).hasOneUse(); + return false; +} + +SDValue M680x0TargetLowering::LowerBRCOND(SDValue Op, SelectionDAG &DAG) const { + bool addTest = true; + SDValue Chain = Op.getOperand(0); + SDValue Cond = Op.getOperand(1); + SDValue Dest = Op.getOperand(2); + SDLoc DL(Op); + SDValue CC; + bool Inverted = false; + + if (Cond.getOpcode() == ISD::SETCC) { + // Check for setcc([su]{add,sub,mul}o == 0). + if (cast(Cond.getOperand(2))->get() == ISD::SETEQ && + isNullConstant(Cond.getOperand(1)) && + Cond.getOperand(0).getResNo() == 1 && + (Cond.getOperand(0).getOpcode() == ISD::SADDO || + Cond.getOperand(0).getOpcode() == ISD::UADDO || + Cond.getOperand(0).getOpcode() == ISD::SSUBO || + Cond.getOperand(0).getOpcode() == ISD::USUBO /*|| + Cond.getOperand(0).getOpcode() == ISD::SMULO || + Cond.getOperand(0).getOpcode() == ISD::UMULO)*/)) { + Inverted = true; + Cond = Cond.getOperand(0); + } else { + if (SDValue NewCond = LowerSETCC(Cond, DAG)) + Cond = NewCond; + } + } +#if 0 + // FIXME: LowerXALUO doesn't handle these!! + else if (Cond.getOpcode() == M680x0ISD::ADD || + Cond.getOpcode() == M680x0ISD::SUB || + Cond.getOpcode() == M680x0ISD::SMUL || + Cond.getOpcode() == M680x0ISD::UMUL) + Cond = LowerXALUO(Cond, DAG); +#endif + + // Look pass (and (setcc_carry (cmp ...)), 1). + if (Cond.getOpcode() == ISD::AND && + Cond.getOperand(0).getOpcode() == M680x0ISD::SETCC_CARRY && + isOneConstant(Cond.getOperand(1))) + Cond = Cond.getOperand(0); + + // If condition flag is set by a M680x0ISD::CMP, then use it as the condition + // setting operand in place of the M680x0ISD::SETCC. + unsigned CondOpcode = Cond.getOpcode(); + if (CondOpcode == M680x0ISD::SETCC || CondOpcode == M680x0ISD::SETCC_CARRY) { + CC = Cond.getOperand(0); + + SDValue Cmp = Cond.getOperand(1); + unsigned Opc = Cmp.getOpcode(); + // FIXME: WHY THE SPECIAL CASING OF LogicalCmp?? + if (isM680x0LogicalCmp(Cmp) || Opc == M680x0ISD::BT) { + Cond = Cmp; + addTest = false; + } else { + switch (cast(CC)->getZExtValue()) { + default: + break; + case M680x0::COND_VS: + case M680x0::COND_CS: + // These can only come from an arithmetic instruction with overflow, + // e.g. SADDO, UADDO. + Cond = Cond.getNode()->getOperand(1); + addTest = false; + break; + } + } + } + CondOpcode = Cond.getOpcode(); + if (CondOpcode == ISD::UADDO || CondOpcode == ISD::SADDO || + CondOpcode == ISD::USUBO || CondOpcode == ISD::SSUBO /*|| + CondOpcode == ISD::UMULO || CondOpcode == ISD::SMULO*/) { + SDValue LHS = Cond.getOperand(0); + SDValue RHS = Cond.getOperand(1); + unsigned MxOpcode; + unsigned MxCond; + SDVTList VTs; + // Keep this in sync with LowerXALUO, otherwise we might create redundant + // instructions that can't be removed afterwards (i.e. M680x0ISD::ADD and + // M680x0ISD::INC). + switch (CondOpcode) { + case ISD::UADDO: + MxOpcode = M680x0ISD::ADD; + MxCond = M680x0::COND_CS; + break; + case ISD::SADDO: + MxOpcode = M680x0ISD::ADD; + MxCond = M680x0::COND_VS; + break; + case ISD::USUBO: + MxOpcode = M680x0ISD::SUB; + MxCond = M680x0::COND_CS; + break; + case ISD::SSUBO: + MxOpcode = M680x0ISD::SUB; + MxCond = M680x0::COND_VS; + break; + case ISD::UMULO: + MxOpcode = M680x0ISD::UMUL; + MxCond = M680x0::COND_VS; + break; + case ISD::SMULO: + MxOpcode = M680x0ISD::SMUL; + MxCond = M680x0::COND_VS; + break; + default: + llvm_unreachable("unexpected overflowing operator"); + } + + if (Inverted) + MxCond = M680x0::GetOppositeBranchCondition((M680x0::CondCode)MxCond); + + if (CondOpcode == ISD::UMULO) + VTs = DAG.getVTList(LHS.getValueType(), LHS.getValueType(), MVT::i8); + else + VTs = DAG.getVTList(LHS.getValueType(), MVT::i8); + + SDValue MxOp = DAG.getNode(MxOpcode, DL, VTs, LHS, RHS); + + if (CondOpcode == ISD::UMULO) + Cond = MxOp.getValue(2); + else + Cond = MxOp.getValue(1); + + CC = DAG.getConstant(MxCond, DL, MVT::i8); + addTest = false; + } else { + unsigned CondOpc; + if (Cond.hasOneUse() && isAndOrOfSetCCs(Cond, CondOpc)) { + SDValue Cmp = Cond.getOperand(0).getOperand(1); + if (CondOpc == ISD::OR) { + // Also, recognize the pattern generated by an FCMP_UNE. We can emit + // two branches instead of an explicit OR instruction with a + // separate test. + if (Cmp == Cond.getOperand(1).getOperand(1) && + isM680x0LogicalCmp(Cmp)) { + CC = Cond.getOperand(0).getOperand(0); + Chain = DAG.getNode(M680x0ISD::BRCOND, DL, Op.getValueType(), Chain, + Dest, CC, Cmp); + CC = Cond.getOperand(1).getOperand(0); + Cond = Cmp; + addTest = false; + } + } else { // ISD::AND + // Also, recognize the pattern generated by an FCMP_OEQ. We can emit + // two branches instead of an explicit AND instruction with a + // separate test. However, we only do this if this block doesn't + // have a fall-through edge, because this requires an explicit + // jmp when the condition is false. + if (Cmp == Cond.getOperand(1).getOperand(1) && + isM680x0LogicalCmp(Cmp) && Op.getNode()->hasOneUse()) { + M680x0::CondCode CCode = + (M680x0::CondCode)Cond.getOperand(0).getConstantOperandVal(0); + CCode = M680x0::GetOppositeBranchCondition(CCode); + CC = DAG.getConstant(CCode, DL, MVT::i8); + SDNode *User = *Op.getNode()->use_begin(); + // Look for an unconditional branch following this conditional branch. + // We need this because we need to reverse the successors in order + // to implement FCMP_OEQ. + if (User->getOpcode() == ISD::BR) { + SDValue FalseBB = User->getOperand(1); + SDNode *NewBR = + DAG.UpdateNodeOperands(User, User->getOperand(0), Dest); + assert(NewBR == User); + (void)NewBR; + Dest = FalseBB; + + Chain = DAG.getNode(M680x0ISD::BRCOND, DL, Op.getValueType(), Chain, + Dest, CC, Cmp); + M680x0::CondCode CCode = + (M680x0::CondCode)Cond.getOperand(1).getConstantOperandVal(0); + CCode = M680x0::GetOppositeBranchCondition(CCode); + CC = DAG.getConstant(CCode, DL, MVT::i8); + Cond = Cmp; + addTest = false; + } + } + } + } else if (Cond.hasOneUse() && isXor1OfSetCC(Cond)) { + // Recognize for xorb (setcc), 1 patterns. The xor inverts the condition. + // It should be transformed during dag combiner except when the condition + // is set by a arithmetics with overflow node. + M680x0::CondCode CCode = + (M680x0::CondCode)Cond.getOperand(0).getConstantOperandVal(0); + CCode = M680x0::GetOppositeBranchCondition(CCode); + CC = DAG.getConstant(CCode, DL, MVT::i8); + Cond = Cond.getOperand(0).getOperand(1); + addTest = false; + } /*else if (Cond.getOpcode() == ISD::SETCC && + cast(Cond.getOperand(2))->get() == ISD::SETOEQ) { + // For FCMP_OEQ, we can emit + // two branches instead of an explicit AND instruction with a + // separate test. However, we only do this if this block doesn't + // have a fall-through edge, because this requires an explicit + // jmp when the condition is false. + if (Op.getNode()->hasOneUse()) { + SDNode *User = *Op.getNode()->use_begin(); + // Look for an unconditional branch following this conditional branch. + // We need this because we need to reverse the successors in order + // to implement FCMP_OEQ. + if (User->getOpcode() == ISD::BR) { + SDValue FalseBB = User->getOperand(1); + SDNode *NewBR = + DAG.UpdateNodeOperands(User, User->getOperand(0), Dest); + assert(NewBR == User); + (void)NewBR; + Dest = FalseBB; + + SDValue Cmp = DAG.getNode(M680x0ISD::CMP, DL, MVT::i32, + Cond.getOperand(0), Cond.getOperand(1)); + // Cmp = ConvertCmpIfNecessary(Cmp, DAG); + CC = DAG.getConstant(M680x0::COND_NE, DL, MVT::i8); + Chain = DAG.getNode(M680x0ISD::BRCOND, DL, Op.getValueType(), + Chain, Dest, CC, Cmp); + CC = DAG.getConstant(M680x0::COND_P, DL, MVT::i8); + Cond = Cmp; + addTest = false; + } + } + } else if (Cond.getOpcode() == ISD::SETCC && + cast(Cond.getOperand(2))->get() == ISD::SETUNE) { + // For FCMP_UNE, we can emit + // two branches instead of an explicit AND instruction with a + // separate test. However, we only do this if this block doesn't + // have a fall-through edge, because this requires an explicit + // jmp when the condition is false. + if (Op.getNode()->hasOneUse()) { + SDNode *User = *Op.getNode()->use_begin(); + // Look for an unconditional branch following this conditional branch. + // We need this because we need to reverse the successors in order + // to implement FCMP_UNE. + if (User->getOpcode() == ISD::BR) { + SDValue FalseBB = User->getOperand(1); + SDNode *NewBR = + DAG.UpdateNodeOperands(User, User->getOperand(0), Dest); + assert(NewBR == User); + (void)NewBR; + + SDValue Cmp = DAG.getNode(M680x0ISD::CMP, DL, MVT::i32, + Cond.getOperand(0), Cond.getOperand(1)); + Cmp = ConvertCmpIfNecessary(Cmp, DAG); + CC = DAG.getConstant(M680x0::COND_NE, DL, MVT::i8); + Chain = DAG.getNode(M680x0ISD::BRCOND, DL, Op.getValueType(), + Chain, Dest, CC, Cmp); + CC = DAG.getConstant(M680x0::COND_NP, DL, MVT::i8); + Cond = Cmp; + addTest = false; + Dest = FalseBB; + } + } + } */ + } + + if (addTest) { + // Look pass the truncate if the high bits are known zero. + if (isTruncWithZeroHighBitsInput(Cond, DAG)) + Cond = Cond.getOperand(0); + + // We know the result is compared against zero. Try to match it to BT. + if (Cond.hasOneUse()) { + if (SDValue NewSetCC = LowerToBT(Cond, ISD::SETNE, DL, DAG)) { + CC = NewSetCC.getOperand(0); + Cond = NewSetCC.getOperand(1); + addTest = false; + } + } + } + + if (addTest) { + M680x0::CondCode MxCond = Inverted ? M680x0::COND_EQ : M680x0::COND_NE; + CC = DAG.getConstant(MxCond, DL, MVT::i8); + Cond = EmitTest(Cond, MxCond, DL, DAG); + } + // Cond = ConvertCmpIfNecessary(Cond, DAG); + return DAG.getNode(M680x0ISD::BRCOND, DL, Op.getValueType(), Chain, Dest, CC, + Cond); +} + +SDValue +M680x0TargetLowering::LowerADDC_ADDE_SUBC_SUBE(SDValue Op, + SelectionDAG &DAG) const { + MVT VT = Op.getNode()->getSimpleValueType(0); + + // Let legalize expand this if it isn't a legal type yet. + if (!DAG.getTargetLoweringInfo().isTypeLegal(VT)) + return SDValue(); + + SDVTList VTs = DAG.getVTList(VT, MVT::i8); + + unsigned Opc; + bool ExtraOp = false; + switch (Op.getOpcode()) { + default: + llvm_unreachable("Invalid code"); + case ISD::ADDC: + Opc = M680x0ISD::ADD; + break; + case ISD::ADDE: + Opc = M680x0ISD::ADDX; + ExtraOp = true; + break; + case ISD::SUBC: + Opc = M680x0ISD::SUB; + break; + case ISD::SUBE: + Opc = M680x0ISD::SUBX; + ExtraOp = true; + break; + } + + if (!ExtraOp) + return DAG.getNode(Opc, SDLoc(Op), VTs, Op.getOperand(0), Op.getOperand(1)); + return DAG.getNode(Opc, SDLoc(Op), VTs, Op.getOperand(0), Op.getOperand(1), + Op.getOperand(2)); +} + +// ConstantPool, JumpTable, GlobalAddress, and ExternalSymbol are lowered as +// their target countpart wrapped in the M680x0ISD::Wrapper node. Suppose N is +// one of the above mentioned nodes. It has to be wrapped because otherwise +// Select(N) returns N. So the raw TargetGlobalAddress nodes, etc. can only +// be used to form addressing mode. These wrapped nodes will be selected +// into MOV32ri. +SDValue M680x0TargetLowering::LowerConstantPool(SDValue Op, + SelectionDAG &DAG) const { + ConstantPoolSDNode *CP = cast(Op); + + // In PIC mode (unless we're in PCRel PIC mode) we add an offset to the + // global base reg. + unsigned char OpFlag = Subtarget.classifyLocalReference(nullptr); + + unsigned WrapperKind = M680x0ISD::Wrapper; + if (M680x0II::isPCRelGlobalReference(OpFlag)) { + WrapperKind = M680x0ISD::WrapperPC; + } + + auto PtrVT = getPointerTy(DAG.getDataLayout()); + SDValue Result = DAG.getTargetConstantPool( + CP->getConstVal(), PtrVT, CP->getAlignment(), CP->getOffset(), OpFlag); + + SDLoc DL(CP); + Result = DAG.getNode(WrapperKind, DL, PtrVT, Result); + + // With PIC, the address is actually $g + Offset. + if (M680x0II::isGlobalRelativeToPICBase(OpFlag)) { + Result = DAG.getNode(ISD::ADD, DL, PtrVT, + DAG.getNode(M680x0ISD::GlobalBaseReg, SDLoc(), PtrVT), + Result); + } + + return Result; +} + +SDValue M680x0TargetLowering::LowerExternalSymbol(SDValue Op, + SelectionDAG &DAG) const { + const char *Sym = cast(Op)->getSymbol(); + + // In PIC mode (unless we're in PCRel PIC mode) we add an offset to the + // global base reg. + const Module *Mod = DAG.getMachineFunction().getFunction().getParent(); + unsigned char OpFlag = Subtarget.classifyExternalReference(*Mod); + + unsigned WrapperKind = M680x0ISD::Wrapper; + if (M680x0II::isPCRelGlobalReference(OpFlag)) { + WrapperKind = M680x0ISD::WrapperPC; + } + + auto PtrVT = getPointerTy(DAG.getDataLayout()); + SDValue Result = DAG.getTargetExternalSymbol(Sym, PtrVT, OpFlag); + + SDLoc DL(Op); + Result = DAG.getNode(WrapperKind, DL, PtrVT, Result); + + // With PIC, the address is actually $g + Offset. + if (M680x0II::isGlobalRelativeToPICBase(OpFlag)) { + Result = DAG.getNode(ISD::ADD, DL, PtrVT, + DAG.getNode(M680x0ISD::GlobalBaseReg, SDLoc(), PtrVT), + Result); + } + + // For symbols that require a load from a stub to get the address, emit the + // load. + if (M680x0II::isGlobalStubReference(OpFlag)) { + Result = DAG.getLoad(PtrVT, DL, DAG.getEntryNode(), Result, + MachinePointerInfo::getGOT(DAG.getMachineFunction())); + } + + return Result; +} + +SDValue M680x0TargetLowering::LowerBlockAddress(SDValue Op, + SelectionDAG &DAG) const { + unsigned char OpFlags = Subtarget.classifyBlockAddressReference(); + const BlockAddress *BA = cast(Op)->getBlockAddress(); + int64_t Offset = cast(Op)->getOffset(); + SDLoc DL(Op); + auto PtrVT = getPointerTy(DAG.getDataLayout()); + + // Create the TargetBlockAddressAddress node. + SDValue Result = DAG.getTargetBlockAddress(BA, PtrVT, Offset, OpFlags); + + if (M680x0II::isPCRelBlockReference(OpFlags)) { + Result = DAG.getNode(M680x0ISD::WrapperPC, DL, PtrVT, Result); + } else { + Result = DAG.getNode(M680x0ISD::Wrapper, DL, PtrVT, Result); + } + + // With PIC, the address is actually $g + Offset. + if (M680x0II::isGlobalRelativeToPICBase(OpFlags)) { + Result = + DAG.getNode(ISD::ADD, DL, PtrVT, + DAG.getNode(M680x0ISD::GlobalBaseReg, DL, PtrVT), Result); + } + + return Result; +} + +SDValue M680x0TargetLowering::LowerGlobalAddress(const GlobalValue *GV, + const SDLoc &DL, + int64_t Offset, + SelectionDAG &DAG) const { + unsigned char OpFlags = Subtarget.classifyGlobalReference(GV); + auto PtrVT = getPointerTy(DAG.getDataLayout()); + + // Create the TargetGlobalAddress node, folding in the constant + // offset if it is legal. + SDValue Result; + if (M680x0II::isDirectGlobalReference(OpFlags)) { + Result = DAG.getTargetGlobalAddress(GV, DL, PtrVT, Offset); + Offset = 0; + } else { + Result = DAG.getTargetGlobalAddress(GV, DL, PtrVT, 0, OpFlags); + } + + if (M680x0II::isPCRelGlobalReference(OpFlags)) + Result = DAG.getNode(M680x0ISD::WrapperPC, DL, PtrVT, Result); + else + Result = DAG.getNode(M680x0ISD::Wrapper, DL, PtrVT, Result); + + // With PIC, the address is actually $g + Offset. + if (M680x0II::isGlobalRelativeToPICBase(OpFlags)) { + Result = + DAG.getNode(ISD::ADD, DL, PtrVT, + DAG.getNode(M680x0ISD::GlobalBaseReg, DL, PtrVT), Result); + } + + // For globals that require a load from a stub to get the address, emit the + // load. + if (M680x0II::isGlobalStubReference(OpFlags)) { + Result = DAG.getLoad(PtrVT, DL, DAG.getEntryNode(), Result, + MachinePointerInfo::getGOT(DAG.getMachineFunction())); + } + + // If there was a non-zero offset that we didn't fold, create an explicit + // addition for it. + if (Offset != 0) { + Result = DAG.getNode(ISD::ADD, DL, PtrVT, Result, + DAG.getConstant(Offset, DL, PtrVT)); + } + + return Result; +} + +SDValue M680x0TargetLowering::LowerGlobalAddress(SDValue Op, + SelectionDAG &DAG) const { + const GlobalValue *GV = cast(Op)->getGlobal(); + int64_t Offset = cast(Op)->getOffset(); + return LowerGlobalAddress(GV, SDLoc(Op), Offset, DAG); +} + +//===----------------------------------------------------------------------===// +// Custom Lower Jump Table +//===----------------------------------------------------------------------===// + +SDValue M680x0TargetLowering::LowerJumpTable(SDValue Op, + SelectionDAG &DAG) const { + JumpTableSDNode *JT = cast(Op); + + // In PIC mode (unless we're in PCRel PIC mode) we add an offset to the + // global base reg. + unsigned char OpFlag = Subtarget.classifyLocalReference(nullptr); + + unsigned WrapperKind = M680x0ISD::Wrapper; + if (M680x0II::isPCRelGlobalReference(OpFlag)) { + WrapperKind = M680x0ISD::WrapperPC; + } + + auto PtrVT = getPointerTy(DAG.getDataLayout()); + SDValue Result = DAG.getTargetJumpTable(JT->getIndex(), PtrVT, OpFlag); + SDLoc DL(JT); + Result = DAG.getNode(WrapperKind, DL, PtrVT, Result); + + // With PIC, the address is actually $g + Offset. + if (M680x0II::isGlobalRelativeToPICBase(OpFlag)) { + Result = DAG.getNode(ISD::ADD, DL, PtrVT, + DAG.getNode(M680x0ISD::GlobalBaseReg, SDLoc(), PtrVT), + Result); + } + + return Result; +} + +unsigned M680x0TargetLowering::getJumpTableEncoding() const { + return Subtarget.getJumpTableEncoding(); +} + +const MCExpr *M680x0TargetLowering::LowerCustomJumpTableEntry( + const MachineJumpTableInfo *MJTI, const MachineBasicBlock *MBB, + unsigned uid, MCContext &Ctx) const { + return MCSymbolRefExpr::create(MBB->getSymbol(), MCSymbolRefExpr::VK_GOTOFF, + Ctx); +} + +SDValue +M680x0TargetLowering::getPICJumpTableRelocBase(SDValue Table, + SelectionDAG &DAG) const { + if (getJumpTableEncoding() == MachineJumpTableInfo::EK_Custom32) + return DAG.getNode(M680x0ISD::GlobalBaseReg, SDLoc(), + getPointerTy(DAG.getDataLayout())); + + // MachineJumpTableInfo::EK_LabelDifference32 entry + return Table; +} + +// NOTE This only used for MachineJumpTableInfo::EK_LabelDifference32 entries +const MCExpr *M680x0TargetLowering::getPICJumpTableRelocBaseExpr( + const MachineFunction *MF, unsigned JTI, MCContext &Ctx) const { + return MCSymbolRefExpr::create(MF->getJTISymbol(JTI, Ctx), Ctx); +} + +/// Determines whether the callee is required to pop its own arguments. +/// Callee pop is necessary to support tail calls. +bool M680x0::isCalleePop(CallingConv::ID CallingConv, bool IsVarArg, + bool GuaranteeTCO) { + // FIXME RTD is not available untill M68010 + return false; + // // If GuaranteeTCO is true, we force some calls to be callee pop so that we + // // can guarantee TCO. + // if (!IsVarArg && shouldGuaranteeTCO(CallingConv, GuaranteeTCO)) + // return true; + // + // switch (CallingConv) { + // default: + // return false; + // case CallingConv::M680x0_StdCall: + // case CallingConv::M680x0_FastCall: + // case CallingConv::M680x0_ThisCall: + // case CallingConv::M680x0_VectorCall: + // return !is64Bit; + // } +} + +// Return true if it is OK for this CMOV pseudo-opcode to be cascaded +// together with other CMOV pseudo-opcodes into a single basic-block with +// conditional jump around it. +static bool isCMOVPseudo(MachineInstr &MI) { + switch (MI.getOpcode()) { + case M680x0::CMOV8d: + case M680x0::CMOV16d: + case M680x0::CMOV32r: + return true; + + default: + return false; + } +} + +// The CCR operand of SelectItr might be missing a kill marker +// because there were multiple uses of CCR, and ISel didn't know +// which to mark. Figure out whether SelectItr should have had a +// kill marker, and set it if it should. Returns the correct kill +// marker value. +static bool checkAndUpdateCCRKill(MachineBasicBlock::iterator SelectItr, + MachineBasicBlock *BB, + const TargetRegisterInfo *TRI) { + // Scan forward through BB for a use/def of CCR. + MachineBasicBlock::iterator miI(std::next(SelectItr)); + for (MachineBasicBlock::iterator miE = BB->end(); miI != miE; ++miI) { + const MachineInstr &mi = *miI; + if (mi.readsRegister(M680x0::CCR)) + return false; + if (mi.definesRegister(M680x0::CCR)) + break; // Should have kill-flag - update below. + } + + // If we hit the end of the block, check whether CCR is live into a + // successor. + if (miI == BB->end()) { + for (MachineBasicBlock::succ_iterator sItr = BB->succ_begin(), + sEnd = BB->succ_end(); + sItr != sEnd; ++sItr) { + MachineBasicBlock *succ = *sItr; + if (succ->isLiveIn(M680x0::CCR)) + return false; + } + } + + // We found a def, or hit the end of the basic block and CCR wasn't live + // out. SelectMI should have a kill flag on CCR. + SelectItr->addRegisterKilled(M680x0::CCR, TRI); + return true; +} + +MachineBasicBlock * +M680x0TargetLowering::EmitLoweredSelect(MachineInstr &MI, + MachineBasicBlock *BB) const { + const TargetInstrInfo *TII = Subtarget.getInstrInfo(); + DebugLoc DL = MI.getDebugLoc(); + + // To "insert" a SELECT_CC instruction, we actually have to insert the + // diamond control-flow pattern. The incoming instruction knows the + // destination vreg to set, the condition code register to branch on, the + // true/false values to select between, and a branch opcode to use. + const BasicBlock *LLVM_BB = BB->getBasicBlock(); + MachineFunction::iterator It = ++BB->getIterator(); + + // thisMBB: + // ... + // TrueVal = ... + // cmpTY ccX, r1, r2 + // bCC copy1MBB + // fallthrough --> copy0MBB + MachineBasicBlock *thisMBB = BB; + MachineFunction *F = BB->getParent(); + + // This code lowers all pseudo-CMOV instructions. Generally it lowers these + // as described above, by inserting a BB, and then making a PHI at the join + // point to select the true and false operands of the CMOV in the PHI. + // + // The code also handles two different cases of multiple CMOV opcodes + // in a row. + // + // Case 1: + // In this case, there are multiple CMOVs in a row, all which are based on + // the same condition setting (or the exact opposite condition setting). + // In this case we can lower all the CMOVs using a single inserted BB, and + // then make a number of PHIs at the join point to model the CMOVs. The only + // trickiness here, is that in a case like: + // + // t2 = CMOV cond1 t1, f1 + // t3 = CMOV cond1 t2, f2 + // + // when rewriting this into PHIs, we have to perform some renaming on the + // temps since you cannot have a PHI operand refer to a PHI result earlier + // in the same block. The "simple" but wrong lowering would be: + // + // t2 = PHI t1(BB1), f1(BB2) + // t3 = PHI t2(BB1), f2(BB2) + // + // but clearly t2 is not defined in BB1, so that is incorrect. The proper + // renaming is to note that on the path through BB1, t2 is really just a + // copy of t1, and do that renaming, properly generating: + // + // t2 = PHI t1(BB1), f1(BB2) + // t3 = PHI t1(BB1), f2(BB2) + // + // Case 2, we lower cascaded CMOVs such as + // + // (CMOV (CMOV F, T, cc1), T, cc2) + // + // to two successives branches. For that, we look for another CMOV as the + // following instruction. + // + // Without this, we would add a PHI between the two jumps, which ends up + // creating a few copies all around. For instance, for + // + // (sitofp (zext (fcmp une))) + // + // we would generate: + // + // ucomiss %xmm1, %xmm0 + // movss <1.0f>, %xmm0 + // movaps %xmm0, %xmm1 + // jne .LBB5_2 + // xorps %xmm1, %xmm1 + // .LBB5_2: + // jp .LBB5_4 + // movaps %xmm1, %xmm0 + // .LBB5_4: + // retq + // + // because this custom-inserter would have generated: + // + // A + // | \ + // | B + // | / + // C + // | \ + // | D + // | / + // E + // + // A: X = ...; Y = ... + // B: empty + // C: Z = PHI [X, A], [Y, B] + // D: empty + // E: PHI [X, C], [Z, D] + // + // If we lower both CMOVs in a single step, we can instead generate: + // + // A + // | \ + // | C + // | /| + // |/ | + // | | + // | D + // | / + // E + // + // A: X = ...; Y = ... + // D: empty + // E: PHI [X, A], [X, C], [Y, D] + // + // Which, in our sitofp/fcmp example, gives us something like: + // + // ucomiss %xmm1, %xmm0 + // movss <1.0f>, %xmm0 + // jne .LBB5_4 + // jp .LBB5_4 + // xorps %xmm0, %xmm0 + // .LBB5_4: + // retq + // + MachineInstr *CascadedCMOV = nullptr; + MachineInstr *LastCMOV = &MI; + M680x0::CondCode CC = M680x0::CondCode(MI.getOperand(3).getImm()); + M680x0::CondCode OppCC = M680x0::GetOppositeBranchCondition(CC); + MachineBasicBlock::iterator NextMIIt = + std::next(MachineBasicBlock::iterator(MI)); + + // Check for case 1, where there are multiple CMOVs with the same condition + // first. Of the two cases of multiple CMOV lowerings, case 1 reduces the + // number of jumps the most. + + if (isCMOVPseudo(MI)) { + // See if we have a string of CMOVS with the same condition. + while (NextMIIt != BB->end() && isCMOVPseudo(*NextMIIt) && + (NextMIIt->getOperand(3).getImm() == CC || + NextMIIt->getOperand(3).getImm() == OppCC)) { + LastCMOV = &*NextMIIt; + ++NextMIIt; + } + } + + // This checks for case 2, but only do this if we didn't already find + // case 1, as indicated by LastCMOV == MI. + if (LastCMOV == &MI && NextMIIt != BB->end() && + NextMIIt->getOpcode() == MI.getOpcode() && + NextMIIt->getOperand(2).getReg() == MI.getOperand(2).getReg() && + NextMIIt->getOperand(1).getReg() == MI.getOperand(0).getReg() && + NextMIIt->getOperand(1).isKill()) { + CascadedCMOV = &*NextMIIt; + } + + MachineBasicBlock *jcc1MBB = nullptr; + + // If we have a cascaded CMOV, we lower it to two successive branches to + // the same block. CCR is used by both, so mark it as live in the second. + if (CascadedCMOV) { + jcc1MBB = F->CreateMachineBasicBlock(LLVM_BB); + F->insert(It, jcc1MBB); + jcc1MBB->addLiveIn(M680x0::CCR); + } + + MachineBasicBlock *copy0MBB = F->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *sinkMBB = F->CreateMachineBasicBlock(LLVM_BB); + F->insert(It, copy0MBB); + F->insert(It, sinkMBB); + + // If the CCR register isn't dead in the terminator, then claim that it's + // live into the sink and copy blocks. + const TargetRegisterInfo *TRI = Subtarget.getRegisterInfo(); + + MachineInstr *LastCCRSUser = CascadedCMOV ? CascadedCMOV : LastCMOV; + if (!LastCCRSUser->killsRegister(M680x0::CCR) && + !checkAndUpdateCCRKill(LastCCRSUser, BB, TRI)) { + copy0MBB->addLiveIn(M680x0::CCR); + sinkMBB->addLiveIn(M680x0::CCR); + } + + // Transfer the remainder of BB and its successor edges to sinkMBB. + sinkMBB->splice(sinkMBB->begin(), BB, + std::next(MachineBasicBlock::iterator(LastCMOV)), BB->end()); + sinkMBB->transferSuccessorsAndUpdatePHIs(BB); + + // Add the true and fallthrough blocks as its successors. + if (CascadedCMOV) { + // The fallthrough block may be jcc1MBB, if we have a cascaded CMOV. + BB->addSuccessor(jcc1MBB); + + // In that case, jcc1MBB will itself fallthrough the copy0MBB, and + // jump to the sinkMBB. + jcc1MBB->addSuccessor(copy0MBB); + jcc1MBB->addSuccessor(sinkMBB); + } else { + BB->addSuccessor(copy0MBB); + } + + // The true block target of the first (or only) branch is always sinkMBB. + BB->addSuccessor(sinkMBB); + + // Create the conditional branch instruction. + unsigned Opc = M680x0::GetCondBranchFromCond(CC); + BuildMI(BB, DL, TII->get(Opc)).addMBB(sinkMBB); + + if (CascadedCMOV) { + unsigned Opc2 = M680x0::GetCondBranchFromCond( + (M680x0::CondCode)CascadedCMOV->getOperand(3).getImm()); + BuildMI(jcc1MBB, DL, TII->get(Opc2)).addMBB(sinkMBB); + } + + // copy0MBB: + // %FalseValue = ... + // # fallthrough to sinkMBB + copy0MBB->addSuccessor(sinkMBB); + + // sinkMBB: + // %Result = phi [ %FalseValue, copy0MBB ], [ %TrueValue, thisMBB ] + // ... + MachineBasicBlock::iterator MIItBegin = MachineBasicBlock::iterator(MI); + MachineBasicBlock::iterator MIItEnd = + std::next(MachineBasicBlock::iterator(LastCMOV)); + MachineBasicBlock::iterator SinkInsertionPoint = sinkMBB->begin(); + DenseMap> RegRewriteTable; + MachineInstrBuilder MIB; + + // As we are creating the PHIs, we have to be careful if there is more than + // one. Later CMOVs may reference the results of earlier CMOVs, but later + // PHIs have to reference the individual true/false inputs from earlier PHIs. + // That also means that PHI construction must work forward from earlier to + // later, and that the code must maintain a mapping from earlier PHI's + // destination registers, and the registers that went into the PHI. + + for (MachineBasicBlock::iterator MIIt = MIItBegin; MIIt != MIItEnd; ++MIIt) { + unsigned DestReg = MIIt->getOperand(0).getReg(); + unsigned Op1Reg = MIIt->getOperand(1).getReg(); + unsigned Op2Reg = MIIt->getOperand(2).getReg(); + + // If this CMOV we are generating is the opposite condition from + // the jump we generated, then we have to swap the operands for the + // PHI that is going to be generated. + if (MIIt->getOperand(3).getImm() == OppCC) + std::swap(Op1Reg, Op2Reg); + + if (RegRewriteTable.find(Op1Reg) != RegRewriteTable.end()) + Op1Reg = RegRewriteTable[Op1Reg].first; + + if (RegRewriteTable.find(Op2Reg) != RegRewriteTable.end()) + Op2Reg = RegRewriteTable[Op2Reg].second; + + MIB = BuildMI(*sinkMBB, SinkInsertionPoint, DL, TII->get(M680x0::PHI), + DestReg) + .addReg(Op1Reg) + .addMBB(copy0MBB) + .addReg(Op2Reg) + .addMBB(thisMBB); + + // Add this PHI to the rewrite table. + RegRewriteTable[DestReg] = std::make_pair(Op1Reg, Op2Reg); + } + + // If we have a cascaded CMOV, the second Jcc provides the same incoming + // value as the first Jcc (the True operand of the SELECT_CC/CMOV nodes). + if (CascadedCMOV) { + MIB.addReg(MI.getOperand(2).getReg()).addMBB(jcc1MBB); + // Copy the PHI result to the register defined by the second CMOV. + BuildMI(*sinkMBB, std::next(MachineBasicBlock::iterator(MIB.getInstr())), + DL, TII->get(TargetOpcode::COPY), + CascadedCMOV->getOperand(0).getReg()) + .addReg(MI.getOperand(0).getReg()); + CascadedCMOV->eraseFromParent(); + } + + // Now remove the CMOV(s). + for (MachineBasicBlock::iterator MIIt = MIItBegin; MIIt != MIItEnd;) + (MIIt++)->eraseFromParent(); + + return sinkMBB; +} + +MachineBasicBlock * +M680x0TargetLowering::EmitLoweredSegAlloca(MachineInstr &MI, + MachineBasicBlock *BB) const { + // FIXME See Target TODO.md + llvm_unreachable("Cannot lower Segmented Stack Alloca with stack-split on"); +} + +MachineBasicBlock * +M680x0TargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI, + MachineBasicBlock *BB) const { + switch (MI.getOpcode()) { + default: + llvm_unreachable("Unexpected instr type to insert"); + case M680x0::CMOV8d: + case M680x0::CMOV16d: + case M680x0::CMOV32r: + return EmitLoweredSelect(MI, BB); + case M680x0::SALLOCA: + return EmitLoweredSegAlloca(MI, BB); + } +} + +SDValue M680x0TargetLowering::LowerVASTART(SDValue Op, + SelectionDAG &DAG) const { + MachineFunction &MF = DAG.getMachineFunction(); + auto PtrVT = getPointerTy(MF.getDataLayout()); + M680x0MachineFunctionInfo *FuncInfo = MF.getInfo(); + + const Value *SV = cast(Op.getOperand(2))->getValue(); + SDLoc DL(Op); + + // vastart just stores the address of the VarArgsFrameIndex slot into the + // memory location argument. + SDValue FR = DAG.getFrameIndex(FuncInfo->getVarArgsFrameIndex(), PtrVT); + return DAG.getStore(Op.getOperand(0), DL, FR, Op.getOperand(1), + MachinePointerInfo(SV)); +} + +// Lower dynamic stack allocation to _alloca call for Cygwin/Mingw targets. +// Calls to _alloca are needed to probe the stack when allocating more than 4k +// bytes in one go. Touching the stack at 4K increments is necessary to ensure +// that the guard pages used by the OS virtual memory manager are allocated in +// correct sequence. +SDValue M680x0TargetLowering::LowerDYNAMIC_STACKALLOC(SDValue Op, + SelectionDAG &DAG) const { + MachineFunction &MF = DAG.getMachineFunction(); + bool SplitStack = MF.shouldSplitStack(); + + SDLoc DL(Op); + + // Get the inputs. + SDNode *Node = Op.getNode(); + SDValue Chain = Op.getOperand(0); + SDValue Size = Op.getOperand(1); + unsigned Align = cast(Op.getOperand(2))->getZExtValue(); + EVT VT = Node->getValueType(0); + + // Chain the dynamic stack allocation so that it doesn't modify the stack + // pointer when other instructions are using the stack. + Chain = DAG.getCALLSEQ_START(Chain, 0, 0, DL); + + SDValue Result; + if (SplitStack) { + auto &MRI = MF.getRegInfo(); + auto SPTy = getPointerTy(DAG.getDataLayout()); + auto *ARClass = getRegClassFor(SPTy); + unsigned Vreg = MRI.createVirtualRegister(ARClass); + Chain = DAG.getCopyToReg(Chain, DL, Vreg, Size); + Result = DAG.getNode(M680x0ISD::SEG_ALLOCA, DL, SPTy, Chain, + DAG.getRegister(Vreg, SPTy)); + } else { + auto &TLI = DAG.getTargetLoweringInfo(); + unsigned SPReg = TLI.getStackPointerRegisterToSaveRestore(); + assert(SPReg && "Target cannot require DYNAMIC_STACKALLOC expansion and" + " not tell us which reg is the stack pointer!"); + + SDValue SP = DAG.getCopyFromReg(Chain, DL, SPReg, VT); + Chain = SP.getValue(1); + const TargetFrameLowering &TFI = *Subtarget.getFrameLowering(); + unsigned StackAlign = TFI.getStackAlignment(); + Result = DAG.getNode(ISD::SUB, DL, VT, SP, Size); // Value + if (Align > StackAlign) + Result = DAG.getNode(ISD::AND, DL, VT, Result, + DAG.getConstant(-(uint64_t)Align, DL, VT)); + Chain = DAG.getCopyToReg(Chain, DL, SPReg, Result); // Output chain + } + + Chain = DAG.getCALLSEQ_END(Chain, DAG.getIntPtrConstant(0, DL, true), + DAG.getIntPtrConstant(0, DL, true), SDValue(), DL); + + SDValue Ops[2] = {Result, Chain}; + return DAG.getMergeValues(Ops, DL); +} + +//===----------------------------------------------------------------------===// +// DAG Combine +//===----------------------------------------------------------------------===// + +static SDValue getSETCC(M680x0::CondCode Cond, SDValue CCR, const SDLoc &dl, + SelectionDAG &DAG) { + return DAG.getNode(M680x0ISD::SETCC, dl, MVT::i8, + DAG.getConstant(Cond, dl, MVT::i8), CCR); +} +// When legalizing carry, we create carries via add X, -1 +// If that comes from an actual carry, via setcc, we use the +// carry directly. +static SDValue combineCarryThroughADD(SDValue CCR) { + if (CCR.getOpcode() == M680x0ISD::ADD) { + if (isAllOnesConstant(CCR.getOperand(1))) { + SDValue Carry = CCR.getOperand(0); + while ( + Carry.getOpcode() == ISD::TRUNCATE || + Carry.getOpcode() == ISD::ZERO_EXTEND || + Carry.getOpcode() == ISD::SIGN_EXTEND || + Carry.getOpcode() == ISD::ANY_EXTEND || + (Carry.getOpcode() == ISD::AND && isOneConstant(Carry.getOperand(1)))) + Carry = Carry.getOperand(0); + if (Carry.getOpcode() == M680x0ISD::SETCC || + Carry.getOpcode() == M680x0ISD::SETCC_CARRY) { + if (Carry.getConstantOperandVal(0) == M680x0::COND_CS) + return Carry.getOperand(1); + } + } + } + + return SDValue(); +} + +// Check whether a boolean test is testing a boolean value generated by +// M680x0ISD::SETCC. If so, return the operand of that SETCC and proper +// condition code. +// +// Simplify the following patterns: +// (Op (CMP (SETCC Cond CCR) 1) EQ) or +// (Op (CMP (SETCC Cond CCR) 0) NEQ) +// to (Op CCR Cond) +// +// (Op (CMP (SETCC Cond CCR) 0) EQ) or +// (Op (CMP (SETCC Cond CCR) 1) NEQ) +// to (Op CCR !Cond) +// +// where Op could be BRCOND or CMOV. +// +static SDValue checkBoolTestSetCCCombine(SDValue Cmp, M680x0::CondCode &CC) { + // FIXME Read through, make sure it fits m68k + // // This combine only operates on CMP-like nodes. + // if (!(Cmp.getOpcode() == M680x0ISD::CMP || + // (Cmp.getOpcode() == M680x0ISD::SUB && !Cmp->hasAnyUseOfValue(0)))) + // return SDValue(); + // + // // Quit if not used as a boolean value. + // if (CC != M680x0::COND_EQ && CC != M680x0::COND_NE) + // return SDValue(); + // + // // Check CMP operands. One of them should be 0 or 1 and the other should be + // // an SetCC or extended from it. + // SDValue Op1 = Cmp.getOperand(0); + // SDValue Op2 = Cmp.getOperand(1); + // + // SDValue SetCC; + // const ConstantSDNode *C = nullptr; + // bool needOppositeCond = (CC == M680x0::COND_EQ); + // bool checkAgainstTrue = false; // Is it a comparison against 1? + // + // if ((C = dyn_cast(Op1))) + // SetCC = Op2; + // else if ((C = dyn_cast(Op2))) + // SetCC = Op1; + // else // Quit if all operands are not constants. + // return SDValue(); + // + // if (C->getZExtValue() == 1) { + // needOppositeCond = !needOppositeCond; + // checkAgainstTrue = true; + // } else if (C->getZExtValue() != 0) + // // Quit if the constant is neither 0 or 1. + // return SDValue(); + // + // bool truncatedToBoolWithAnd = false; + // // Skip (zext $x), (trunc $x), or (and $x, 1) node. + // while (SetCC.getOpcode() == ISD::ZERO_EXTEND || + // SetCC.getOpcode() == ISD::TRUNCATE || SetCC.getOpcode() == ISD::AND) + // { + // if (SetCC.getOpcode() == ISD::AND) { + // int OpIdx = -1; + // if (isOneConstant(SetCC.getOperand(0))) + // OpIdx = 1; + // if (isOneConstant(SetCC.getOperand(1))) + // OpIdx = 0; + // if (OpIdx < 0) + // break; + // SetCC = SetCC.getOperand(OpIdx); + // truncatedToBoolWithAnd = true; + // } else + // SetCC = SetCC.getOperand(0); + // } + // + // switch (SetCC.getOpcode()) { + // case M680x0ISD::SETCC_CARRY: + // // Since SETCC_CARRY gives output based on R = CF ? ~0 : 0, it's unsafe + // to + // // simplify it if the result of SETCC_CARRY is not canonicalized to 0 or + // 1, + // // i.e. it's a comparison against true but the result of SETCC_CARRY is + // not + // // truncated to i1 using 'and'. + // if (checkAgainstTrue && !truncatedToBoolWithAnd) + // break; + // assert(M680x0::CondCode(SetCC.getConstantOperandVal(0)) == + // M680x0::COND_CS && + // "Invalid use of SETCC_CARRY!"); + // LLVM_FALLTHROUGH; + // case M680x0ISD::SETCC: + // // Set the condition code or opposite one if necessary. + // CC = M680x0::CondCode(SetCC.getConstantOperandVal(0)); + // if (needOppositeCond) + // CC = M680x0::GetOppositeBranchCondition(CC); + // return SetCC.getOperand(1); + // case M680x0ISD::CMOV: { + // // Check whether false/true value has canonical one, i.e. 0 or 1. + // ConstantSDNode *FVal = dyn_cast(SetCC.getOperand(0)); + // ConstantSDNode *TVal = dyn_cast(SetCC.getOperand(1)); + // // Quit if true value is not a constant. + // if (!TVal) + // return SDValue(); + // // Quit if false value is not a constant. + // if (!FVal) { + // SDValue Op = SetCC.getOperand(0); + // // Skip 'zext' or 'trunc' node. + // if (Op.getOpcode() == ISD::ZERO_EXTEND || Op.getOpcode() == + // ISD::TRUNCATE) + // Op = Op.getOperand(0); + // // A special case for rdrand/rdseed, where 0 is set if false cond is + // // found. + // if ((Op.getOpcode() != M680x0ISD::RDRAND && + // Op.getOpcode() != M680x0ISD::RDSEED) || + // Op.getResNo() != 0) + // return SDValue(); + // } + // // Quit if false value is not the constant 0 or 1. + // bool FValIsFalse = true; + // if (FVal && FVal->getZExtValue() != 0) { + // if (FVal->getZExtValue() != 1) + // return SDValue(); + // // If FVal is 1, opposite cond is needed. + // needOppositeCond = !needOppositeCond; + // FValIsFalse = false; + // } + // // Quit if TVal is not the constant opposite of FVal. + // if (FValIsFalse && TVal->getZExtValue() != 1) + // return SDValue(); + // if (!FValIsFalse && TVal->getZExtValue() != 0) + // return SDValue(); + // CC = M680x0::CondCode(SetCC.getConstantOperandVal(2)); + // if (needOppositeCond) + // CC = M680x0::GetOppositeBranchCondition(CC); + // return SetCC.getOperand(3); + // } + // } + + return SDValue(); +} + +/// Optimize a CCR definition used according to the condition code \p CC into +/// a simpler CCR value, potentially returning a new \p CC and replacing uses +/// of chain values. +static SDValue combineSetCCCCR(SDValue CCR, M680x0::CondCode &CC, + SelectionDAG &DAG, + const M680x0Subtarget &Subtarget) { + if (CC == M680x0::COND_CS) + if (SDValue Flags = combineCarryThroughADD(CCR)) + return Flags; + + if (SDValue R = checkBoolTestSetCCCombine(CCR, CC)) + return R; + return SDValue(); +} + +// Optimize RES = M680x0ISD::SETCC CONDCODE, CCR_INPUT +static SDValue combineM680x0SetCC(SDNode *N, SelectionDAG &DAG, + const M680x0Subtarget &Subtarget) { + SDLoc DL(N); + M680x0::CondCode CC = M680x0::CondCode(N->getConstantOperandVal(0)); + SDValue CCR = N->getOperand(1); + + // Try to simplify the CCR and condition code operands. + if (SDValue Flags = combineSetCCCCR(CCR, CC, DAG, Subtarget)) + return getSETCC(CC, Flags, DL, DAG); + + return SDValue(); +} +static SDValue combineM680x0BrCond(SDNode *N, SelectionDAG &DAG, + const M680x0Subtarget &Subtarget) { + SDLoc DL(N); + M680x0::CondCode CC = M680x0::CondCode(N->getConstantOperandVal(2)); + SDValue CCR = N->getOperand(3); + + // Try to simplify the CCR and condition code operands. + // Make sure to not keep references to operands, as combineSetCCCCR can + // RAUW them under us. + if (SDValue Flags = combineSetCCCCR(CCR, CC, DAG, Subtarget)) { + SDValue Cond = DAG.getConstant(CC, DL, MVT::i8); + return DAG.getNode(M680x0ISD::BRCOND, DL, N->getVTList(), N->getOperand(0), + N->getOperand(1), Cond, Flags); + } + + return SDValue(); +} + +static SDValue combineSUBX(SDNode *N, SelectionDAG &DAG) { + if (SDValue Flags = combineCarryThroughADD(N->getOperand(2))) { + MVT VT = N->getSimpleValueType(0); + SDVTList VTs = DAG.getVTList(VT, MVT::i32); + return DAG.getNode(M680x0ISD::SUBX, SDLoc(N), VTs, N->getOperand(0), + N->getOperand(1), Flags); + } + + return SDValue(); +} + +/// Returns true if Elt is a constant zero or a floating point constant +0.0. +// static bool isZeroNode(SDValue Elt) { +// return isNullConstant(Elt) || isNullFPConstant(Elt); +// } + +// Optimize RES, CCR = M680x0ISD::ADDX LHS, RHS, CCR +static SDValue combineADDX(SDNode *N, SelectionDAG &DAG, + TargetLowering::DAGCombinerInfo &DCI) { + // FIXME Read through, make sure it fits m68k + // // If the LHS and RHS of the ADDX node are zero, then it can't overflow and + // // the result is either zero or one (depending on the input carry bit). + // // Strength reduce this down to a "set on carry" aka SETCC_CARRY&1. + // if (isZeroNode(N->getOperand(0)) && isZeroNode(N->getOperand(1)) && + // // We don't have a good way to replace an CCR use, so only do this when + // // dead right now. + // SDValue(N, 1).use_empty()) { + // SDLoc DL(N); + // EVT VT = N->getValueType(0); + // SDValue CarryOut = DAG.getConstant(0, DL, N->getValueType(1)); + // SDValue Res1 = + // DAG.getNode(ISD::AND, DL, VT, + // DAG.getNode(M680x0ISD::SETCC_CARRY, DL, VT, + // DAG.getConstant(M680x0::COND_CS, DL, + // MVT::i8), N->getOperand(2)), + // DAG.getConstant(1, DL, VT)); + // return DCI.CombineTo(N, Res1, CarryOut); + // } + + if (SDValue Flags = combineCarryThroughADD(N->getOperand(2))) { + MVT VT = N->getSimpleValueType(0); + SDVTList VTs = DAG.getVTList(VT, MVT::i32); + return DAG.getNode(M680x0ISD::ADDX, SDLoc(N), VTs, N->getOperand(0), + N->getOperand(1), Flags); + } + + return SDValue(); +} + +SDValue M680x0TargetLowering::PerformDAGCombine(SDNode *N, + DAGCombinerInfo &DCI) const { + SelectionDAG &DAG = DCI.DAG; + switch (N->getOpcode()) { + case M680x0ISD::SUBX: + return combineSUBX(N, DAG); + case M680x0ISD::ADDX: + return combineADDX(N, DAG, DCI); + case M680x0ISD::SETCC: + return combineM680x0SetCC(N, DAG, Subtarget); + case M680x0ISD::BRCOND: + return combineM680x0BrCond(N, DAG, Subtarget); + } + + return SDValue(); +} + +//===----------------------------------------------------------------------===// +// M680x0ISD Node Names +//===----------------------------------------------------------------------===// +const char *M680x0TargetLowering::getTargetNodeName(unsigned Opcode) const { + switch (Opcode) { + case M680x0ISD::CALL: + return "M680x0ISD::CALL"; + case M680x0ISD::TAIL_CALL: + return "M680x0ISD::TAIL_CALL"; + case M680x0ISD::RET: + return "M680x0ISD::RET"; + case M680x0ISD::TC_RETURN: + return "M680x0ISD::TC_RETURN"; + case M680x0ISD::ADD: + return "M680x0ISD::ADD"; + case M680x0ISD::SUB: + return "M680x0ISD::SUB"; + case M680x0ISD::ADDX: + return "M680x0ISD::ADDX"; + case M680x0ISD::SUBX: + return "M680x0ISD::SUBX"; + case M680x0ISD::SMUL: + return "M680x0ISD::SMUL"; + case M680x0ISD::UMUL: + return "M680x0ISD::UMUL"; + case M680x0ISD::OR: + return "M680x0ISD::OR"; + case M680x0ISD::XOR: + return "M680x0ISD::XOR"; + case M680x0ISD::AND: + return "M680x0ISD::AND"; + case M680x0ISD::CMP: + return "M680x0ISD::CMP"; + case M680x0ISD::BT: + return "M680x0ISD::BT"; + case M680x0ISD::SELECT: + return "M680x0ISD::SELECT"; + case M680x0ISD::CMOV: + return "M680x0ISD::CMOV"; + case M680x0ISD::BRCOND: + return "M680x0ISD::BRCOND"; + case M680x0ISD::SETCC: + return "M680x0ISD::SETCC"; + case M680x0ISD::SETCC_CARRY: + return "M680x0ISD::SETCC_CARRY"; + case M680x0ISD::GlobalBaseReg: + return "M680x0ISD::GlobalBaseReg"; + case M680x0ISD::Wrapper: + return "M680x0ISD::Wrapper"; + case M680x0ISD::WrapperPC: + return "M680x0ISD::WrapperPC"; + case M680x0ISD::SEG_ALLOCA: + return "M680x0ISD::SEG_ALLOCA"; + default: + return NULL; + } +} Index: lib/Target/M680x0/M680x0InstrArithmetic.td =================================================================== --- /dev/null +++ lib/Target/M680x0/M680x0InstrArithmetic.td @@ -0,0 +1,845 @@ +//===-- M680x0InstrArithmetic.td - Integer Arith Instrs ----*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file describes the integer arithmetic instructions in the M680x0 +/// architecture. Here is the current status of the file: +/// +/// Machine: +/// +/// ADD [~] ADDA [~] ADDI [~] ADDQ [ ] ADDX [~] +/// CLR [ ] CMP [~] CMPA [~] CMPI [~] CMPM [ ] +/// CMP2 [ ] DIVS/DIVU [~] DIVSL/DIVUL [ ] EXT [~] EXTB [ ] +/// MULS/MULU [~] NEG [~] NEGX [~] SUB [~] SUBA [~] +/// SUBI [~] SUBQ [ ] SUBX [~] +/// +/// Map: +/// +/// [ ] - was not touched at all +/// [!] - requires extarnal stuff implemented +/// [~] - functional implementation +/// [X] - complete implementation +/// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Encoding +//===----------------------------------------------------------------------===// + +/// Encoding for Normal forms +/// ---------------------------------------------------- +/// F E D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +/// ---------------------------------------------------- +/// | | | EFFECTIVE ADDRESS +/// x x x x | REG | OP MODE | MODE | REG +/// ---------------------------------------------------- +class MxArithEncoding + : MxEncoding; + +/// Encoding for Extended forms +/// ------------------------------------------------------ +/// F E D C | B A 9 | 8 | 7 6 | 5 4 | 3 | 2 1 0 +/// ------------------------------------------------------ +/// x x x x | REG Rx | 1 | SIZE | 0 0 | M | REG Ry +/// ------------------------------------------------------ +/// Rx - destination +/// Ry - source +/// M - address mode switch +class MxArithXEncoding + : MxEncoding, SIZE, MxBead1Bit<0b1>, DST, CMD>; + +/// Encoding for Immediate forms +/// --------------------------------------------------- +/// F E D C B A 9 8 | 7 6 | 5 4 3 | 2 1 0 +/// --------------------------------------------------- +/// | | EFFECTIVE ADDRESS +/// x x x x x x x x | SIZE | MODE | REG +/// --------------------------------------------------- +/// 16-BIT WORD DATA | 8-BIT BYTE DATA +/// --------------------------------------------------- +/// 32-BIT LONG DATA +/// --------------------------------------------------- +/// NOTE It is used to store an immediate to memory, imm-to-reg are handled with +/// normal version +class MxArithImmEncoding + : MxEncoding, + SRC_EXT.Imm, SRC_EXT.B8, SRC_EXT.Scale, SRC_EXT.WL, SRC_EXT.DAReg, + DST_EXT.Imm, DST_EXT.B8, DST_EXT.Scale, DST_EXT.WL, DST_EXT.DAReg>; + + +//===----------------------------------------------------------------------===// +// Add/Sub +//===----------------------------------------------------------------------===// + +let Defs = [CCR] in { +let Constraints = "$src = $dst" in { + +// $reg, $ccr <- $reg op $reg +class MxBiArOp_RFRR_xEA CMD> + : MxInst<(outs TYPE.ROp:$dst), (ins TYPE.ROp:$src, TYPE.ROp:$opd), + MN#"."#TYPE.Prefix#"\t$opd, $dst", + [(set TYPE.VT:$dst, CCR, (NODE TYPE.VT:$src, TYPE.VT:$opd))], + MxArithEncoding, + !cast("MxOpMode"#TYPE.Size#TYPE.RLet#"EA"), + MxBeadReg<0>, !cast("MxEncEA"#TYPE.RLet#"_2"), MxExtEmpty>>; + +/// This Op is similar to the one above except it uses reversed opmode, some +/// commands(e.g. eor) do not support dEA or rEA modes and require EAd for +/// register only operations. +/// NOTE when using dd commands it is irrelevant which opmode to use(as it seems) +/// but some opcodes support address register and some do not which creates this +/// mess. +class MxBiArOp_RFRR_EAd CMD> + : MxInst<(outs TYPE.ROp:$dst), (ins TYPE.ROp:$src, TYPE.ROp:$opd), + MN#"."#TYPE.Prefix#"\t$opd, $dst", + [(set TYPE.VT:$dst, CCR, (NODE TYPE.VT:$src, TYPE.VT:$opd))], + MxArithEncoding, + !cast("MxOpMode"#TYPE.Size#"EAd"), + MxBeadReg<2>, MxEncEAd_0, MxExtEmpty>>; + +// $reg <- $reg op $imm +class MxBiArOp_RFRI_xEA CMD> + : MxInst<(outs TYPE.ROp:$dst), (ins TYPE.ROp:$src, TYPE.IOp:$opd), + MN#"."#TYPE.Prefix#"\t$opd, $dst", + [(set TYPE.VT:$dst, CCR, (NODE TYPE.VT:$src, TYPE.IPat:$opd))], + MxArithEncoding, + !cast("MxOpMode"#TYPE.Size#TYPE.RLet#"EA"), + MxBeadReg<0>, MxEncEAi, + !cast("MxExtI"#TYPE.Size#"_2")>>; + +// Again, there are two ways to write an immediate to Dn register either dEA +// opmode or using *I encoding, and again some instrucitons also support address +// registers some do not. +class MxBiArOp_RFRI CMD> + : MxInst<(outs TYPE.ROp:$dst), (ins TYPE.ROp:$src, TYPE.IOp:$opd), + MN#"i."#TYPE.Prefix#"\t$opd, $dst", + [(set TYPE.VT:$dst, CCR, (NODE TYPE.VT:$src, TYPE.IPat:$opd))], + MxArithImmEncoding, !cast("MxEncSize"#TYPE.Size), + !cast("MxEncEA"#TYPE.RLet#"_0"), MxExtEmpty, + !cast("MxExtI"#TYPE.Size#"_2")>>; + +let mayLoad = 1 in +class MxBiArOp_RFRM CMD, MxEncEA EA, MxEncExt EXT> + : MxInst<(outs TYPE.ROp:$dst), (ins TYPE.ROp:$src, OPD:$opd), + MN#"."#TYPE.Prefix#"\t$opd, $dst", + [(set TYPE.VT:$dst, CCR, (NODE TYPE.VT:$src, (TYPE.Load PAT:$opd)))], + MxArithEncoding, + !cast("MxOpMode"#TYPE.Size#TYPE.RLet#"EA"), + MxBeadReg<0>, EA, EXT>>; + +} // Constraints + +let mayLoad = 1, mayStore = 1 in { + +class MxBiArOp_FMR CMD, MxEncEA EA, MxEncExt EXT> + : MxInst<(outs), (ins MEMOpd:$dst, TYPE.ROp:$opd), + MN#"."#TYPE.Prefix#"\t$opd, $dst", + // FIXME These cannot consume CCR from MxAdd/MxSub which leads for + // MxAdd to survive the match and subsequent malmatch. + /* [(store (NODE (TYPE.Load MEMPat:$dst), TYPE.VT:$opd), MEMPat:$dst)], */ + [], + MxArithEncoding, + !cast("MxOpMode"#TYPE.Size#"EA"#TYPE.RLet), + MxBeadReg<1>, EA, EXT>>; + +class MxBiArOp_FMI CMD, MxEncEA MEMEA, MxEncExt MEMExt> + : MxInst<(outs), (ins MEMOpd:$dst, TYPE.IOp:$opd), + MN#"."#TYPE.Prefix#"\t$opd, $dst", + // FIXME Same as above + /* [(store (NODE (TYPE.Load MEMPat:$dst), TYPE.IPat:$opd), MEMPat:$dst)], */ + [], + MxArithImmEncoding, + !cast("MxEncSize"#TYPE.Size), + MEMEA, MEMExt, + !cast("MxExtI"#TYPE.Size#"_1")>>; +} // mayLoad, mayStore +} // Defs = [CCR] + +multiclass MxBiArOp_DF CMD, bits<4> CMDI> { + +// op $mem, $reg +def NAME#"8dk" : MxBiArOp_RFRM; +def NAME#"16dk" : MxBiArOp_RFRM; +def NAME#"32dk" : MxBiArOp_RFRM; + +def NAME#"8dq" : MxBiArOp_RFRM; +def NAME#"16dq" : MxBiArOp_RFRM; +def NAME#"32dq" : MxBiArOp_RFRM; + +def NAME#"8dp" : MxBiArOp_RFRM; +def NAME#"16dp" : MxBiArOp_RFRM; +def NAME#"32dp" : MxBiArOp_RFRM; + +def NAME#"8df" : MxBiArOp_RFRM; +def NAME#"16df" : MxBiArOp_RFRM; +def NAME#"32df" : MxBiArOp_RFRM; + +def NAME#"8dj" : MxBiArOp_RFRM; +def NAME#"16dj" : MxBiArOp_RFRM; +def NAME#"32dj" : MxBiArOp_RFRM; + +// op $imm, $reg +def NAME#"8di" : MxBiArOp_RFRI_xEA; +def NAME#"16di" : MxBiArOp_RFRI_xEA; +def NAME#"32di" : MxBiArOp_RFRI_xEA; + +// op $reg, $mem +def NAME#"8pd" : MxBiArOp_FMR; +def NAME#"16pd" : MxBiArOp_FMR; +def NAME#"32pd" : MxBiArOp_FMR; + +def NAME#"8fd" : MxBiArOp_FMR; +def NAME#"16fd" : MxBiArOp_FMR; +def NAME#"32fd" : MxBiArOp_FMR; + +def NAME#"8jd" : MxBiArOp_FMR; +def NAME#"16jd" : MxBiArOp_FMR; +def NAME#"32jd" : MxBiArOp_FMR; + +// op $imm, $mem +def NAME#"8pi" : MxBiArOp_FMI; +def NAME#"16pi" : MxBiArOp_FMI; +def NAME#"32pi" : MxBiArOp_FMI; + +def NAME#"8fi" : MxBiArOp_FMI; +def NAME#"16fi" : MxBiArOp_FMI; +def NAME#"32fi" : MxBiArOp_FMI; + +def NAME#"8ji" : MxBiArOp_FMI; +def NAME#"16ji" : MxBiArOp_FMI; +def NAME#"32ji" : MxBiArOp_FMI; + +let isCommutable = isComm in { + +def NAME#"8dd" : MxBiArOp_RFRR_xEA; +def NAME#"16dd" : MxBiArOp_RFRR_xEA; +def NAME#"32dd" : MxBiArOp_RFRR_xEA; + +} // isCommutable = ? + +} // MxBiArOp_DF + + +// These special snowflakes allowed to match address registers but since *A +// operations do not produce CCR we should not match them against Mx nodes that +// produce it. +let Pattern = [(null_frag)] in +multiclass MxBiArOp_AF CMD, bits<4> CMDI> { + +def NAME#"32rk" : MxBiArOp_RFRM; +def NAME#"32rq" : MxBiArOp_RFRM; +def NAME#"32rf" : MxBiArOp_RFRM; +def NAME#"32rp" : MxBiArOp_RFRM; +def NAME#"32rj" : MxBiArOp_RFRM; +def NAME#"32ri" : MxBiArOp_RFRI_xEA; + +let isCommutable = isComm in +def NAME#"32rr" : MxBiArOp_RFRR_xEA; + +} // MxBiArOp_AF + +// NOTE These naturally produce CCR + +defm ADD : MxBiArOp_DF<"add", MxAdd, 1, 0xD, 0x6>; +defm ADD : MxBiArOp_AF<"add", MxAdd, 1, 0xD, 0x6>; +defm SUB : MxBiArOp_DF<"sub", MxSub, 0, 0x9, 0x4>; +defm SUB : MxBiArOp_AF<"sub", MxSub, 0, 0x9, 0x4>; + + +let Uses = [CCR], Defs = [CCR] in { +let Constraints = "$src = $dst" in { + +// $reg, ccr <- $reg op $reg op ccr +class MxBiArOp_RFRRF CMD> + : MxInst<(outs TYPE.ROp:$dst), (ins TYPE.ROp:$src, TYPE.ROp:$opd), + MN#"."#TYPE.Prefix#"\t$opd, $dst", + [(set TYPE.VT:$dst, CCR, (NODE TYPE.VT:$src, TYPE.VT:$opd, CCR))], + MxArithXEncoding, + !cast("MxEncSize"#TYPE.Size), + MxBead1Bit<0>, MxBeadReg<2>, MxBeadReg<0>>>; + +} // Constraints +} // Uses, Defs + +multiclass MxBiArOp_RFF CMD> { + +let isCommutable = isComm in { + +def NAME#"8dd" : MxBiArOp_RFRRF; +def NAME#"16dd" : MxBiArOp_RFRRF; +def NAME#"32dd" : MxBiArOp_RFRRF; + +} // isCommutable = ? + +} // MxBiArOp_RFF + +// NOTE These consume and produce CCR +defm ADDX : MxBiArOp_RFF<"addx", MxAddX, 1, 0xD>; +defm SUBX : MxBiArOp_RFF<"subx", MxSubX, 0, 0x9>; + + +//===----------------------------------------------------------------------===// +// And/Xor/Or +//===----------------------------------------------------------------------===// + +defm AND : MxBiArOp_DF<"and", MxAnd, 1, 0xC, 0x2>; +defm OR : MxBiArOp_DF<"or", MxOr, 1, 0x8, 0x0>; + +multiclass MxBiArOp_DF_EAd CMD, bits<4> CMDI> { + +let isCommutable = 1 in { + +def NAME#"8dd" : MxBiArOp_RFRR_EAd; +def NAME#"16dd" : MxBiArOp_RFRR_EAd; +def NAME#"32dd" : MxBiArOp_RFRR_EAd; + +} // isCommutable = 1 + +def NAME#"8di" : MxBiArOp_RFRI; +def NAME#"16di" : MxBiArOp_RFRI; +def NAME#"32di" : MxBiArOp_RFRI; + +} // MxBiArOp_DF_EAd + +defm XOR : MxBiArOp_DF_EAd<"eor", MxXor, 0xB, 0xA>; + + +//===----------------------------------------------------------------------===// +// CMP +//===----------------------------------------------------------------------===// + +let Defs = [CCR] in { +class MxCmp_RR + : MxInst<(outs), (ins TYPE.ROp:$lhs, TYPE.ROp:$rhs), + "cmp."#TYPE.Prefix#"\t$lhs, $rhs", + [(set CCR, (MxCmp TYPE.VT:$lhs, TYPE.VT:$rhs))], + MxArithEncoding, + !cast("MxOpMode"#TYPE.Size#"dEA"), + MxBeadReg<1>, MxEncEAd_0, MxExtEmpty>>; + +class MxCmp_RI + : MxInst<(outs), (ins TYPE.IOp:$imm, TYPE.ROp:$reg), + "cmpi."#TYPE.Prefix#"\t$imm, $reg", + [(set CCR, (MxCmp TYPE.IPat:$imm, TYPE.VT:$reg))], + MxArithImmEncoding, !cast("MxEncSize"#TYPE.Size), + MxEncEAd_1, MxExtEmpty, + !cast("MxExtI"#TYPE.Size#"_0")>>; + +let mayLoad = 1 in { + +class MxCmp_MI + : MxInst<(outs), (ins TYPE.IOp:$imm, MEMOpd:$mem), + "cmpi."#TYPE.Prefix#"\t$imm, $mem", + [(set CCR, (MxCmp TYPE.IPat:$imm, (load MEMPat:$mem)))], + MxArithImmEncoding, !cast("MxEncSize"#TYPE.Size), + EA, EXT, !cast("MxExtI"#TYPE.Size#"_0")>>; + +class MxCmp_BI + : MxInst<(outs), (ins TYPE.IOp:$imm, MxAL32:$abs), + "cmpi."#TYPE.Prefix#"\t$imm, $abs", + [(set CCR, (MxCmp TYPE.IPat:$imm, (load (i32 (MxWrapper tglobaladdr:$abs)))))], + MxArithImmEncoding, !cast("MxEncSize"#TYPE.Size), + MxEncEAb, MxExtI32_1, !cast("MxExtI"#TYPE.Size#"_0")>>; + +class MxCmp_RM + : MxInst<(outs), (ins TYPE.ROp:$reg, MEMOpd:$mem), + "cmp."#TYPE.Prefix#"\t$mem, $reg", + [(set CCR, (MxCmp (load MEMPat:$mem), TYPE.ROp:$reg))], + MxArithEncoding, + !cast("MxOpMode"#TYPE.Size#"dEA"), + MxBeadReg<0>, EA, EXT>>; +} // let mayLoad = 1 + +} // let Defs = [CCR] + +multiclass MMxCmp_RM { +def NAME#TYPE.KOp.Letter : MxCmp_RM; +def NAME#TYPE.QOp.Letter : MxCmp_RM; +def NAME#TYPE.POp.Letter : MxCmp_RM; +def NAME#TYPE.FOp.Letter : MxCmp_RM; +def NAME#TYPE.JOp.Letter : MxCmp_RM; +} + +multiclass MMxCmp_MI { +def NAME#TYPE.KOp.Letter#"i" : MxCmp_MI; +def NAME#TYPE.QOp.Letter#"i" : MxCmp_MI; +def NAME#TYPE.POp.Letter#"i" : MxCmp_MI; +def NAME#TYPE.FOp.Letter#"i" : MxCmp_MI; +def NAME#TYPE.JOp.Letter#"i" : MxCmp_MI; +} + +foreach S = [8, 16, 32] in { +def CMP#S#dd : MxCmp_RR("MxType"#S#"d")>; +def CMP#S#di : MxCmp_RI("MxType"#S#"d")>; +def CMP#S#bi : MxCmp_BI("MxType"#S#"d")>; +} // foreach + +// cmp mem, Dn +defm CMP8d : MMxCmp_RM; +defm CMP16d : MMxCmp_RM; +defm CMP32d : MMxCmp_RM; + +// cmp #imm, mem +defm CMP8 : MMxCmp_MI; +defm CMP16 : MMxCmp_MI; +defm CMP32 : MMxCmp_MI; + + +//===----------------------------------------------------------------------===// +// EXT +//===----------------------------------------------------------------------===// + +def MxExtOpmode_wb : MxBead3Bits<0b010>; +def MxExtOpmode_lw : MxBead3Bits<0b011>; +def MxExtOpmode_lb : MxBead3Bits<0b111>; + +/// --------------------------------------------------- +/// F E D C B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +/// --------------------------------------------------- +/// 0 1 0 0 1 0 0 | OPMODE | 0 0 0 | REG +/// --------------------------------------------------- +class MxExtEncoding + : MxEncoding, MxBead3Bits<0b000>, OPMODE, + MxBead3Bits<0b100>, MxBead4Bits<0b0100>>; + +let Defs = [CCR] in +let Constraints = "$src = $dst" in +class MxExt + : MxInst<(outs TO.ROp:$dst), (ins TO.ROp:$src), + "ext."#TO.Prefix#"\t$src", [], + MxExtEncoding("MxExtOpmode_"#TO.Prefix#FROM.Prefix)>>; + +def EXT16 : MxExt; +def EXT32 : MxExt; + +def : Pat<(sext_inreg i16:$src, i8), (EXT16 $src)>; +def : Pat<(sext_inreg i32:$src, i16), (EXT32 $src)>; +def : Pat<(sext_inreg i32:$src, i8), + (EXT32 (MOVXd32d16 (EXT16 (EXTRACT_SUBREG $src, MxSubRegIndex16Lo))))>; + + +//===----------------------------------------------------------------------===// +// DIV/MUL +//===----------------------------------------------------------------------===// + +def MxSDiMuOpmode : MxBead3Bits<0b111>; +def MxUDiMuOpmode : MxBead3Bits<0b011>; + +/// Word operation: +/// ---------------------------------------------------- +/// F E D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +/// ---------------------------------------------------- +/// | | | EFFECTIVE ADDRESS +/// x x x x | REG | OP MODE | MODE | REG +/// ---------------------------------------------------- +class MxDiMuEncoding + : MxEncoding, CMD, + EXT.Imm, EXT.B8, EXT.Scale, EXT.WL, EXT.DAReg>; + +let Defs = [CCR] in { +let Constraints = "$src = $dst" in { +// $reg <- $reg op $reg +class MxDiMuOp_DD CMD, MxBead3Bits OPMODE, + MxOperand DST, MxOperand OPD> + : MxInst<(outs DST:$dst), (ins DST:$src, OPD:$opd), MN#"\t$opd, $dst", [], + MxDiMuEncoding, OPMODE, MxEncEAd_2, MxExtEmpty>>; + +// $reg <- $reg op $imm +class MxDiMuOp_DI CMD, MxBead3Bits OPMODE, + MxOperand DST, MxOperand OPD> + : MxInst<(outs DST:$dst), (ins DST:$src, unknown:$opd), MN#"\t$opd, $dst", [], + MxDiMuEncoding, OPMODE, MxEncEAi, MxExtI16_2>>; +} // let Constraints +} // Defs = [CCR] + +multiclass MxDiMuOp CMD, bit isComm = 0> { + +let isCommutable = isComm in { +def "S"#NAME#"d32d16" : MxDiMuOp_DD; +def "U"#NAME#"d32d16" : MxDiMuOp_DD; +} + +def "S"#NAME#"d32i16" : MxDiMuOp_DI; +def "U"#NAME#"d32i16" : MxDiMuOp_DI; + +} + +defm DIV : MxDiMuOp<"div", 0x8>; + +// RR i8 +def : Pat<(sdiv i8:$dst, i8:$opd), + (EXTRACT_SUBREG + (SDIVd32d16 (MOVSXd32d8 $dst), (MOVSXd16d8 $opd)), + MxSubRegIndex8Lo)>; + +def : Pat<(udiv i8:$dst, i8:$opd), + (EXTRACT_SUBREG + (UDIVd32d16 (MOVZXd32d8 $dst), (MOVZXd16d8 $opd)), + MxSubRegIndex8Lo)>; + +def : Pat<(srem i8:$dst, i8:$opd), + (EXTRACT_SUBREG + (ASR32di (ASR32di (SDIVd32d16 (MOVSXd32d8 $dst), (MOVSXd16d8 $opd)), 8), 8), + MxSubRegIndex8Lo)>; + +def : Pat<(urem i8:$dst, i8:$opd), + (EXTRACT_SUBREG + (LSR32di (LSR32di (UDIVd32d16 (MOVZXd32d8 $dst), (MOVZXd16d8 $opd)), 8), 8), + MxSubRegIndex8Lo)>; + +// RR i16 +def : Pat<(sdiv i16:$dst, i16:$opd), + (EXTRACT_SUBREG + (SDIVd32d16 (MOVSXd32d16 $dst), $opd), + MxSubRegIndex16Lo)>; + +def : Pat<(udiv i16:$dst, i16:$opd), + (EXTRACT_SUBREG + (UDIVd32d16 (MOVZXd32d16 $dst), $opd), + MxSubRegIndex16Lo)>; + +def : Pat<(srem i16:$dst, i16:$opd), + (EXTRACT_SUBREG + (ASR32di (ASR32di (SDIVd32d16 (MOVSXd32d16 $dst), $opd), 8), 8), + MxSubRegIndex16Lo)>; + +def : Pat<(urem i16:$dst, i16:$opd), + (EXTRACT_SUBREG + (LSR32di (LSR32di (UDIVd32d16 (MOVZXd32d16 $dst), $opd), 8), 8), + MxSubRegIndex16Lo)>; + + +// RI i8 +def : Pat<(sdiv i8:$dst, MximmSExt8:$opd), + (EXTRACT_SUBREG + (SDIVd32i16 (MOVSXd32d8 $dst), imm:$opd), + MxSubRegIndex8Lo)>; + +def : Pat<(udiv i8:$dst, MximmSExt8:$opd), + (EXTRACT_SUBREG + (UDIVd32i16 (MOVZXd32d8 $dst), imm:$opd), + MxSubRegIndex8Lo)>; + +def : Pat<(srem i8:$dst, MximmSExt8:$opd), + (EXTRACT_SUBREG + (ASR32di (ASR32di (SDIVd32i16 (MOVSXd32d8 $dst), imm:$opd), 8), 8), + MxSubRegIndex8Lo)>; + +def : Pat<(urem i8:$dst, MximmSExt8:$opd), + (EXTRACT_SUBREG + (LSR32di (LSR32di (UDIVd32i16 (MOVZXd32d8 $dst), imm:$opd), 8), 8), + MxSubRegIndex8Lo)>; + +// RI i16 +def : Pat<(sdiv i16:$dst, MximmSExt16:$opd), + (EXTRACT_SUBREG + (SDIVd32i16 (MOVSXd32d16 $dst), imm:$opd), + MxSubRegIndex16Lo)>; + +def : Pat<(udiv i16:$dst, MximmSExt16:$opd), + (EXTRACT_SUBREG + (UDIVd32i16 (MOVZXd32d16 $dst), imm:$opd), + MxSubRegIndex16Lo)>; + +def : Pat<(srem i16:$dst, MximmSExt16:$opd), + (EXTRACT_SUBREG + (ASR32di (ASR32di (SDIVd32i16 (MOVSXd32d16 $dst), imm:$opd), 8), 8), + MxSubRegIndex16Lo)>; + +def : Pat<(urem i16:$dst, MximmSExt16:$opd), + (EXTRACT_SUBREG + (LSR32di (LSR32di (UDIVd32i16 (MOVZXd32d16 $dst), imm:$opd), 8), 8), + MxSubRegIndex16Lo)>; + + +defm MUL : MxDiMuOp<"mul", 0xC, 1>; + +// RR +def : Pat<(mul i16:$dst, i16:$opd), + (EXTRACT_SUBREG + (SMULd32d16 (MOVXd32d16 $dst), $opd), + MxSubRegIndex16Lo)>; + +def : Pat<(mulhs i16:$dst, i16:$opd), + (EXTRACT_SUBREG + (ASR32di (ASR32di (SMULd32d16 (MOVXd32d16 $dst), $opd), 8), 8), + MxSubRegIndex16Lo)>; + +def : Pat<(mulhu i16:$dst, i16:$opd), + (EXTRACT_SUBREG + (LSR32di (LSR32di (UMULd32d16 (MOVXd32d16 $dst), $opd), 8), 8), + MxSubRegIndex16Lo)>; + + +// RI +def : Pat<(mul i16:$dst, MximmSExt16:$opd), + (EXTRACT_SUBREG + (SMULd32i16 (MOVXd32d16 $dst), imm:$opd), + MxSubRegIndex16Lo)>; + +def : Pat<(mulhs i16:$dst, MximmSExt16:$opd), + (EXTRACT_SUBREG + (ASR32di (ASR32di (SMULd32i16 (MOVXd32d16 $dst), imm:$opd), 8), 8), + MxSubRegIndex16Lo)>; + +def : Pat<(mulhu i16:$dst, MximmSExt16:$opd), + (EXTRACT_SUBREG + (LSR32di (LSR32di (UMULd32i16 (MOVXd32d16 $dst), imm:$opd), 8), 8), + MxSubRegIndex16Lo)>; + + +//===----------------------------------------------------------------------===// +// NEG/NEGX +//===----------------------------------------------------------------------===// + +/// ------------+------------+------+---------+--------- +/// F E D C | B A 9 8 | 7 6 | 5 4 3 | 2 1 0 +/// ------------+------------+------+------------------- +/// | | | EFFECTIVE ADDRESS +/// 0 1 0 0 | x x x x | SIZE | MODE | REG +/// ------------+------------+------+---------+--------- +class MxNEGEncoding + : MxEncoding, + EXT.Imm, EXT.B8, EXT.Scale, EXT.WL, EXT.DAReg>; + +let Defs = [CCR] in { +let Constraints = "$src = $dst" in { + +class MxNeg_D + : MxInst<(outs TYPE.ROp:$dst), (ins TYPE.ROp:$src), "neg."#TYPE.Prefix#"\t$dst", + [(set TYPE.VT:$dst, (ineg TYPE.VT:$src))], + MxNEGEncoding, !cast("MxEncSize"#TYPE.Size), + MxEncEAd_0, MxExtEmpty>>; + +let Uses = [CCR] in { +class MxNegX_D + : MxInst<(outs TYPE.ROp:$dst), (ins TYPE.ROp:$src), "negx."#TYPE.Prefix#"\t$dst", + [(set TYPE.VT:$dst, (MxSubX 0, TYPE.VT:$src, CCR))], + MxNEGEncoding, !cast("MxEncSize"#TYPE.Size), + MxEncEAd_0, MxExtEmpty>>; +} + +} // let Constraints +} // let Defs = [CCR] + +foreach S = [8, 16, 32] in { + def NEG#S#d : MxNeg_D("MxType"#S#"d")>; + def NEGX#S#d : MxNegX_D("MxType"#S#"d")>; +} + +def : Pat<(MxSub 0, i8 :$src), (NEG8d MxDRD8 :$src)>; +def : Pat<(MxSub 0, i16:$src), (NEG16d MxDRD16:$src)>; +def : Pat<(MxSub 0, i32:$src), (NEG32d MxDRD32:$src)>; + +//===----------------------------------------------------------------------===// +// no-CCR Patterns +//===----------------------------------------------------------------------===// + +/// Basically the reason for this stuff is that add and addc share the same +/// operand types constraints for whatever reasons and I had to define a common +/// MxAdd and MxSub instructions that produce CCR and then pattern-map add and addc +/// to it. +/// NOTE On the other hand I see no reason why I cannot just drop explicit CCR +/// result. Anyway works for now, hopefully I will better understand how this stuff +/// is designed later +foreach node = ["add", "addc"] in { + +// add reg, reg +def : Pat<(!cast(node) i8 :$src, i8 :$opd), (ADD8dd MxDRD8 :$src, MxDRD8 :$opd)>; +def : Pat<(!cast(node) i16:$src, i16:$opd), (ADD16dd MxDRD16:$src, MxDRD16:$opd)>; +def : Pat<(!cast(node) i32:$src, i32:$opd), (ADD32rr MxXRD32:$src, MxXRD32:$opd)>; + +// add (An), reg +def : Pat<(!cast(node) MxType8.VT:$src, (Mxloadi8 MxType8.JPat:$opd)), + (ADD8dj MxDRD8:$src, MxType8.JOp:$opd)>; +def : Pat<(!cast(node) MxType16.VT:$src, (Mxloadi16 MxType16.JPat:$opd)), + (ADD16dj MxDRD16:$src, MxType16.JOp:$opd)>; +def : Pat<(!cast(node) MxType32.VT:$src, (Mxloadi32 MxType32.JPat:$opd)), + (ADD32rj MxXRD32:$src, MxType32.JOp:$opd)>; + +// add (i,An), reg +def : Pat<(!cast(node) MxType8.VT:$src, (Mxloadi8 MxType8.PPat:$opd)), + (ADD8dp MxDRD8:$src, MxType8.POp:$opd)>; +def : Pat<(!cast(node) MxType16.VT:$src, (Mxloadi16 MxType16.PPat:$opd)), + (ADD16dp MxDRD16:$src, MxType16.POp:$opd)>; +def : Pat<(!cast(node) MxType32.VT:$src, (Mxloadi32 MxType32.PPat:$opd)), + (ADD32rp MxXRD32:$src, MxType32.POp:$opd)>; + +// add (i,An,Xn), reg +def : Pat<(!cast(node) MxType8.VT:$src, (Mxloadi8 MxType8.FPat:$opd)), + (ADD8df MxDRD8:$src, MxType8.FOp:$opd)>; +def : Pat<(!cast(node) MxType16.VT:$src, (Mxloadi16 MxType16.FPat:$opd)), + (ADD16df MxDRD16:$src, MxType16.FOp:$opd)>; +def : Pat<(!cast(node) MxType32.VT:$src, (Mxloadi32 MxType32.FPat:$opd)), + (ADD32rf MxXRD32:$src, MxType32.FOp:$opd)>; + +// add reg, imm +def : Pat<(!cast(node) i8: $src, MximmSExt8:$opd), (ADD8di MxDRD8 :$src, imm:$opd)>; +def : Pat<(!cast(node) i16:$src, MximmSExt16:$opd), (ADD16di MxDRD16:$src, imm:$opd)>; + +// LEAp is more complex and thus will be selected over normal ADD32ri but it cannot +// be used with data registers, here by adding complexity to a simple ADD32ri insts +// we make sure it will be selected over LEAp +let AddedComplexity = 15 in { +def : Pat<(!cast(node) i32:$src, MximmSExt32:$opd), (ADD32ri MxXRD32:$src, imm:$opd)>; +} // AddedComplexity = 15 + +// add imm, (An) +def : Pat<(store (!cast(node) (load MxType8.JPat:$dst), MxType8.IPat:$opd), MxType8.JPat:$dst), + (ADD8ji MxType8.JOp:$dst, imm:$opd)>; +def : Pat<(store (!cast(node) (load MxType16.JPat:$dst), MxType16.IPat:$opd), MxType16.JPat:$dst), + (ADD16ji MxType16.JOp:$dst, imm:$opd)>; +def : Pat<(store (!cast(node) (load MxType32.JPat:$dst), MxType32.IPat:$opd), MxType32.JPat:$dst), + (ADD32ji MxType32.JOp:$dst, imm:$opd)>; + +// These patterns treat AL value as immediate +/* def : Pat<(!cast(node) MxType32r.ROp:$src, MxType32r.BPat:$opd), */ +/* (ADD32ri MxXRD32:$src, MxType32r.IOp:$opd)>; */ +/* */ +/* def : Pat<(store (!cast(node) (load MxType32.FPat:$dst), MxType32.BPat:$opd), MxType32.FPat:$dst), */ +/* (ADD32ji MxType8.FOp:$dst, MxType32.IOp:$opd)>; */ +/* */ +/* def : Pat<(store (!cast(node) (load MxType32.PPat:$dst), MxType32.BPat:$opd), MxType32.PPat:$dst), */ +/* (ADD32ji MxType8.POp:$dst, MxType32.IOp:$opd)>; */ +/* */ +/* def : Pat<(store (!cast(node) (load MxType32.JPat:$dst), MxType32.BPat:$opd), MxType32.JPat:$dst), */ +/* (ADD32ji MxType8.JOp:$dst, MxType32.IOp:$opd)>; */ + +} // foreach add, addc + +def : Pat<(adde i8 :$src, i8 :$opd), (ADDX8dd MxDRD8 :$src, MxDRD8 :$opd)>; +def : Pat<(adde i16:$src, i16:$opd), (ADDX16dd MxDRD16:$src, MxDRD16:$opd)>; +def : Pat<(adde i32:$src, i32:$opd), (ADDX32dd MxDRD32:$src, MxDRD32:$opd)>; + + + +foreach node = ["sub", "subc"] in { + +// sub reg, reg +def : Pat<(!cast(node) i8 :$src, i8 :$opd), (SUB8dd MxDRD8 :$src, MxDRD8 :$opd)>; +def : Pat<(!cast(node) i16:$src, i16:$opd), (SUB16dd MxDRD16:$src, MxDRD16:$opd)>; +def : Pat<(!cast(node) i32:$src, i32:$opd), (SUB32rr MxXRD32:$src, MxXRD32:$opd)>; + + +// sub (An), reg +def : Pat<(!cast(node) MxType8.VT:$src, (Mxloadi8 MxType8.JPat:$opd)), + (SUB8dj MxDRD8:$src, MxType8.JOp:$opd)>; +def : Pat<(!cast(node) MxType16.VT:$src, (Mxloadi16 MxType16.JPat:$opd)), + (SUB16dj MxDRD16:$src, MxType16.JOp:$opd)>; +def : Pat<(!cast(node) MxType32.VT:$src, (Mxloadi32 MxType32.JPat:$opd)), + (SUB32rj MxXRD32:$src, MxType32.JOp:$opd)>; + +// sub (i,An), reg +def : Pat<(!cast(node) MxType8.VT:$src, (Mxloadi8 MxType8.PPat:$opd)), + (SUB8dp MxDRD8:$src, MxType8.POp:$opd)>; +def : Pat<(!cast(node) MxType16.VT:$src, (Mxloadi16 MxType16.PPat:$opd)), + (SUB16dp MxDRD16:$src, MxType16.POp:$opd)>; +def : Pat<(!cast(node) MxType32.VT:$src, (Mxloadi32 MxType32.PPat:$opd)), + (SUB32rp MxXRD32:$src, MxType32.POp:$opd)>; + +// sub (i,An,Xn), reg +def : Pat<(!cast(node) MxType8.VT:$src, (Mxloadi8 MxType8.FPat:$opd)), + (SUB8df MxDRD8:$src, MxType8.FOp:$opd)>; +def : Pat<(!cast(node) MxType16.VT:$src, (Mxloadi16 MxType16.FPat:$opd)), + (SUB16df MxDRD16:$src, MxType16.FOp:$opd)>; +def : Pat<(!cast(node) MxType32.VT:$src, (Mxloadi32 MxType32.FPat:$opd)), + (SUB32rf MxXRD32:$src, MxType32.FOp:$opd)>; + +// sub reg, imm +def : Pat<(!cast(node) i8 :$src, MximmSExt8 :$opd), (SUB8di MxDRD8 :$src, imm:$opd)>; +def : Pat<(!cast(node) i16:$src, MximmSExt16:$opd), (SUB16di MxDRD16:$src, imm:$opd)>; +def : Pat<(!cast(node) i32:$src, MximmSExt32:$opd), (SUB32ri MxXRD32:$src, imm:$opd)>; + +// sub imm, (An) +def : Pat<(store (!cast(node) (load MxType8.JPat:$dst), MxType8.IPat:$opd), MxType8.JPat:$dst), + (SUB8ji MxType8.JOp:$dst, imm:$opd)>; +def : Pat<(store (!cast(node) (load MxType16.JPat:$dst), MxType16.IPat:$opd), MxType16.JPat:$dst), + (SUB16ji MxType16.JOp:$dst, imm:$opd)>; +def : Pat<(store (!cast(node) (load MxType32.JPat:$dst), MxType32.IPat:$opd), MxType32.JPat:$dst), + (SUB32ji MxType32.JOp:$dst, imm:$opd)>; + +} // foreach sub, subx + +def : Pat<(sube i8 :$src, i8 :$opd), (SUBX8dd MxDRD8 :$src, MxDRD8 :$opd)>; +def : Pat<(sube i16:$src, i16:$opd), (SUBX16dd MxDRD16:$src, MxDRD16:$opd)>; +def : Pat<(sube i32:$src, i32:$opd), (SUBX32dd MxDRD32:$src, MxDRD32:$opd)>; + + +// and reg, reg +def : Pat<(and i8 :$src, i8 :$opd), (AND8dd MxDRD8 :$src, MxDRD8 :$opd)>; +def : Pat<(and i16:$src, i16:$opd), (AND16dd MxDRD16:$src, MxDRD16:$opd)>; +def : Pat<(and i32:$src, i32:$opd), (AND32dd MxDRD32:$src, MxDRD32:$opd)>; + +// and reg, mem +/* def : Pat<(and MxDRD8:$src, (Mxloadi8 addr:$opd)), */ +/* (AND8rm MxDRD8:$src, addr:$opd)>; */ +/* def : Pat<(and MxXRD16:$src, (Mxloadi16 addr:$opd)), */ +/* (AND16rm MxXRD16:$src, addr:$opd)>; */ +/* def : Pat<(and MxXRD32:$src, (Mxloadi32 addr:$opd)), */ +/* (AND32rm MxXRD32:$src, addr:$opd)>; */ +/* def : Pat<(and GR64:$src, (loadi64 addr:$opd)), */ +/* (AND64rm GR64:$src, addr:$opd)>; */ + +// and reg, imm +def : Pat<(and i8: $src, MximmSExt8 :$opd), (AND8di MxDRD8 :$src, imm:$opd)>; +def : Pat<(and i16:$src, MximmSExt16:$opd), (AND16di MxDRD16:$src, imm:$opd)>; +def : Pat<(and i32:$src, MximmSExt32:$opd), (AND32di MxDRD32:$src, imm:$opd)>; + + +// xor reg,reg +def : Pat<(xor i8 :$src, i8 :$opd), (XOR8dd MxDRD8 :$src, MxDRD8 :$opd)>; +def : Pat<(xor i16:$src, i16:$opd), (XOR16dd MxDRD16:$src, MxDRD16:$opd)>; +def : Pat<(xor i32:$src, i32:$opd), (XOR32dd MxDRD32:$src, MxDRD32:$opd)>; + +// xor reg, mem +/* def : Pat<(xor MxDRD8:$src, (Mxloadi8 addr:$opd)), */ +/* (XOR8rm MxDRD8:$src, addr:$opd)>; */ +/* def : Pat<(xor MxXRD16:$src, (Mxloadi16 addr:$opd)), */ +/* (XOR16rm MxXRD16:$src, addr:$opd)>; */ +/* def : Pat<(xor MxXRD32:$src, (Mxloadi32 addr:$opd)), */ +/* (XOR32rm MxXRD32:$src, addr:$opd)>; */ +/* def : Pat<(xor GR64:$src, (loadi64 addr:$opd)), */ +/* (XOR64rm GR64:$src, addr:$opd)>; */ + +// xor reg, imm +def : Pat<(xor i8: $src, MximmSExt8 :$opd), (XOR8di MxDRD8 :$src, imm:$opd)>; +def : Pat<(xor i16:$src, MximmSExt16:$opd), (XOR16di MxDRD16:$src, imm:$opd)>; +def : Pat<(xor i32:$src, MximmSExt32:$opd), (XOR32di MxDRD32:$src, imm:$opd)>; + + +// or reg, reg +def : Pat<(or i8 :$src, i8 :$opd), (OR8dd MxDRD8 :$src, MxDRD8 :$opd)>; +def : Pat<(or i16:$src, i16:$opd), (OR16dd MxDRD16:$src, MxDRD16:$opd)>; +def : Pat<(or i32:$src, i32:$opd), (OR32dd MxDRD32:$src, MxDRD32:$opd)>; + +// or reg/mem +/* def : Pat<(or MxDRD8:$src, (Mxloadi8 addr:$opd)), */ +/* (OR8rm MxDRD8:$src, addr:$opd)>; */ +/* def : Pat<(or MxXRD16:$src, (Mxloadi16 addr:$opd)), */ +/* (OR16rm MxXRD16:$src, addr:$opd)>; */ +/* def : Pat<(or MxXRD32:$src, (Mxloadi32 addr:$opd)), */ +/* (OR32rm MxXRD32:$src, addr:$opd)>; */ +/* def : Pat<(or GR64:$src, (loadi64 addr:$opd)), */ +/* (OR64rm GR64:$src, addr:$opd)>; */ + +// or reg, imm +def : Pat<(or i8: $src, MximmSExt8 :$opd), (OR8di MxDRD8 :$src, imm:$opd)>; +def : Pat<(or i16:$src, MximmSExt16:$opd), (OR16di MxDRD16:$src, imm:$opd)>; +def : Pat<(or i32:$src, MximmSExt32:$opd), (OR32di MxDRD32:$src, imm:$opd)>; Index: lib/Target/M680x0/M680x0InstrBits.td =================================================================== --- /dev/null +++ lib/Target/M680x0/M680x0InstrBits.td @@ -0,0 +1,91 @@ +//===------- M680x0InstrBits.td - Bit Manipulation Instrs --*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file describes the bit manipulation instructions in the M680x0 +/// architecture. Here is the current status of the file: +/// +/// Machine: +/// +/// BCNG [ ] BCLR [ ] BSET [ ] BTST [~] +/// +/// Map: +/// +/// [ ] - was not touched at all +/// [!] - requires extarnal stuff implemented +/// [~] - in progress but usable +/// [x] - done +/// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// BTST +//===----------------------------------------------------------------------===// + +/// ------------+---------+---------+---------+--------- +/// F E D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +/// ------------+---------+---------+---------+--------- +/// 0 0 0 0 | REG | 1 0 0 | MODE | REG +/// ------------+---------+---------+---------+--------- +class MxBTSTEnc_R + : MxEncoding, REG, MxBead4Bits<0b0000>, + EXT.Imm, EXT.B8, EXT.Scale, EXT.WL, EXT.DAReg>; + +/// -------------------------------+---------+--------- +/// F E D C B A 9 8 . 7 6 | 5 4 3 | 2 1 0 +/// -------------------------------+---------+--------- +/// 0 0 0 0 1 0 0 0 . 0 0 | MODE | REG +/// ------------------------+------+---------+--------- +/// 0 0 0 0 0 0 0 0 | BIT NUMBER +/// ------------------------+-------------------------- +class MxBTSTEnc_I + : MxEncoding, + MxBead4Bits<0b1000>, MxBead4Bits<0b0000>, IMM, + EXT.Imm, EXT.B8, EXT.Scale, EXT.WL, EXT.DAReg>; + +let Defs = [CCR] in { +class MxBTST_RR + : MxInst<(outs), (ins TYPE.ROp:$dst, TYPE.ROp:$bitno), "btst\t$bitno, $dst", + [(set CCR, (MxBt TYPE.VT:$dst, TYPE.VT:$bitno))], + MxBTSTEnc_R, MxEncEAd_0, MxExtEmpty>>; + +class MxBTST_RI + : MxInst<(outs), (ins TYPE.ROp:$dst, TYPE.IOp:$bitno), "btst\t$bitno, $dst", + [(set CCR, (MxBt TYPE.VT:$dst, TYPE.IPat:$bitno))], + MxBTSTEnc_I, MxEncEAd_0, MxExtEmpty>>; + +class MxBTST_MR + : MxInst<(outs), (ins MEMOpd:$dst, TYPE.ROp:$bitno), "btst\t$bitno, $dst", + [(set CCR, (MxBt (TYPE.Load MEMPat:$dst), TYPE.VT:$bitno))], + MxBTSTEnc_R, EA, EXT>>; + +class MxBTST_MI + : MxInst<(outs), (ins MEMOpd:$dst, TYPE.IOp:$bitno), "btst\t$bitno, $dst", + [(set CCR, (MxBt (TYPE.Load MEMPat:$dst), TYPE.IPat:$bitno))], + MxBTSTEnc_I, EA, EXT>>; +} // Defs = [CCR] + +// Register BTST limited to 32 bits only +def BTST32dd : MxBTST_RR; +def BTST32di : MxBTST_RI; + +// Memory BTST limited to 8 bits only +def BTST8jd : MxBTST_MR; +def BTST8pd : MxBTST_MR; +def BTST8fd : MxBTST_MR; +def BTST8qd : MxBTST_MR; +def BTST8kd : MxBTST_MR; + +def BTST8ji : MxBTST_MI; +def BTST8pi : MxBTST_MI; +def BTST8fi : MxBTST_MI; +def BTST8qi : MxBTST_MI; +def BTST8ki : MxBTST_MI; Index: lib/Target/M680x0/M680x0InstrBuilder.h =================================================================== --- /dev/null +++ lib/Target/M680x0/M680x0InstrBuilder.h @@ -0,0 +1,96 @@ +//===-- M680x0InstrBuilder.h - Functions to build M680x0 insts --*- 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 exposes functions that may be used with BuildMI from the +/// MachineInstrBuilder.h file to handle M680x0'isms in a clean way. +/// +/// TODO edit the following mem stuff +/// The BuildMem function may be used with the BuildMI function to add entire +/// memory references in a single, typed, function call. M680x0 memory +/// references can be very complex expressions (described in the README), so +/// wrapping them up behind an easier to use interface makes sense. +/// Descriptions of the functions are included below. +/// +/// For reference, the order of operands for memory references is: +/// (Operand), Base, Scale, Index, Displacement. +/// +//===----------------------------------------------------------------------===// +// +#ifndef LLVM_LIB_TARGET_M6800_M6800INSTRBUILDER_H +#define LLVM_LIB_TARGET_M6800_M6800INSTRBUILDER_H + +#include "llvm/ADT/SmallVector.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineMemOperand.h" +#include "llvm/CodeGen/MachineOperand.h" +#include "llvm/MC/MCInstrDesc.h" + +#include + +namespace llvm { + +static inline const MachineInstrBuilder & +addOffset(const MachineInstrBuilder &MIB, int Offset) { + return MIB.addImm(Offset); +} + +/// addRegIndirectWithDisp - This function is used to add a memory reference +/// of the form (Offset, Base), i.e., one with no scale or index, but with a +/// displacement. An example is: (4,D0). +static inline const MachineInstrBuilder & +addRegIndirectWithDisp(const MachineInstrBuilder &MIB, unsigned Reg, + bool isKill, int Offset) { + return MIB.addImm(Offset).addReg(Reg, getKillRegState(isKill)); +} + +/// addFrameReference - This function is used to add a reference to the base of +/// an abstract object on the stack frame of the current function. This +/// reference has base register as the FrameIndex offset until it is resolved. +/// This allows a constant offset to be specified as well... +static inline const MachineInstrBuilder & +addFrameReference(const MachineInstrBuilder &MIB, int FI, int Offset = 0) { + MachineInstr *MI = MIB; + MachineFunction &MF = *MI->getParent()->getParent(); + MachineFrameInfo &MFI = MF.getFrameInfo(); + const MCInstrDesc &MCID = MI->getDesc(); + auto Flags = MachineMemOperand::MONone; + if (MCID.mayLoad()) + Flags |= MachineMemOperand::MOLoad; + if (MCID.mayStore()) + Flags |= MachineMemOperand::MOStore; + MachineMemOperand *MMO = MF.getMachineMemOperand( + MachinePointerInfo::getFixedStack(MF, FI, Offset), Flags, + MFI.getObjectSize(FI), MFI.getObjectAlignment(FI)); + return MIB.addImm(Offset).addFrameIndex(FI).addMemOperand(MMO); +} + +static inline const MachineInstrBuilder & +addMemOperand(const MachineInstrBuilder &MIB, int FI, int Offset = 0) { + MachineInstr *MI = MIB; + MachineFunction &MF = *MI->getParent()->getParent(); + MachineFrameInfo &MFI = MF.getFrameInfo(); + const MCInstrDesc &MCID = MI->getDesc(); + auto Flags = MachineMemOperand::MONone; + if (MCID.mayLoad()) + Flags |= MachineMemOperand::MOLoad; + if (MCID.mayStore()) + Flags |= MachineMemOperand::MOStore; + MachineMemOperand *MMO = MF.getMachineMemOperand( + MachinePointerInfo::getFixedStack(MF, FI, Offset), Flags, + MFI.getObjectSize(FI), MFI.getObjectAlignment(FI)); + return MIB.addMemOperand(MMO); +} + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_M6800_M6800INSTRBUILDER_H Index: lib/Target/M680x0/M680x0InstrCompiler.td =================================================================== --- /dev/null +++ lib/Target/M680x0/M680x0InstrCompiler.td @@ -0,0 +1,129 @@ +//===-- M680x0InstrCompiler.td - Pseudos and Patterns ------*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file describes the various pseudo instructions used by the compiler, +/// as well as Pat patterns used during instruction selection. +/// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// ConstantPool, GlobalAddress, ExternalSymbol, and JumpTable +//===----------------------------------------------------------------------===// + +def : Pat<(i32 (MxWrapper tconstpool :$src)), (MOV32ri tconstpool :$src)>; +def : Pat<(i32 (MxWrapper tglobaladdr :$src)), (MOV32ri tglobaladdr :$src)>; +def : Pat<(i32 (MxWrapper texternalsym :$src)), (MOV32ri texternalsym :$src)>; +def : Pat<(i32 (MxWrapper tjumptable :$src)), (MOV32ri tjumptable :$src)>; +def : Pat<(i32 (MxWrapper tblockaddress :$src)), (MOV32ri tblockaddress :$src)>; + +def : Pat<(add MxDRD32:$src, (MxWrapper tconstpool:$opd)), + (ADD32ri MxDRD32:$src, tconstpool:$opd)>; +def : Pat<(add MxARD32:$src, (MxWrapper tjumptable:$opd)), + (ADD32ri MxARD32:$src, tjumptable:$opd)>; +def : Pat<(add MxARD32:$src, (MxWrapper tglobaladdr :$opd)), + (ADD32ri MxARD32:$src, tglobaladdr:$opd)>; +def : Pat<(add MxARD32:$src, (MxWrapper texternalsym:$opd)), + (ADD32ri MxARD32:$src, texternalsym:$opd)>; +def : Pat<(add MxARD32:$src, (MxWrapper tblockaddress:$opd)), + (ADD32ri MxARD32:$src, tblockaddress:$opd)>; + +def : Pat<(store (i32 (MxWrapper tglobaladdr:$src)), iPTR:$dst), + (MOV32ji MxARI32:$dst, tglobaladdr:$src)>; +def : Pat<(store (i32 (MxWrapper texternalsym:$src)), iPTR:$dst), + (MOV32ji MxARI32:$dst, texternalsym:$src)>; +def : Pat<(store (i32 (MxWrapper tblockaddress:$src)), iPTR:$dst), + (MOV32ji MxARI32:$dst, tblockaddress:$src)>; + +def : Pat<(i32 (MxWrapperPC tconstpool :$src)), (LEA32q tconstpool :$src)>; +def : Pat<(i32 (MxWrapperPC tglobaladdr :$src)), (LEA32q tglobaladdr :$src)>; +def : Pat<(i32 (MxWrapperPC texternalsym :$src)), (LEA32q texternalsym :$src)>; +def : Pat<(i32 (MxWrapperPC tjumptable :$src)), (LEA32q tjumptable :$src)>; +def : Pat<(i32 (MxWrapperPC tblockaddress :$src)), (LEA32q tblockaddress :$src)>; + + +//===----------------------------------------------------------------------===// +// Conditional Move Pseudo Instructions +// +// CMOV* - Used to implement the SELECT DAG operation. Expanded after +// instruction selection into a branch sequence. +//===----------------------------------------------------------------------===// + +let usesCustomInserter = 1, Uses = [CCR] in +class MxCMove + : MxPseudo<(outs TYPE.ROp:$dst), (ins TYPE.ROp:$t, TYPE.ROp:$f, i8imm:$cond), "", + [(set TYPE.VT:$dst, (TYPE.VT (MxCmov TYPE.VT:$t, TYPE.VT:$f, imm:$cond, CCR)))]>; + +def CMOV8d : MxCMove; +def CMOV16d : MxCMove; +def CMOV32r : MxCMove; + + +//===----------------------------------------------------------------------===// +// Calls +//===----------------------------------------------------------------------===// + +// ADJCALLSTACKDOWN/UP implicitly use/def %SP because they may be expanded into +// a stack adjustment and the codegen must know that they may modify the stack +// pointer before prolog-epilog rewriting occurs. +// Pessimistically assume ADJCALLSTACKDOWN / ADJCALLSTACKUP will become +// sub / add which can clobber CCR. +let Defs = [SP, CCR], Uses = [SP] in { + +def ADJCALLSTACKDOWN + : MxPseudo<(outs), (ins i32imm:$amt1, i32imm:$amt2), "#ADJCALLSTACKDOWN", + [(MxCallSeqStart timm:$amt1, timm:$amt2)]>; + +def ADJCALLSTACKUP + : MxPseudo<(outs), (ins i32imm:$amt1, i32imm:$amt2), "#ADJCALLSTACKUP", + [(MxCallSeqEnd timm:$amt1, timm:$amt2)]>; + +} // Defs + +//===----------------------------------------------------------------------===// +// Tail Call +//===----------------------------------------------------------------------===// + +// FIXME need to read more about TC in llvm + +// Tailcall stuff. The TCRETURN instructions execute after the epilog, so they +// can never use callee-saved registers. That is the purpose of the XR32_TC +// register classes. + +// FIXME: This is disabled for PIC mode because the global base +// register which is part of the address mode may be assigned a +// callee-saved register. +def : Pat<(MxTCRet (load MxCP_ARII:$dst), imm:$adj), + (TCRETURNj (MOV32af_TC MxARII32:$dst), imm:$adj)>, + Requires<[IsNotPIC]>; + +def : Pat<(MxTCRet AR32_TC:$dst, imm:$adj), + (TCRETURNj MxARI32_TC:$dst, imm:$adj)>; + +def : Pat<(MxTCRet (i32 tglobaladdr:$dst), imm:$adj), + (TCRETURNq MxPCD32:$dst, imm:$adj)>; + +def : Pat<(MxTCRet (i32 texternalsym:$dst), imm:$adj), + (TCRETURNq MxPCD32:$dst, imm:$adj)>; + + +//===----------------------------------------------------------------------===// +// Segmented Stack +// +// When using segmented stacks these are lowered into instructions which first +// check if the current stacklet has enough free memory. If it does, memory is +// allocated by bumping the stack pointer. Otherwise memory is allocated from +// the heap. +//===----------------------------------------------------------------------===// + +let Defs = [SP, CCR], Uses = [SP] in +let usesCustomInserter = 1 in +def SALLOCA : MxPseudo<(outs MxARD32:$dst), (ins MxARD32:$size), + "# variable sized alloca for segmented stacks", + [(set iPTR:$dst, (MxSegAlloca iPTR:$size))]>; Index: lib/Target/M680x0/M680x0InstrControl.td =================================================================== --- /dev/null +++ lib/Target/M680x0/M680x0InstrControl.td @@ -0,0 +1,312 @@ +//===-- M680x0InstrControl.td - Control Flow Instructions --*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file describes the M680x0 jump, return, call, and related instructions. +/// Here is the current status of the file: +/// +/// Machine: +/// +/// BRA [x] BSR [ ] Bcc [ ] DBcc [ ] FBcc [ ] +/// FDBcc [ ] FNOP [ ] FPn [ ] FScc [ ] FTST [ ] +/// JMP [~] JSR [x] NOP [x] RTD [!] RTR [ ] +/// RTS [x] Scc [x] TST [ ] +/// +/// Pseudo: +/// +/// RET [x] +/// TCRETURNj [x] TCRETURNq [x] +/// TAILJMPj [x] TAILJMPq [x] +/// +/// Map: +/// +/// [ ] - was not touched at all +/// [!] - requires extarnal stuff implemented +/// [~] - in progress but usable +/// [x] - done +/// +/// +/// NOTE +/// Though branch and jump instructions are using memory operands they +/// DO NOT read the jump address from memory, they just calculate EA +/// and jump there. +/// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// NOP +//===----------------------------------------------------------------------===// + +let hasSideEffects = 0 in { + def NOP : MxInst<(outs), (ins), "nop", [], MxEncFixed<0x4E71>>; +} + + +//===----------------------------------------------------------------------===// +// Conditions +//===----------------------------------------------------------------------===// + +/// CC—Carry clear GE—Greater than or equal +/// LS—Lower or same PL—Plus +/// CS—Carry set GT—Greater than +/// LT—Less than T—Always true* +/// EQ—Equal HI—Higher +/// MI—Minus VC—Overflow clear +/// F—Never true* LE—Less than or equal +/// NE—Not equal VS—Overflow set +/// +/// *Not applicable to the Bcc instructions. +def MxCCt : MxBead4Bits<0b0000>; +def MxCCf : MxBead4Bits<0b0001>; +def MxCChi : MxBead4Bits<0b0010>; +def MxCCls : MxBead4Bits<0b0011>; +def MxCCcc : MxBead4Bits<0b0100>; +def MxCCcs : MxBead4Bits<0b0101>; +def MxCCne : MxBead4Bits<0b0110>; +def MxCCeq : MxBead4Bits<0b0111>; +def MxCCvc : MxBead4Bits<0b1000>; +def MxCCvs : MxBead4Bits<0b1001>; +def MxCCpl : MxBead4Bits<0b1010>; +def MxCCmi : MxBead4Bits<0b1011>; +def MxCCge : MxBead4Bits<0b1100>; +def MxCClt : MxBead4Bits<0b1101>; +def MxCCgt : MxBead4Bits<0b1110>; +def MxCCle : MxBead4Bits<0b1111>; + +/// --------------------------------+---------+--------- +/// F E D C | B A 9 8 | 7 6 | 5 4 3 | 2 1 0 +/// --------------------------------+---------+--------- +/// 0 1 0 1 | CONDITION | 1 1 | MODE | REG +/// ---------------------------------------------------- +class MxSccEncoding + : MxEncoding, CC, MxBead4Bits<0b0101>, + EXT.Imm, EXT.B8, EXT.Scale, EXT.WL, EXT.DAReg>; + +let Uses = [CCR] in { +class MxSccR + : MxInst<(outs MxDRD8:$dst), (ins), "s"#CC#"\t$dst", + [(set i8:$dst, (MxSetCC !cast("MxCOND"#CC), CCR))], + MxSccEncoding("MxCC"#CC)>>; + +class MxSccM + : MxInst<(outs), (ins MEMOpd:$dst), "s"#CC#"\t$dst", + [(store (MxSetCC !cast("MxCOND"#CC), CCR), MEMPat:$dst)], + MxSccEncoding("MxCC"#CC)>>; +} + +foreach cc = [ "cc", "ls", "lt", "eq", "mi", "f", "ne", "ge", + "cs", "pl", "gt", "t", "hi", "vc", "le", "vs"] in { +def SET#"d8"#cc : MxSccR; +def SET#"j8"#cc : MxSccM; +def SET#"p8"#cc : MxSccM; +} + +//===----------------------------------------------------------------------===// +// Jumps +//===----------------------------------------------------------------------===// + +///------------------------------+---------+--------- +/// F E D C B A 9 8 7 6 | 5 4 3 | 2 1 0 +///------------------------------+---------+--------- +/// 0 1 0 0 1 1 1 0 1 1 | MODE | REG +///------------------------------+---------+--------- +let isBranch = 1, isTerminator = 1, isBarrier = 1, isIndirectBranch = 1 in +class MxJMP + : MxInst<(outs), (ins LOCOp:$dst), "jmp\t$dst", [(brind iPTR:$dst)], + MxEncoding, + MxBead4Bits<0b1110>, MxBead4Bits<0b0100>, + EXT.Imm, EXT.B8, EXT.Scale, EXT.WL, EXT.DAReg>>; + +def JMP32j : MxJMP; + + +// FIXME M680x0 does not allow 16 bit indirect jumps use sext operands +/* def JMP16r : MxInst<(outs), (ins M680x0_ARI16:$dst), */ +/* "jmp\t$dst", */ +/* [(brind AR16:$dst)]>; */ + +//===----------------------------------------------------------------------===// +// Branches +//===----------------------------------------------------------------------===// + +/// -------------------------------------------------- +/// F E D C | B A 9 8 | 7 6 5 4 3 2 1 0 +/// -------------------------------------------------- +/// 0 1 1 0 | CONDITION | 8-BIT DISPLACEMENT +/// -------------------------------------------------- +/// 16-BIT DISPLACEMENT IF 8-BIT DISPLACEMENT = $00 +/// -------------------------------------------------- +/// 32-BIT DISPLACEMENT IF 8-BIT DISPLACEMENT = $FF +/// -------------------------------------------------- +let isBranch = 1, isTerminator = 1, Uses = [CCR] in +class MxBcc + : MxInst<(outs), (ins TARGET:$dst), "b"#cc#"\t$dst", [], ENC>; + +foreach cc = [ "cc", "ls", "lt", "eq", "mi", "ne", "ge", + "cs", "pl", "gt", "hi", "vc", "le", "vs"] in { +def B#cc#"8" + : MxBcc, !cast("MxCC"#cc), MxBead4Bits<0x6>>>; +def B#cc#"16" + : MxBcc, MxBead4Bits<0x0>, !cast("MxCC"#cc), + MxBead4Bits<0x6>, MxBead16Imm<0>>>; +} + +foreach cc = [ "cc", "ls", "lt", "eq", "mi", "ne", "ge", + "cs", "pl", "gt", "hi", "vc", "le", "vs"] in { +def : Pat<(MxBrCond bb:$target, !cast("MxCOND"#cc), CCR), + (!cast("B"#cc#"8") MxBrTarget8:$target)>; +} + +/// ------------------------------------------------- +/// F E D C B A 9 8 | 7 6 5 4 3 2 1 0 +/// ------------------------------------------------- +/// 0 1 1 0 0 0 0 0 | 8-BIT DISPLACEMENT +/// ------------------------------------------------- +/// 16-BIT DISPLACEMENT IF 8-BIT DISPLACEMENT = $00 +/// ------------------------------------------------- +/// 32-BIT DISPLACEMENT IF 8-BIT DISPLACEMENT = $FF +/// ------------------------------------------------- +let isBranch = 1, isTerminator = 1 in +class MxBra + : MxInst<(outs), (ins TARGET:$dst), "bra\t$dst", [], ENC>; + +def BRA8 : MxBra, MxBead4Bits<0x0>, MxBead4Bits<0x6>>>; +def BRA16 : MxBra, MxBead4Bits<0x0>, + MxBead4Bits<0x0>, MxBead4Bits<0x6>, MxBead16Imm<0>>>; + +def : Pat<(br bb:$target), (BRA8 MxBrTarget8:$target)>; + + +//===----------------------------------------------------------------------===// +// Call +//===----------------------------------------------------------------------===// + +// All calls clobber the non-callee saved registers. %SP is marked as +// a use to prevent stack-pointer assignments that appear immediately +// before calls from potentially appearing dead. Uses for argument +// registers are added manually. +let Uses = [SP] in +let isCall = 1 in +///------------------------------+---------+--------- +/// F E D C B A 9 8 7 6 | 5 4 3 | 2 1 0 +///------------------------------+---------+--------- +/// 0 1 0 0 1 1 1 0 1 0 | MODE | REG +///------------------------------+---------+--------- +class MxCall + : MxInst<(outs), (ins LOCOp:$dst), "jsr\t$dst", [], + MxEncoding, MxBead4Bits<0b1110>, MxBead4Bits<0b0100>, + EXT.Imm, EXT.B8, EXT.Scale, EXT.WL, EXT.DAReg>>; + +def CALLk : MxCall; +def CALLq : MxCall; +def CALLb : MxCall; +def CALLj : MxCall; + +def : Pat<(MxCall (i32 tglobaladdr:$dst)), (CALLq tglobaladdr:$dst)>, Requires<[IsPIC]>; +def : Pat<(MxCall (i32 texternalsym:$dst)), (CALLq texternalsym:$dst)>, Requires<[IsPIC]>; +def : Pat<(MxCall (i32 imm:$dst)), (CALLq imm:$dst)>, Requires<[IsPIC]>; + +def : Pat<(MxCall (i32 tglobaladdr:$dst)), (CALLb tglobaladdr:$dst)>, Requires<[IsNotPIC]>; +def : Pat<(MxCall (i32 texternalsym:$dst)), (CALLb texternalsym:$dst)>, Requires<[IsNotPIC]>; +def : Pat<(MxCall (i32 imm:$dst)), (CALLb imm:$dst)>, Requires<[IsNotPIC]>; + +def : Pat<(MxCall iPTR:$dst), (CALLj MxARI32:$dst)>; + +//===----------------------------------------------------------------------===// +// Tail Call +//===----------------------------------------------------------------------===// + +let isCodeGenOnly = 1 in { +let Uses = [SP] in { +let isCall = 1, isTerminator = 1, isBarrier = 1 in { + +let isReturn = 1 in +def TCRETURNq : MxPseudo<(outs), (ins MxPCD32:$dst, i32imm:$adj)>; +def TAILJMPq : MxPseudo<(outs), (ins MxPCD32:$dst)>; + +// NOTE j does not mean load and jump M680x0 jmp just calculates EA and jumps +// and it is using Mem form like (An) thus j letter. +let isReturn = 1 in +def TCRETURNj : MxPseudo<(outs), (ins MxARI32_TC:$dst, i32imm:$adj)>; +def TAILJMPj : MxPseudo<(outs), (ins MxARI32_TC:$dst)>; +} +} // let Uses = [SP] +} // let isCodeGenOnly = 1 + +//===----------------------------------------------------------------------===// +// Return +//===----------------------------------------------------------------------===// + +// TODO don't forget about LINK/UNLK + +let isTerminator = 1, isReturn = 1, isBarrier = 1, hasCtrlDep = 1 in { + +def RTS : MxInst<(outs), (ins), "rts", [], MxEncFixed<0x4E75>>; + +let isCodeGenOnly = 1 in { +def RET : MxPseudo<(outs), (ins i32imm:$adj, variable_ops), "", + [(MxRet timm:$adj)]>; +} // let isCodeGenOnly = 1 + +} + + +//===----------------------------------------------------------------------===// +// SETCC_C Patterns +//===----------------------------------------------------------------------===// + +// Use subx to materialize carry bit. +let Uses = [CCR], Defs = [CCR], isPseudo = 1 in { +// FIXME: These are pseudo ops that should be replaced with Pat<> patterns. +// However, Pat<> can't replicate the destination reg into the inputs of the +// result. +def SETCS_C8d : MxPseudo<(outs MxDRD8:$dst), (ins), "", + [(set MxDRD8:$dst, (MxSetCC_C MxCONDcs, CCR))]>; +def SETCS_C16d : MxPseudo<(outs MxDRD16:$dst), (ins), "", + [(set MxDRD16:$dst, (MxSetCC_C MxCONDcs, CCR))]>; +def SETCS_C32d : MxPseudo<(outs MxXRD32:$dst), (ins), "", + [(set MxXRD32:$dst, (MxSetCC_C MxCONDcs, CCR))]>; +} // isCodeGenOnly + + +def : Pat<(i16 (anyext (i8 (MxSetCC_C MxCONDcs, CCR)))), (SETCS_C16d)>; +def : Pat<(i32 (anyext (i8 (MxSetCC_C MxCONDcs, CCR)))), (SETCS_C32d)>; + +def : Pat<(i16 (sext (i8 (MxSetCC_C MxCONDcs, CCR)))), (SETCS_C16d)>; +def : Pat<(i32 (sext (i8 (MxSetCC_C MxCONDcs, CCR)))), (SETCS_C32d)>; + +// We canonicalize 'scs' to "(and (subx reg,reg), 1)" on the hope that the and +// will be eliminated and that the subx can be extended up to a wider type. When +// this happens, it is great. However, if we are left with an 8-bit subx and an +// and, we might as well just match it as a setb. +def : Pat<(and (i8 (MxSetCC_C MxCONDcs, CCR)), 1), (SETd8cs)>; + +// (add OP, SETB) -> (addx OP, (move 0)) +def : Pat<(add (and (i8 (MxSetCC_C MxCONDcs, CCR)), 1), MxDRD8:$op), + (ADDX8dd MxDRD8:$op, (MOV8di 0))>; +def : Pat<(add (and (i32 (MxSetCC_C MxCONDcs, CCR)), 1), MxXRD32:$op), + (ADDX32dd MxDRD32:$op, (MOV32ri 0))>; + +// (sub OP, SETB) -> (subx OP, (move 0)) +def : Pat<(sub MxDRD8:$op, (and (i8 (MxSetCC_C MxCONDcs, CCR)), 1)), + (SUBX8dd MxDRD8:$op, (MOV8di 0))>; +def : Pat<(sub MxXRD32:$op, (and (i32 (MxSetCC_C MxCONDcs, CCR)), 1)), + (SUBX32dd MxDRD32:$op, (MOV32ri 0))>; + +// (sub OP, SETCC_CARRY) -> (addx OP, (move 0)) +def : Pat<(sub MxDRD8:$op, (i8 (MxSetCC_C MxCONDcs, CCR))), + (ADDX8dd MxDRD8:$op, (MOV8di 0))>; +def : Pat<(sub MxXRD32:$op, (i32 (MxSetCC_C MxCONDcs, CCR))), + (ADDX32dd MxDRD32:$op, (MOV32ri 0))>; Index: lib/Target/M680x0/M680x0InstrData.td =================================================================== --- /dev/null +++ lib/Target/M680x0/M680x0InstrData.td @@ -0,0 +1,718 @@ +//== M680x0InstrData.td - M680x0 Data Movement Instructions -*- tablegen --===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file describes the Motorola 680x0 data movement instructions which are +/// the basic means of transferring and storing addresses and data. Here is the +/// current status of the file: +/// +/// Machine: +/// +/// EXG [ ] FMOVE [ ] FSMOVE [ ] FDMOVE [ ] FMOVEM [ ] +/// LEA [~] PEA [ ] MOVE [~] MOVE16 [ ] MOVEA [ ] +/// MOVEM [ ] MOVEP [ ] MOVEQ [ ] LINK [ ] UNLK [ ] +/// +/// Pseudo: +/// +/// MOVSX [x] MOVZX [x] MOVX [x] +/// +/// Map: +/// +/// [ ] - was not touched at all +/// [!] - requires extarnal stuff implemented +/// [~] - in progress but usable +/// [x] - done +/// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// MOVE +//===----------------------------------------------------------------------===// + +/// ----------------------------------------------------- +/// F E | D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +/// ----------------------------------------------------- +/// | | DESTINATION | SOURCE +/// 0 0 | SIZE | REG | MODE | MODE | REG +/// ----------------------------------------------------- +/// +/// NOTE Move requires EA X version for direct register destination(0) +class MxMoveEncoding + : MxEncoding< + srcEA.Reg, srcEA.DA, srcEA.Mode, dstEA.DA, dstEA.Mode, dstEA.Reg, size, MxBead2Bits<0b00>, + srcExt.Imm, srcExt.B8, srcExt.Scale, srcExt.WL, srcExt.DAReg, + dstExt.Imm, dstExt.B8, dstExt.Scale, dstExt.WL, dstExt.DAReg>; + +/// MOVE has alternate size encoding +class MxMoveSize value> : MxBead2Bits; +def MxMoveSize8 : MxMoveSize<0b01>; +def MxMoveSize16 : MxMoveSize<0b11>; +def MxMoveSize32 : MxMoveSize<0b10>; + +let Defs = [CCR] in +class MxMove pattern, MxEncoding enc> + : MxInst; + +class MxMove_RR + : MxMove; + +let mayStore = 1 in { +class MxMove_MR + : MxMove; + +class MxMove_MI + : MxMove; +} // let mayStore = 1 + +class MxMove_RI + : MxMove; + + +let mayLoad = 1 in +class MxMove_RM + : MxMove>; + +multiclass MMxMove_RM { + +// FIXME tablegen defines implicitly created defs inside multiclass which +// breaks some code like letting Pattern afterwards in _TC + +// REG <- (An)+ +def NAME#REG.OOp.Letter#REG.Postfix : MxMove_RM; + +// REG <- -(An) +def NAME#REG.EOp.Letter#REG.Postfix : MxMove_RM; + +// REG <- (i,PC,Xn) +def NAME#REG.KOp.Letter#REG.Postfix : MxMove_RM; + +// REG <- (i,PC) +def NAME#REG.QOp.Letter#REG.Postfix : MxMove_RM; + +// REG <- (i,An,Xn) +def NAME#REG.FOp.Letter#REG.Postfix : MxMove_RM; + +// REG <- (i,An) +def NAME#REG.POp.Letter#REG.Postfix : MxMove_RM; + +// REG <- (ABS) +def NAME#REG.BOp.Letter#REG.Postfix : MxMove_RM; + +// REG <- (An) +def NAME#REG.JOp.Letter#REG.Postfix : MxMove_RM; +} + +let mayLoad = 1, mayStore = 1 in { +class MxMove_MM + : MxMove>; +} // let mayLoad = 1, mayStore = 1 + +multiclass MMxMove_MM { + +// MEM <- (An)+ +def NAME#TYPE.OOp.Letter#TYPE.Postfix + : MxMove_MM; + +// MEM <- -(An) +def NAME#TYPE.EOp.Letter#TYPE.Postfix + : MxMove_MM; + +// MEM <- (i,An) +def NAME#TYPE.POp.Letter#TYPE.Postfix + : MxMove_MM; + +// MEM <- (i,An,Xn) +def NAME#TYPE.FOp.Letter#TYPE.Postfix + : MxMove_MM; + +// MEM <- (i,PC,Xn) +def NAME#TYPE.KOp.Letter#TYPE.Postfix + : MxMove_MM; + +// MEM <- (i,PC) +def NAME#TYPE.QOp.Letter#TYPE.Postfix + : MxMove_MM; + +// MEM <- (ABS) +def NAME#TYPE.BOp.Letter#TYPE.Postfix + : MxMove_MM; + +// MEM <- (An) +def NAME#TYPE.JOp.Letter#TYPE.Postfix + : MxMove_MM; +} + +def MOV8dd + : MxMove_RR>; + +// M <- R +def MOV8fd : MxMove_MR>; + +def MOV8pd : MxMove_MR>; + +def MOV8ed : MxMove_MR>; + +def MOV8od : MxMove_MR>; + +def MOV8bd : MxMove_MR>; + +def MOV8jd : MxMove_MR>; + + +// R <- I +def MOV8di : MxMove_RI>; + +foreach S = [16, 32] in { + foreach D = [ "r", "a" ] in { + + foreach O = [ "r", "a" ] in { + def MOV#S#D#O : MxMove_RR< + !cast("MxType"#S#D), + !cast("MxType"#S#O), + MxMoveEncoding("MxMoveSize"#S), + !cast("MxEncEA"#D#"_1"), MxExtEmpty, + !cast("MxEncEA"#D#"_0_reflected"), MxExtEmpty>>; + } + + // M <- R + def MOV#S#"f"#D : MxMove_MR< + !cast("MxType"#S).FOp, + !cast("MxType"#S).FPat, + !cast("MxType"#S#D), + MxMoveEncoding("MxMoveSize"#S), + !cast("MxEncEA"#D#"_1"), MxExtEmpty, + MxEncEAf_0, MxExtBrief_0>>; + + def MOV#S#"p"#D : MxMove_MR< + !cast("MxType"#S).POp, + !cast("MxType"#S).PPat, + !cast("MxType"#S#D), + MxMoveEncoding("MxMoveSize"#S), + !cast("MxEncEA"#D#"_1"), MxExtEmpty, + MxEncEAp_0, MxExtI16_0>>; + + def MOV#S#"e"#D : MxMove_MR< + !cast("MxType"#S).EOp, + !cast("MxType"#S).EPat, + !cast("MxType"#S#D), + MxMoveEncoding("MxMoveSize"#S), + !cast("MxEncEA"#D#"_1"), MxExtEmpty, + MxEncEAe_0, MxExtEmpty>>; + + def MOV#S#"o"#D : MxMove_MR< + !cast("MxType"#S).OOp, + !cast("MxType"#S).OPat, + !cast("MxType"#S#D), + MxMoveEncoding("MxMoveSize"#S), + !cast("MxEncEA"#D#"_1"), MxExtEmpty, + MxEncEAo_0, MxExtEmpty>>; + + def MOV#S#"b"#D : MxMove_MR< + !cast("MxType"#S).BOp, + !cast("MxType"#S).BPat, + !cast("MxType"#S#D), + MxMoveEncoding("MxMoveSize"#S), + !cast("MxEncEA"#D#"_1"), MxExtEmpty, + MxEncEAb, MxExtI32_0>>; + + def MOV#S#"j"#D : MxMove_MR< + !cast("MxType"#S).JOp, + !cast("MxType"#S).JPat, + !cast("MxType"#S#D), + MxMoveEncoding("MxMoveSize"#S), + !cast("MxEncEA"#D#"_1"), MxExtEmpty, + MxEncEAj_0, MxExtEmpty>>; + + + // R <- I + def MOV#S#D#"i" : MxMove_RI< + !cast("MxType"#S#D), + MxMoveEncoding("MxMoveSize"#S), + MxEncEAi, !cast("MxExtI"#S#"_1"), + !cast("MxEncEA"#D#"_0_reflected"), MxExtEmpty>>; + } +} + +// M <- I +foreach S = [8, 16, 32] in { + def MOV#S#"f"#"i" : MxMove_MI< + !cast("MxType"#S).FOp, + !cast("MxType"#S).FPat, + !cast("MxType"#S), + MxMoveEncoding("MxMoveSize"#S), + MxEncEAi, !cast("MxExtI"#S#"_1"), + MxEncEAf_0, MxExtBrief_0>>; + + def MOV#S#"p"#"i" : MxMove_MI< + !cast("MxType"#S).POp, + !cast("MxType"#S).PPat, + !cast("MxType"#S), + MxMoveEncoding("MxMoveSize"#S), + MxEncEAi, !cast("MxExtI"#S#"_1"), + MxEncEAp_0, MxExtI16_0>>; + + def MOV#S#"b"#"i" : MxMove_MI< + !cast("MxType"#S).BOp, + !cast("MxType"#S).BPat, + !cast("MxType"#S), + MxMoveEncoding("MxMoveSize"#S), + MxEncEAi, !cast("MxExtI"#S#"_1"), + MxEncEAb, MxExtI32_0>>; + + def MOV#S#"j"#"i" : MxMove_MI< + !cast("MxType"#S).JOp, + !cast("MxType"#S).JPat, + !cast("MxType"#S), + MxMoveEncoding("MxMoveSize"#S), + MxEncEAi, !cast("MxExtI"#S#"_1"), + MxEncEAj_0, MxExtEmpty>>; +} + +// Store ABS(basically pointer) as Immdiate to Mem +def : Pat<(store MxType32.BPat :$src, MxType32.PPat :$dst), + (MOV32pi MxType32.POp :$dst, MxType32.IOp :$src)>; + +def : Pat<(store MxType32.BPat :$src, MxType32.FPat :$dst), + (MOV32fi MxType32.FOp :$dst, MxType32.IOp :$src)>; + +def : Pat<(store MxType32.BPat :$src, MxType32.BPat :$dst), + (MOV32bi MxType32.BOp :$dst, MxType32.IOp :$src)>; + +def : Pat<(store MxType32.BPat :$src, MxType32.JPat :$dst), + (MOV32ji MxType32.JOp :$dst, MxType32.IOp :$src)>; + +// FIXME Tablegen needs to parse complex defm names inside the loop FFS + +// R <- M +defm MOV8d : MMxMove_RM; + +defm MOV16r : MMxMove_RM; +defm MOV16a : MMxMove_RM; + +defm MOV32r : MMxMove_RM; +defm MOV32a : MMxMove_RM; + +let Pattern = [(null_frag)] in { +defm MOV16r : MMxMove_RM; +defm MOV16a : MMxMove_RM; + +defm MOV32r : MMxMove_RM; +defm MOV32a : MMxMove_RM; +} // Pattern + +// M <- M +defm MOV8p : MMxMove_MM; +defm MOV16p : MMxMove_MM; +defm MOV32p : MMxMove_MM; + +defm MOV8f : MMxMove_MM; +defm MOV16f : MMxMove_MM; +defm MOV32f : MMxMove_MM; + +defm MOV8b : MMxMove_MM; +defm MOV16b : MMxMove_MM; +defm MOV32b : MMxMove_MM; + +defm MOV8e : MMxMove_MM; +defm MOV16e : MMxMove_MM; +defm MOV32e : MMxMove_MM; + +defm MOV8o : MMxMove_MM; +defm MOV16o : MMxMove_MM; +defm MOV32o : MMxMove_MM; + +defm MOV8j : MMxMove_MM; +defm MOV16j : MMxMove_MM; +defm MOV32j : MMxMove_MM; + +//===----------------------------------------------------------------------===// +// MOVEM +// +// The mask is already pre-processed by the save/restore spill hook +//===----------------------------------------------------------------------===// + +// Direction +def MxMOVEM_MR : MxBead1Bit<0>; +def MxMOVEM_RM : MxBead1Bit<1>; + +// Size +def MxMOVEM_W : MxBead1Bit<0>; +def MxMOVEM_L : MxBead1Bit<1>; + +/// ---------------+-------------+-------------+--------- +/// F E D C B | A | 9 8 7 | 6 | 5 4 3 | 2 1 0 +/// ---------------+---+---------+---+---------+--------- +/// 0 1 0 0 1 | D | 0 0 1 | S | MODE | REG +/// ---------------+---+---------+---+---------+--------- +/// REGISTER LIST MASK +/// ----------------------------------------------------- +/// D - direction(RM,MR) +/// S - size(W,L) +class MxMOVEMEncoding + : MxEncoding, DIR, + MxBead1Bit<1>, MxBead4Bits<0b0100>, IMM, + EXT.Imm, EXT.B8, EXT.Scale, EXT.WL, EXT.DAReg>; + +let mayStore = 1 in +class MxMOVEM_MR + : MxInst<(outs), (ins MEMOp:$dst, MxMoveMask:$mask), + "movem."#TYPE.Prefix#"\t$mask, $dst", [], + MxMOVEMEncoding>>; + +let mayLoad = 1 in +class MxMOVEM_RM + : MxInst<(outs), (ins MxMoveMask:$mask, MEMOp:$src), + "movem."#TYPE.Prefix#"\t$src, $mask", [], + MxMOVEMEncoding>>; + +def MOVM32jm : MxMOVEM_MR; +def MOVM32pm : MxMOVEM_MR; + +def MOVM32mj : MxMOVEM_RM; +def MOVM32mp : MxMOVEM_RM; + +// Pseudo versions. These a required by virtual register spill/restore since +// the mask requires real register to encode. These instruction will be expanded +// into real MOVEM after RA finishes. +let mayStore = 1 in +class MxMOVEM_MR_Pseudo + : MxPseudo<(outs), (ins MEMOp:$dst, TYPE.ROp:$reg)>; +let mayLoad = 1 in +class MxMOVEM_RM_Pseudo + : MxPseudo<(outs TYPE.ROp:$dst), (ins MEMOp:$src)>; + +// Mem <- Reg +def MOVM8jm_P : MxMOVEM_MR_Pseudo; +def MOVM16jm_P : MxMOVEM_MR_Pseudo; +def MOVM32jm_P : MxMOVEM_MR_Pseudo; + +def MOVM8pm_P : MxMOVEM_MR_Pseudo; +def MOVM16pm_P : MxMOVEM_MR_Pseudo; +def MOVM32pm_P : MxMOVEM_MR_Pseudo; + +// Reg <- Mem +def MOVM8mj_P : MxMOVEM_RM_Pseudo; +def MOVM16mj_P : MxMOVEM_RM_Pseudo; +def MOVM32mj_P : MxMOVEM_RM_Pseudo; + +def MOVM8mp_P : MxMOVEM_RM_Pseudo; +def MOVM16mp_P : MxMOVEM_RM_Pseudo; +def MOVM32mp_P : MxMOVEM_RM_Pseudo; + + +//===----------------------------------------------------------------------===// +// MOVE to/from SR/CCR +// +// A special care must be taken working with to/from CCR since it is basically +// word-size SR register truncated for user mode thus it only supports word-size +// instructions. Plus the original M68000 does not support moves from CCR. So in +// order to use CCR effectively one MUST use proper byte-size pseudo instructi- +// ons that will be resolved sometime after RA pass. +//===----------------------------------------------------------------------===// + +/// -------------------------------------------------- +/// F E D C B A 9 8 7 6 | 5 4 3 | 2 1 0 +/// -------------------------------------------------- +/// | EFFECTIVE ADDRESS +/// 0 1 0 0 0 1 0 0 1 1 | MODE | REG +/// -------------------------------------------------- +let Defs = [CCR] in +class MxMoveToCCR + : MxInst<(outs CCRC:$dst), INS, "move.w\t$src, $dst", [], + MxEncoding, MxBead4Bits<0b0001>, MxBead2Bits<0b01>, + EXT.Imm, EXT.B8, EXT.Scale, EXT.WL, EXT.DAReg>>; + +class MxMoveToCCRPseudo : MxPseudo<(outs CCRC:$dst), INS>; + +let mayLoad = 1 in { +def MOV16cp : MxMoveToCCR<(ins MxType16d.POp:$src), MxEncEAp_1, MxExtI16_1>; +def MOV8cp : MxMoveToCCRPseudo<(ins MxType8d.POp:$src)>; +} // let mayLoad = 1 + +def MOV16cd : MxMoveToCCR<(ins MxType16d.ROp:$src), MxEncEAd_1, MxExtEmpty>; +def MOV8cd : MxMoveToCCRPseudo<(ins MxType8d.ROp:$src)>; + +/// Move from CCR +/// -------------------------------------------------- +/// F E D C B A 9 8 7 6 | 5 4 3 | 2 1 0 +/// -------------------------------------------------- +/// | EFFECTIVE ADDRESS +/// 0 1 0 0 0 0 1 0 1 1 | MODE | REG +/// -------------------------------------------------- +let Uses = [CCR] in +class MxMoveFromCCR + : MxInst, MxBead4Bits<0b0000>, MxBead2Bits<0b01>, + EXT.Imm, EXT.B8, EXT.Scale, EXT.WL, EXT.DAReg>>, + Requires<[ IsM68010 ]>; + +class MxMoveFromCCRPseudo : MxPseudo<(outs), INS>; + +let mayStore = 1 in { +def MOV16pc + : MxMoveFromCCR<(outs), (ins MxType16d.POp:$dst, CCRC:$src), MxEncEAp_0, MxExtI16_0>; +def MOV8pc : MxMoveFromCCRPseudo<(ins MxType8d.POp:$dst, CCRC:$src)>; +} // let mayLoad = 1 + +def MOV16dc + : MxMoveFromCCR<(outs MxType16d.ROp:$dst), (ins CCRC:$src), MxEncEAd_0, MxExtEmpty>; + +def MOV8dc : MxMoveFromCCRPseudo<(ins MxType8d.ROp:$dst, CCRC:$src)>; + + +//===----------------------------------------------------------------------===// +// LEA +//===----------------------------------------------------------------------===// + +/// ---------------------------------------------------- +/// F E D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +/// ---------------------------------------------------- +/// 0 1 0 0 | DST REG | 1 1 1 | MODE | REG +/// ---------------------------------------------------- +class MxLEA + : MxInst<(outs MxARD32:$dst), (ins SRCOpd:$src), + "lea\t$src, $dst", [(set i32:$dst, SRCPat:$src)], + MxEncoding, MxBeadReg<0>, MxBead4Bits<0x4>, + EXT.Imm, EXT.B8, EXT.Scale, EXT.WL, EXT.DAReg>>; + +def LEA32p : MxLEA; +def LEA32f : MxLEA; +def LEA32q : MxLEA; +def LEA32b : MxLEA; + + +//===----------------------------------------------------------------------===// +// Pseudos +//===----------------------------------------------------------------------===// + +/// Pushe/Pop to/from SP for simplicity +let Uses = [SP], Defs = [SP], hasSideEffects = 0 in { + +// SP <- SP - ; (SP) <- Dn +let mayStore = 1 in { +def PUSH8d : MxPseudo<(outs), (ins DR8:$reg)>; +def PUSH16d : MxPseudo<(outs), (ins DR16:$reg)>; +def PUSH32r : MxPseudo<(outs), (ins XR32:$reg)>; +} // let mayStore = 1 + +// Dn <- (SP); SP <- SP + +let mayLoad = 1 in { +def POP8d : MxPseudo<(outs DR8:$reg), (ins)>; +def POP16d : MxPseudo<(outs DR16:$reg), (ins)>; +def POP32r : MxPseudo<(outs XR32:$reg), (ins)>; +} // let mayLoad = 1 + +} // let Uses/Defs = [SP], hasSideEffects = 0 + + +let Defs = [CCR] in { +class MxPseudoMove_RR PAT = []> + : MxPseudo<(outs DST.ROp:$dst), (ins SRC.ROp:$src), "", PAT>; + +class MxPseudoMove_RM PAT = []> + : MxPseudo<(outs DST.ROp:$dst), (ins SRCOpd:$src), "", PAT>; +} + +/// This group of Pseudos is analogues to the real x86 extending moves, but +/// since M680x0 does not have those we need to emulate. These instructions +/// will be expanded right after RA completed because we need to know precisely +/// what registers are allocated for the operands and if they overlap we just +/// extend the value if the registers are completely different we need to move +/// first. +foreach EXT = ["S", "Z"] in { + let hasSideEffects = 0 in { + + def MOV#EXT#Xd16d8 : MxPseudoMove_RR; + def MOV#EXT#Xd32d8 : MxPseudoMove_RR; + def MOV#EXT#Xd32d16 : MxPseudoMove_RR; + + let mayLoad = 1 in { + + def MOV#EXT#Xd16j8 : MxPseudoMove_RM; + def MOV#EXT#Xd32j8 : MxPseudoMove_RM; + def MOV#EXT#Xd32j16 : MxPseudoMove_RM; + + def MOV#EXT#Xd16p8 : MxPseudoMove_RM; + def MOV#EXT#Xd32p8 : MxPseudoMove_RM; + def MOV#EXT#Xd32p16 : MxPseudoMove_RM; + + def MOV#EXT#Xd16f8 : MxPseudoMove_RM; + def MOV#EXT#Xd32f8 : MxPseudoMove_RM; + def MOV#EXT#Xd32f16 : MxPseudoMove_RM; + + } + } +} + +/// This group of instructions is similar to the group above but DOES NOT do +/// any value extension, they just load a smaller register into the lower part +/// of another register if operands' real registers are different or does +/// nothing if they are the same. +def MOVXd16d8 : MxPseudoMove_RR; +def MOVXd32d8 : MxPseudoMove_RR; +def MOVXd32d16 : MxPseudoMove_RR; + +//===----------------------------------------------------------------------===// +// Extend/Truncate Patterns +//===----------------------------------------------------------------------===// + +// i16 <- sext i8 +def: Pat<(i16 (sext i8:$src)), + (EXTRACT_SUBREG (MOVSXd32d8 MxDRD8:$src), MxSubRegIndex16Lo)>; +def: Pat<(MxSExtLoadi16i8 MxCP_ARI:$src), + (EXTRACT_SUBREG (MOVSXd32j8 MxARI8:$src), MxSubRegIndex16Lo)>; +def: Pat<(MxSExtLoadi16i8 MxCP_ARID:$src), + (EXTRACT_SUBREG (MOVSXd32p8 MxARID8:$src), MxSubRegIndex16Lo)>; +def: Pat<(MxSExtLoadi16i8 MxCP_ARII:$src), + (EXTRACT_SUBREG (MOVSXd32f8 MxARII8:$src), MxSubRegIndex16Lo)>; + +// i32 <- sext i8 +def: Pat<(i32 (sext i8:$src)), (MOVSXd32d8 MxDRD8:$src)>; +def: Pat<(MxSExtLoadi32i8 MxCP_ARI :$src), (MOVSXd32j8 MxARI8 :$src)>; +def: Pat<(MxSExtLoadi32i8 MxCP_ARID:$src), (MOVSXd32p8 MxARID8:$src)>; +def: Pat<(MxSExtLoadi32i8 MxCP_ARII:$src), (MOVSXd32f8 MxARII8:$src)>; + +// i32 <- sext i16 +def: Pat<(i32 (sext i16:$src)), (MOVSXd32d16 MxDRD16:$src)>; +def: Pat<(MxSExtLoadi32i16 MxCP_ARI :$src), (MOVSXd32j16 MxARI16 :$src)>; +def: Pat<(MxSExtLoadi32i16 MxCP_ARID:$src), (MOVSXd32p16 MxARID16:$src)>; +def: Pat<(MxSExtLoadi32i16 MxCP_ARII:$src), (MOVSXd32f16 MxARII16:$src)>; + +// i16 <- zext i8 +def: Pat<(i16 (zext i8:$src)), + (EXTRACT_SUBREG (MOVZXd32d8 MxDRD8:$src), MxSubRegIndex16Lo)>; +def: Pat<(MxZExtLoadi16i8 MxCP_ARI:$src), + (EXTRACT_SUBREG (MOVZXd32j8 MxARI8:$src), MxSubRegIndex16Lo)>; +def: Pat<(MxZExtLoadi16i8 MxCP_ARID:$src), + (EXTRACT_SUBREG (MOVZXd32p8 MxARID8:$src), MxSubRegIndex16Lo)>; +def: Pat<(MxZExtLoadi16i8 MxCP_ARII:$src), + (EXTRACT_SUBREG (MOVZXd32f8 MxARII8:$src), MxSubRegIndex16Lo)>; + +// i32 <- zext i8 +def: Pat<(i32 (zext i8:$src)), (MOVZXd32d8 MxDRD8:$src)>; +def: Pat<(MxZExtLoadi32i8 MxCP_ARI :$src), (MOVZXd32j8 MxARI8 :$src)>; +def: Pat<(MxZExtLoadi32i8 MxCP_ARID:$src), (MOVZXd32p8 MxARID8:$src)>; +def: Pat<(MxZExtLoadi32i8 MxCP_ARII:$src), (MOVZXd32f8 MxARII8:$src)>; + +// i32 <- zext i16 +def: Pat<(i32 (zext i16:$src)), (MOVZXd32d16 MxDRD16:$src)>; +def: Pat<(MxZExtLoadi32i16 MxCP_ARI :$src), (MOVZXd32j16 MxARI16 :$src)>; +def: Pat<(MxZExtLoadi32i16 MxCP_ARID:$src), (MOVZXd32p16 MxARID16:$src)>; +def: Pat<(MxZExtLoadi32i16 MxCP_ARII:$src), (MOVZXd32f16 MxARII16:$src)>; + +// i16 <- anyext i8 +def: Pat<(i16 (anyext i8:$src)), + (EXTRACT_SUBREG (MOVZXd32d8 MxDRD8:$src), MxSubRegIndex16Lo)>; +def: Pat<(MxExtLoadi16i8 MxCP_ARI:$src), + (EXTRACT_SUBREG (MOVZXd32j8 MxARI8:$src), MxSubRegIndex16Lo)>; +def: Pat<(MxExtLoadi16i8 MxCP_ARID:$src), + (EXTRACT_SUBREG (MOVZXd32p8 MxARID8:$src), MxSubRegIndex16Lo)>; +def: Pat<(MxExtLoadi16i8 MxCP_ARII:$src), + (EXTRACT_SUBREG (MOVZXd32f8 MxARII8:$src), MxSubRegIndex16Lo)>; + +// i32 <- anyext i8 +def: Pat<(i32 (anyext i8:$src)), (MOVZXd32d8 MxDRD8:$src)>; +def: Pat<(MxExtLoadi32i8 MxCP_ARI :$src), (MOVZXd32j8 MxARI8 :$src)>; +def: Pat<(MxExtLoadi32i8 MxCP_ARID:$src), (MOVZXd32p8 MxARID8:$src)>; +def: Pat<(MxExtLoadi32i8 MxCP_ARII:$src), (MOVZXd32f8 MxARII8:$src)>; + +// i32 <- anyext i16 +def: Pat<(i32 (anyext i16:$src)), (MOVZXd32d16 MxDRD16:$src)>; +def: Pat<(MxExtLoadi32i16 MxCP_ARI :$src), (MOVZXd32j16 MxARI16 :$src)>; +def: Pat<(MxExtLoadi32i16 MxCP_ARID:$src), (MOVZXd32p16 MxARID16:$src)>; +def: Pat<(MxExtLoadi32i16 MxCP_ARII:$src), (MOVZXd32f16 MxARII16:$src)>; + +// trunc patterns +def : Pat<(i16 (trunc i32:$src)), + (EXTRACT_SUBREG MxXRD32:$src, MxSubRegIndex16Lo)>; +def : Pat<(i8 (trunc i32:$src)), + (EXTRACT_SUBREG MxXRD32:$src, MxSubRegIndex8Lo)>; +def : Pat<(i8 (trunc i16:$src)), + (EXTRACT_SUBREG MxXRD16:$src, MxSubRegIndex8Lo)>; Index: lib/Target/M680x0/M680x0InstrFormats.td =================================================================== --- /dev/null +++ lib/Target/M680x0/M680x0InstrFormats.td @@ -0,0 +1,371 @@ +//=== M680x0InstrFormats.td - M680x0 Instruction Formats ---*- tablegen -*-===// +// 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 M680x0 instruction formats. +/// +/// Since M680x0 has quite a lot memory addressing modes there are more +/// instruction prefixes than just i, r and m: +/// TSF Since Form Letter Description +/// 00 M68000 Dn or An r any register +/// 01 M68000 Dn d data register direct +/// 02 M68000 An a address register direct +/// 03 M68000 (An) j address register indirect +/// 04 M68000 (An)+ o address register indirect with postincrement +/// 05 M68000 -(An) e address register indirect with predecrement +/// 06 M68000 (i,An) p address register indirect with displacement +/// 10 M68000 (i,An,Xn.L) f address register indirect with index and scale = 1 +/// 07 M68000 (i,An,Xn.W) F address register indirect with index and scale = 1 +/// 12 M68020 (i,An,Xn.L,SCALE) g address register indirect with index +/// 11 M68020 (i,An,Xn.W,SCALE) G address register indirect with index +/// 14 M68020 ([bd,An],Xn.L,SCALE,od) u memory indirect postindexed mode +/// 13 M68020 ([bd,An],Xn.W,SCALE,od) U memory indirect postindexed mode +/// 16 M68020 ([bd,An,Xn.L,SCALE],od) v memory indirect preindexed mode +/// 15 M68020 ([bd,An,Xn.W,SCALE],od) V memory indirect preindexed mode +/// 20 M68000 abs.L b absolute long address +/// 17 M68000 abs.W B absolute short address +/// 21 M68000 (i,PC) q program counter with displacement +/// 23 M68000 (i,PC,Xn.L) k program counter with index and scale = 1 +/// 22 M68000 (i,PC,Xn.W) K program counter with index and scale = 1 +/// 25 M68020 (i,PC,Xn.L,SCALE) l program counter with index +/// 24 M68020 (i,PC,Xn.W,SCALE) L program counter with index +/// 27 M68020 ([bd,PC],Xn.L,SCALE,od) x program counter memory indirect postindexed mode +/// 26 M68020 ([bd,PC],Xn.W,SCALE,od) X program counter memory indirect postindexed mode +/// 31 M68020 ([bd,PC,Xn.L,SCALE],od) y program counter memory indirect preindexed mode +/// 30 M68020 ([bd,PC,Xn.W,SCALE],od) Y program counter memory indirect preindexed mode +/// 32 M68000 #immediate i immediate data +/// +/// NOTE that long form is always lowercase, word variants are capitalized +/// +/// Operand can be quilified with size where appropriate to force a particular +/// instruction encoding, e.g.: +/// (i8,An,Xn.W) f8 1 extension word +/// (i16,An,Xn.W) f16 2 extension words +/// (i32,An,Xn.W) f32 3 extension words +/// +/// Form without size qualifier will adapt to operand size automatically, e.g.: +/// (i,An,Xn.W) f 1, 2 or 3 extension words +/// +/// Some forms alreay imply a particular size of their operands, e.g.: +/// (i,An) p 1 extension word and i is 16bit +/// +/// Operand order follows x86 Intel order(destination before source), e.g.: +/// MOV8df MOVE (4,A0,D0), D1 +/// +/// Number after instruction mnemonics determines the size of the data +/// +//===----------------------------------------------------------------------===// + +/// ??? Is it possible to use this stuff for disassembling? +/// NOTE 1: In case of conditional beads(DA, DAReg), cond part is able to +/// consume any bit, though a more general instructions must be chosen, e.g. +/// d -> r, a -> r + +//===----------------------------------------------------------------------===// +// Encoding primitives +//===----------------------------------------------------------------------===// + +class MxBead type, bit b4 = 0, bit b5 = 0, bit b6 = 0, bit b7 = 0> { + bits<8> Value = 0b00000000; + let Value{3-0} = type; + let Value{4} = b4; + let Value{5} = b5; + let Value{6} = b6; + let Value{7} = b7; +} + +/// System beads, allow to control beading flow +def MxBeadTerm : MxBead<0x0, 0, 0, 0, 0>; +def MxBeadIgnore : MxBead<0x0, 1, 0, 0, 0>; + +/// Add plain bit to the instruciton +class MxBead1Bit b> : MxBead<0x1, b>; +class MxBead2Bits b> : MxBead<0x2, b{0}, b{1}>; +class MxBead3Bits b> : MxBead<0x3, b{0}, b{1}, b{2}>; +class MxBead4Bits b> : MxBead<0x4, b{0}, b{1}, b{2}, b{3}>; + +/// bits<3> o - operand number +/// bit a - use alternative, used to select index register or +/// outer dispacement/immediate +/// suffix NP means non-padded +class MxBeadDAReg o, bit a = 0> : MxBead<0x5, o{0}, o{1}, o{2}, a>; +class MxBeadDA o, bit a = 0> : MxBead<0x6, o{0}, o{1}, o{2}, a>; +class MxBeadReg o, bit a = 0> : MxBead<0x7, o{0}, o{1}, o{2}, a>; +class MxBead8Disp o, bit a = 0> : MxBead<0x8, o{0}, o{1}, o{2}, a>; + +/// Add Immediate to the instruction. 8-bit version is padded with zeros to fit +/// the word. +class MxBead8Imm o, bit a = 0> : MxBead<0x9, o{0}, o{1}, o{2}, a>; +class MxBead16Imm o, bit a = 0> : MxBead<0xA, o{0}, o{1}, o{2}, a>; +class MxBead32Imm o, bit a = 0> : MxBead<0xB, o{0}, o{1}, o{2}, a>; + +/// Encodes an immediate 0-7(alt. 1-8) into 3 bit field +class MxBead3Imm o, bit a = 0> : MxBead<0xC, o{0}, o{1}, o{2}, a>; + + +class MxEncoding { + bits <192> Value; + let Value{7-0} = n0.Value; + let Value{15-8} = n1.Value; + let Value{23-16} = n2.Value; + let Value{31-24} = n3.Value; + let Value{39-32} = n4.Value; + let Value{47-40} = n5.Value; + let Value{55-48} = n6.Value; + let Value{63-56} = n7.Value; + let Value{71-64} = n8.Value; + let Value{79-72} = n9.Value; + let Value{87-80} = n10.Value; + let Value{95-88} = n11.Value; + let Value{103-96} = n12.Value; + let Value{111-104} = n13.Value; + let Value{119-112} = n14.Value; + let Value{127-120} = n15.Value; + let Value{135-128} = n16.Value; + let Value{143-136} = n17.Value; + let Value{151-144} = n18.Value; + let Value{159-152} = n19.Value; + let Value{167-160} = n20.Value; + let Value{175-168} = n21.Value; + let Value{183-176} = n22.Value; + let Value{191-184} = n23.Value; +} + +class MxEncFixed value> : MxEncoding { + let Value{7-0} = MxBead4Bits.Value; + let Value{15-8} = MxBead4Bits.Value; + let Value{23-16} = MxBead4Bits.Value; + let Value{31-24} = MxBead4Bits.Value; +} + +//===----------------------------------------------------------------------===// +// Encoding composites +// +// These must be lowered to MxEncoding by instr specific wrappers +// +// HERE BE DRAGONS... +//===----------------------------------------------------------------------===// + +class MxEncByte value> : MxEncoding { + MxBead4Bits LO = MxBead4Bits; + MxBead4Bits HI = MxBead4Bits; +} + +def MxEncEmpty : MxEncoding; + + +/// M680x0 Standard Effective Address layout: +/// +/// :-------------------: +/// | 5 4 3 | 2 1 0 | +/// | mode | reg | +/// :-------------------: +/// +/// If the EA is a direct register use the 3 bit is known to distinguish +/// between Data and Address register types and bits 5 and 4 are 0. This allows +/// us to encode EA as MxBead2Bits<0b00> and MxBeadDAReg. +/// +/// +/// But MOVE instruction uses reversed layout for destination EA: +/// +/// :-------------------: +/// | 5 4 3 | 2 1 0 | +/// | reg | mode | +/// :-------------------: +/// +/// And this complicates things a bit because the DA bit is now separated from +/// the register and we have to encode those separately using MxBeadDA +/// +class MxEncEA { + MxBead Reg = reg; + MxBead Mode = mode; + MxBead DA = da; +} + +// NOTE: freaking tablegen... +def MxEncEAr_0: MxEncEA, MxBead2Bits<0b00>>; +def MxEncEAd_0: MxEncEA, MxBead2Bits<0b00>, MxBead1Bit<0>>; +def MxEncEAa_0: MxEncEA, MxBead2Bits<0b00>, MxBead1Bit<1>>; +def MxEncEAj_0: MxEncEA, MxBead2Bits<0b01>, MxBead1Bit<0>>; +def MxEncEAo_0: MxEncEA, MxBead2Bits<0b01>, MxBead1Bit<1>>; +def MxEncEAe_0: MxEncEA, MxBead2Bits<0b10>, MxBead1Bit<0>>; +def MxEncEAp_0: MxEncEA, MxBead2Bits<0b10>, MxBead1Bit<1>>; +def MxEncEAf_0: MxEncEA, MxBead2Bits<0b11>, MxBead1Bit<0>>; + +def MxEncEAa_0_reflected : MxEncEA, MxBead3Bits<0b001>>; +def MxEncEAr_0_reflected : MxEncEA, MxBead2Bits<0b00>, MxBeadDA<0>>; + +def MxEncEAr_1: MxEncEA, MxBead2Bits<0b00>>; +def MxEncEAd_1: MxEncEA, MxBead2Bits<0b00>, MxBead1Bit<0>>; +def MxEncEAa_1: MxEncEA, MxBead2Bits<0b00>, MxBead1Bit<1>>; +def MxEncEAj_1: MxEncEA, MxBead2Bits<0b01>, MxBead1Bit<0>>; +def MxEncEAo_1: MxEncEA, MxBead2Bits<0b01>, MxBead1Bit<1>>; +def MxEncEAe_1: MxEncEA, MxBead2Bits<0b10>, MxBead1Bit<0>>; +def MxEncEAp_1: MxEncEA, MxBead2Bits<0b10>, MxBead1Bit<1>>; +def MxEncEAf_1: MxEncEA, MxBead2Bits<0b11>, MxBead1Bit<0>>; + +def MxEncEAr_2: MxEncEA, MxBead2Bits<0b00>>; +def MxEncEAd_2: MxEncEA, MxBead2Bits<0b00>, MxBead1Bit<0>>; +def MxEncEAa_2: MxEncEA, MxBead2Bits<0b00>, MxBead1Bit<1>>; +def MxEncEAj_2: MxEncEA, MxBead2Bits<0b01>, MxBead1Bit<0>>; +def MxEncEAo_2: MxEncEA, MxBead2Bits<0b01>, MxBead1Bit<1>>; +def MxEncEAe_2: MxEncEA, MxBead2Bits<0b10>, MxBead1Bit<0>>; +def MxEncEAp_2: MxEncEA, MxBead2Bits<0b10>, MxBead1Bit<1>>; +def MxEncEAf_2: MxEncEA, MxBead2Bits<0b11>, MxBead1Bit<0>>; + +def MxEncEAb : MxEncEA, MxBead2Bits<0b11>, MxBead1Bit<1>>; +def MxEncEAq : MxEncEA, MxBead2Bits<0b11>, MxBead1Bit<1>>; +def MxEncEAk : MxEncEA, MxBead2Bits<0b11>, MxBead1Bit<1>>; +def MxEncEAi : MxEncEA, MxBead2Bits<0b11>, MxBead1Bit<1>>; + +// Allows you to specify each bit of opcode +class MxEncOpMode { + MxBead B0 = b0; + MxBead B1 = b1; + MxBead B2 = b2; +} + +// op EA, Dn +def MxOpMode8dEA : MxEncOpMode>; +def MxOpMode16dEA : MxEncOpMode>; +def MxOpMode32dEA : MxEncOpMode>; + +// op EA, An +def MxOpMode16aEA : MxEncOpMode>; +def MxOpMode32aEA : MxEncOpMode>; + +// op EA, Rn +// As you might noticed this guy is special... Since M680x0 differentiates +// between Data and Address registers we required to use different OPMODE codes +// for Address registers DST operands. One way of dealing with it is to use +// separate tablegen instructions, but in this case it would force Register +// Allocator to use specific Register Classes and eventually will lead to +// superfluous moves. Another approach is to use reg-variadic encoding which will +// change OPMODE base on Register Class used. Luckily, all the bits that differ go +// from 0 to 1 and can be encoded with MxBeadDA. +// Basically, if the register used is of Data type these encodings will be +// the same as MxOpMode{16,32}dEA above and used with regular instructions(e.g. ADD, +// SUB), but if the register is of Address type the appropriate bits will flip and +// the instructions become of *A type(e.g ADDA, SUBA). +def MxOpMode16rEA : MxEncOpMode, MxBeadDA<0>, MxBead1Bit<0>>; +def MxOpMode32rEA : MxEncOpMode, MxBead1Bit<1>, MxBeadDA<0>>; + +// op Dn, EA +def MxOpMode8EAd : MxEncOpMode>; +def MxOpMode16EAd : MxEncOpMode>; +def MxOpMode32EAd : MxEncOpMode>; + + +// Represents two types of extension word: +// - Imm extension word +// - Brief extension word +class MxEncExt { + MxBead Imm = imm; + MxBead B8 = b8; + MxBead Scale = scale; + MxBead WL = wl; + MxBead DAReg = daReg; +} + +def MxExtEmpty : MxEncExt; + +// These handle encoding of displacement fields, absolute addresses and +// immediate values, since encoding for these categories is mainly the same, +// with exception of some weird immediates. +def MxExtI8_0 : MxEncExt>; +def MxExtI16_0 : MxEncExt>; +def MxExtI32_0 : MxEncExt>; + +def MxExtI8_1 : MxEncExt>; +def MxExtI16_1 : MxEncExt>; +def MxExtI32_1 : MxEncExt>; + +def MxExtI8_2 : MxEncExt>; +def MxExtI16_2 : MxEncExt>; +def MxExtI32_2 : MxEncExt>; + +/* class MxExtBrief opN, bits<1> wl = 0b1> */ +/* : MxEncExt, MxBead1Bit<0b0>, */ +/* MxBead2Bits<0b00>, MxBead1Bit, */ +/* MxBeadDAReg>; */ + +// NOTE They are all using Long Xn +def MxExtBrief_0 : MxEncExt, MxBead1Bit<0b0>, + MxBead2Bits<0b00>, MxBead1Bit<1>, + MxBeadDAReg<0, 1>>; + +def MxExtBrief_1 : MxEncExt, MxBead1Bit<0b0>, + MxBead2Bits<0b00>, MxBead1Bit<1>, + MxBeadDAReg<1, 1>>; + +def MxExtBrief_2 : MxEncExt, MxBead1Bit<0b0>, + MxBead2Bits<0b00>, MxBead1Bit<1>, + MxBeadDAReg<2, 1>>; + +def MxExtBrief_3 : MxEncExt, MxBead1Bit<0b0>, + MxBead2Bits<0b00>, MxBead1Bit<1>, + MxBeadDAReg<3, 1>>; + +def MxExtBrief_4 : MxEncExt, MxBead1Bit<0b0>, + MxBead2Bits<0b00>, MxBead1Bit<1>, + MxBeadDAReg<4, 1>>; + +class MxEncSize value> : MxBead2Bits; +def MxEncSize8 : MxEncSize<0b00>; +def MxEncSize16 : MxEncSize<0b01>; +def MxEncSize32 : MxEncSize<0b10>; +def MxEncSize64 : MxEncSize<0b11>; + +// M680x0 INSTRUCTION. Most instructions specify the location of an operand by +// using the effective address field in the operation word. The effective address +// is composed of two 3-bit fields: the mode field and the register field. The +// value in the mode field selects the different address modes. The register +// field contains the number of a register. The effective address field may +// require additional information to fully specify the operand. This additional +// information, called the effective address extension, is contained in the +// following word or words and is considered part of the instruction. The +// effective address modes are grouped into three categories: register direct, +// memory addressing, and special. +class MxInst pattern = [], + MxEncoding beads = MxEncEmpty, + InstrItinClass itin = NoItinerary> + : Instruction { + let Namespace = "M680x0"; + let OutOperandList = outs; + let InOperandList = ins; + let AsmString = asmStr; + let Pattern = pattern; + let Itinerary = itin; + + // Byte stream + field bits<192> Beads = beads.Value; + + // Number of bytes + let Size = 0; +} + +// M680x0 PSEUDO INSTRUCTION +class MxPseudo pattern = []> + : MxInst { + let isPseudo = 1; +} Index: lib/Target/M680x0/M680x0InstrInfo.h =================================================================== --- /dev/null +++ lib/Target/M680x0/M680x0InstrInfo.h @@ -0,0 +1,293 @@ +//===-- M680x0InstrInfo.h - M680x0 Instruction Information ------*- 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 the M680x0 implementation of the TargetInstrInfo class. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_M680X0_M680X0INSTRINFO_H +#define LLVM_LIB_TARGET_M680X0_M680X0INSTRINFO_H + +#include "M680x0.h" +#include "M680x0RegisterInfo.h" + +#include "MCTargetDesc/M680x0BaseInfo.h" + +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/TargetInstrInfo.h" + +#define GET_INSTRINFO_HEADER +#include "M680x0GenInstrInfo.inc" + +namespace llvm { + +class M680x0Subtarget; + +namespace M680x0 { + +// These MUST be kept in sync with codes definitions in M680x0InstrInfo.td +enum CondCode { + COND_T = 0, // True + COND_F = 1, // False + COND_HI = 2, // High + COND_LS = 3, // Less or Same + COND_CC = 4, // Carry Clear + COND_CS = 5, // Carry Set + COND_NE = 6, // Not Equal + COND_EQ = 7, // Equal + COND_VC = 8, // Overflow Clear + COND_VS = 9, // Overflow Set + COND_PL = 10, // Plus + COND_MI = 11, // Minus + COND_GE = 12, // Greater or Equal + COND_LT = 13, // Less Than + COND_GT = 14, // Greater Than + COND_LE = 15, // Less or Equal + LAST_VALID_COND = COND_LE, + COND_INVALID +}; + +static inline M680x0::CondCode GetOppositeBranchCondition(M680x0::CondCode CC) { + switch (CC) { + default: + llvm_unreachable("Illegal condition code!"); + case M680x0::COND_T: + return M680x0::COND_F; + case M680x0::COND_F: + return M680x0::COND_T; + case M680x0::COND_HI: + return M680x0::COND_LS; + case M680x0::COND_LS: + return M680x0::COND_HI; + case M680x0::COND_CC: + return M680x0::COND_CS; + case M680x0::COND_CS: + return M680x0::COND_CC; + case M680x0::COND_NE: + return M680x0::COND_EQ; + case M680x0::COND_EQ: + return M680x0::COND_NE; + case M680x0::COND_VC: + return M680x0::COND_VS; + case M680x0::COND_VS: + return M680x0::COND_VC; + case M680x0::COND_PL: + return M680x0::COND_MI; + case M680x0::COND_MI: + return M680x0::COND_PL; + case M680x0::COND_GE: + return M680x0::COND_LT; + case M680x0::COND_LT: + return M680x0::COND_GE; + case M680x0::COND_GT: + return M680x0::COND_LE; + case M680x0::COND_LE: + return M680x0::COND_GT; + } +} + +static inline unsigned GetCondBranchFromCond(M680x0::CondCode CC) { + switch (CC) { + default: + llvm_unreachable("Illegal condition code!"); + case M680x0::COND_EQ: + return M680x0::Beq8; + case M680x0::COND_NE: + return M680x0::Bne8; + case M680x0::COND_LT: + return M680x0::Blt8; + case M680x0::COND_LE: + return M680x0::Ble8; + case M680x0::COND_GT: + return M680x0::Bgt8; + case M680x0::COND_GE: + return M680x0::Bge8; + case M680x0::COND_CS: + return M680x0::Bcs8; + case M680x0::COND_LS: + return M680x0::Bls8; + case M680x0::COND_HI: + return M680x0::Bhi8; + case M680x0::COND_CC: + return M680x0::Bcc8; + case M680x0::COND_MI: + return M680x0::Bmi8; + case M680x0::COND_PL: + return M680x0::Bpl8; + case M680x0::COND_VS: + return M680x0::Bvs8; + case M680x0::COND_VC: + return M680x0::Bvc8; + } +} + +// FIXME would be nice tablegen to generate these predicates, mb tag based + +static inline unsigned IsCMP(unsigned Op) { + switch (Op) { + default: + return false; + case M680x0::CMP8dd: + case M680x0::CMP8df: + case M680x0::CMP8di: + case M680x0::CMP8dj: + case M680x0::CMP8dp: + case M680x0::CMP16dd: + case M680x0::CMP16df: + case M680x0::CMP16di: + case M680x0::CMP16dj: + case M680x0::CMP16dp: + return true; + } +} + +static inline bool IsSETCC(unsigned SETCC) { + switch (SETCC) { + default: + return false; + case M680x0::SETd8eq: + case M680x0::SETd8ne: + case M680x0::SETd8lt: + case M680x0::SETd8ge: + case M680x0::SETd8le: + case M680x0::SETd8gt: + case M680x0::SETd8cs: + case M680x0::SETd8cc: + case M680x0::SETd8ls: + case M680x0::SETd8hi: + case M680x0::SETd8pl: + case M680x0::SETd8mi: + case M680x0::SETd8vc: + case M680x0::SETd8vs: + case M680x0::SETj8eq: + case M680x0::SETj8ne: + case M680x0::SETj8lt: + case M680x0::SETj8ge: + case M680x0::SETj8le: + case M680x0::SETj8gt: + case M680x0::SETj8cs: + case M680x0::SETj8cc: + case M680x0::SETj8ls: + case M680x0::SETj8hi: + case M680x0::SETj8pl: + case M680x0::SETj8mi: + case M680x0::SETj8vc: + case M680x0::SETj8vs: + case M680x0::SETp8eq: + case M680x0::SETp8ne: + case M680x0::SETp8lt: + case M680x0::SETp8ge: + case M680x0::SETp8le: + case M680x0::SETp8gt: + case M680x0::SETp8cs: + case M680x0::SETp8cc: + case M680x0::SETp8ls: + case M680x0::SETp8hi: + case M680x0::SETp8pl: + case M680x0::SETp8mi: + case M680x0::SETp8vc: + case M680x0::SETp8vs: + return true; + } +} + +} // namespace M680x0 + +class M680x0InstrInfo : public M680x0GenInstrInfo { + virtual void anchor(); + +protected: + const M680x0Subtarget &Subtarget; + const M680x0RegisterInfo RI; + +public: + explicit M680x0InstrInfo(const M680x0Subtarget &STI); + + static const M680x0InstrInfo *create(M680x0Subtarget &STI); + + /// TargetInstrInfo is a superset of MRegister info. As such, whenever a + /// client has an instance of instruction info, it should always be able to + /// get register info as well (through this method). + const M680x0RegisterInfo &getRegisterInfo() const { return RI; }; + + unsigned removeBranch(MachineBasicBlock &MBB, + int *BytesRemoved = nullptr) const override; + + unsigned insertBranch(MachineBasicBlock &MBB, MachineBasicBlock *TBB, + MachineBasicBlock *FBB, ArrayRef Cond, + const DebugLoc &DL, + int *BytesAdded = nullptr) const override; + + void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, + const DebugLoc &DL, unsigned DestReg, unsigned SrcReg, + bool KillSrc) const override; + + bool getStackSlotRange(const TargetRegisterClass *RC, unsigned SubIdx, + unsigned &Size, unsigned &Offset, + const MachineFunction &MF) const override; + + void storeRegToStackSlot(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, 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 expandPostRAPseudo(MachineInstr &MI) const override; + + /// Add appropriate SExt nodes + void AddSExt(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, + DebugLoc DL, unsigned Reg, MVT From, MVT To) const; + + /// Add appropriate ZExt nodes + void AddZExt(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, + DebugLoc DL, unsigned Reg, MVT From, MVT To) const; + + /// Move across register classes without extension + bool ExpandMOVX_RR(MachineInstrBuilder &MIB, MVT MVTDst, MVT MVTSrc) const; + + /// Move from register and extend + bool ExpandMOVSZX_RR(MachineInstrBuilder &MIB, bool isSigned, MVT MVTDst, + MVT MVTSrc) const; + + /// Move from memory and extend + bool ExpandMOVSZX_RM(MachineInstrBuilder &MIB, bool isSigned, + const MCInstrDesc &Desc, MVT MVTDst, MVT MVTSrc) const; + + /// Push/Pop to/from stack + bool ExpandPUSH_POP(MachineInstrBuilder &MIB, const MCInstrDesc &Desc, + bool isPush) const; + + /// Moves to/from CCR + bool ExpandCCR(MachineInstrBuilder &MIB, bool isToCCR) const; + + /// Expand all MOVEM pseudos into real MOVEMs + bool ExpandMOVEM(MachineInstrBuilder &MIB, const MCInstrDesc &Desc, + bool isRM) const; + + /// Return a virtual register initialized with the the global base register + /// value. Output instructions required to initialize the register in the + /// function entry block, if necessary. + unsigned getGlobalBaseReg(MachineFunction *MF) const; + + std::pair + decomposeMachineOperandsTargetFlags(unsigned TF) const override; + + ArrayRef> + getSerializableDirectMachineOperandTargetFlags() const override; +}; + +} // namespace llvm + +#endif Index: lib/Target/M680x0/M680x0InstrInfo.cpp =================================================================== --- /dev/null +++ lib/Target/M680x0/M680x0InstrInfo.cpp @@ -0,0 +1,671 @@ +//===-- M680x0InstrInfo.cpp - M680x0 Instruction Information ----*- 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 the M680x0 declaration of the TargetInstrInfo class. +/// +//===----------------------------------------------------------------------===// + +#include "M680x0InstrInfo.h" + +#include "M680x0InstrBuilder.h" +#include "M680x0MachineFunction.h" +#include "M680x0TargetMachine.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/CodeGen/LivePhysRegs.h" +#include "llvm/CodeGen/LiveVariables.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/TargetRegistry.h" + +using namespace llvm; + +#define DEBUG_TYPE "M680x0-instr-info" + +#define GET_INSTRINFO_CTOR_DTOR +#include "M680x0GenInstrInfo.inc" + +// Pin the vtable to this file. +void M680x0InstrInfo::anchor() {} + +M680x0InstrInfo::M680x0InstrInfo(const M680x0Subtarget &STI) + : M680x0GenInstrInfo(M680x0::ADJCALLSTACKDOWN, M680x0::ADJCALLSTACKUP, 0, + M680x0::RET), + Subtarget(STI), RI(STI) {} + +static M680x0::CondCode getCondFromBranchOpc(unsigned BrOpc) { + switch (BrOpc) { + default: + return M680x0::COND_INVALID; + case M680x0::Beq8: + return M680x0::COND_EQ; + case M680x0::Bne8: + return M680x0::COND_NE; + case M680x0::Blt8: + return M680x0::COND_LT; + case M680x0::Ble8: + return M680x0::COND_LE; + case M680x0::Bgt8: + return M680x0::COND_GT; + case M680x0::Bge8: + return M680x0::COND_GE; + case M680x0::Bcs8: + return M680x0::COND_CS; + case M680x0::Bls8: + return M680x0::COND_LS; + case M680x0::Bhi8: + return M680x0::COND_HI; + case M680x0::Bcc8: + return M680x0::COND_CC; + case M680x0::Bmi8: + return M680x0::COND_MI; + case M680x0::Bpl8: + return M680x0::COND_PL; + case M680x0::Bvs8: + return M680x0::COND_VS; + case M680x0::Bvc8: + return M680x0::COND_VC; + } +} + +unsigned M680x0InstrInfo::removeBranch(MachineBasicBlock &MBB, + int *BytesRemoved) const { + assert(!BytesRemoved && "code size not handled"); + + MachineBasicBlock::iterator I = MBB.end(); + unsigned Count = 0; + + while (I != MBB.begin()) { + --I; + if (I->isDebugValue()) + continue; + if (I->getOpcode() != M680x0::BRA8 && + getCondFromBranchOpc(I->getOpcode()) == M680x0::COND_INVALID) + break; + // Remove the branch. + I->eraseFromParent(); + I = MBB.end(); + ++Count; + } + + return Count; +} + +unsigned M680x0InstrInfo::insertBranch( + MachineBasicBlock &MBB, MachineBasicBlock *TBB, MachineBasicBlock *FBB, + ArrayRef Cond, const DebugLoc &DL, int *BytesAdded) const { + // Shouldn't be a fall through. + assert(TBB && "InsertBranch must not be told to insert a fallthrough"); + assert((Cond.size() == 1 || Cond.size() == 0) && + "M680x0 branch conditions have one component!"); + assert(!BytesAdded && "code size not handled"); + + if (Cond.empty()) { + // Unconditional branch? + assert(!FBB && "Unconditional branch with multiple successors!"); + BuildMI(&MBB, DL, get(M680x0::BRA8)).addMBB(TBB); + return 1; + } + + // If FBB is null, it is implied to be a fall-through block. + bool FallThru = FBB == nullptr; + + // Conditional branch. + unsigned Count = 0; + M680x0::CondCode CC = (M680x0::CondCode)Cond[0].getImm(); + unsigned Opc = GetCondBranchFromCond(CC); + BuildMI(&MBB, DL, get(Opc)).addMBB(TBB); + ++Count; + if (!FallThru) { + // Two-way Conditional branch. Insert the second branch. + BuildMI(&MBB, DL, get(M680x0::BRA8)).addMBB(FBB); + ++Count; + } + return Count; +} + +void M680x0InstrInfo::AddSExt(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, DebugLoc DL, + unsigned Reg, MVT From, MVT To) const { + if (From == MVT::i8) { + unsigned R = Reg; + // EXT16 requires i16 register + if (To == MVT::i32) { + R = RI.getSubReg(Reg, M680x0::MxSubRegIndex16Lo); + assert(R && "No viable SUB register available"); + } + BuildMI(MBB, I, DL, get(M680x0::EXT16), R).addReg(R); + } + + if (To == MVT::i32) { + BuildMI(MBB, I, DL, get(M680x0::EXT32), Reg).addReg(Reg); + } +} + +void M680x0InstrInfo::AddZExt(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, DebugLoc DL, + unsigned Reg, MVT From, MVT To) const { + + unsigned Mask, And; + if (From == MVT::i8) { + Mask = 0xFF; + } else { + Mask = 0xFFFF; + } + + if (To == MVT::i16) { + And = M680x0::AND16di; + } else { // i32 + And = M680x0::AND32di; + } + + // TODO use xor r,r to decrease size + BuildMI(MBB, I, DL, get(And), Reg).addReg(Reg).addImm(Mask); +} + +bool M680x0InstrInfo::ExpandMOVX_RR(MachineInstrBuilder &MIB, MVT MVTDst, + MVT MVTSrc) const { + unsigned SubIdx; + + unsigned Move = MVTDst == MVT::i16 ? M680x0::MOV16rr : M680x0::MOV32rr; + + if (MVTSrc == MVT::i8) { + SubIdx = M680x0::MxSubRegIndex8Lo; + } else { // i16 + SubIdx = M680x0::MxSubRegIndex16Lo; + } + + unsigned Dst = MIB->getOperand(0).getReg(); + unsigned Src = MIB->getOperand(1).getReg(); + + assert(Dst != Src && "You cannot use the same Regs with MOVX_RR"); + + auto TRI = getRegisterInfo(); + + auto RCDst = TRI.getMaximalPhysRegClass(Dst, MVTDst); + auto RCSrc = TRI.getMaximalPhysRegClass(Src, MVTSrc); + + assert(RCDst && RCSrc && "Wrong use of MOVX_RR"); + assert(RCDst != RCSrc && "You cannot use the same Reg Classes with MOVX_RR"); + + // We need to find the super source register that matches the size of Dst + unsigned SSrc = RI.getMatchingMegaReg(Src, RCDst); + assert(SSrc && "No viable MEGA register available"); + + DebugLoc DL = MIB->getDebugLoc(); + + // If it happens to that super source register is the destination register + // we do nothing + if (Dst == SSrc) { + LLVM_DEBUG(dbgs() << "Remove " << *MIB.getInstr() << '\n'); + MIB->eraseFromParent(); + } else { // otherwise we need to MOV + LLVM_DEBUG(dbgs() << "Expand " << *MIB.getInstr() << " to MOV\n"); + MIB->setDesc(get(Move)); + MIB->getOperand(1).setReg(SSrc); + } + + return true; +} + +/// Expand SExt MOVE pseudos into a MOV and a EXT if the operands are two +/// different registers or just EXT if it is the same register +bool M680x0InstrInfo::ExpandMOVSZX_RR(MachineInstrBuilder &MIB, bool isSigned, + MVT MVTDst, MVT MVTSrc) const { + LLVM_DEBUG(dbgs() << "Expand " << *MIB.getInstr() << " to "); + + unsigned Move; + + if (MVTDst == MVT::i16) { + Move = M680x0::MOV16rr; + } else { // i32 + Move = M680x0::MOV32rr; + } + + unsigned Dst = MIB->getOperand(0).getReg(); + unsigned Src = MIB->getOperand(1).getReg(); + + assert(Dst != Src && "You cannot use the same Regs with MOVSX_RR"); + + auto TRI = getRegisterInfo(); + + auto RCDst = TRI.getMaximalPhysRegClass(Dst, MVTDst); + auto RCSrc = TRI.getMaximalPhysRegClass(Src, MVTSrc); + + assert(RCDst && RCSrc && "Wrong use of MOVSX_RR"); + assert(RCDst != RCSrc && "You cannot use the same Reg Classes with MOVSX_RR"); + + // We need to find the super source register that matches the size of Dst + unsigned SSrc = RI.getMatchingMegaReg(Src, RCDst); + assert(SSrc && "No viable MEGA register available"); + + MachineBasicBlock &MBB = *MIB->getParent(); + DebugLoc DL = MIB->getDebugLoc(); + + if (Dst != SSrc) { + LLVM_DEBUG(dbgs() << "Move and " << '\n'); + BuildMI(MBB, MIB.getInstr(), DL, get(Move), Dst).addReg(SSrc); + } + + if (isSigned) { + LLVM_DEBUG(dbgs() << "Sign Extend" << '\n'); + AddSExt(MBB, MIB.getInstr(), DL, Dst, MVTSrc, MVTDst); + } else { + LLVM_DEBUG(dbgs() << "Zero Extend" << '\n'); + AddZExt(MBB, MIB.getInstr(), DL, Dst, MVTSrc, MVTDst); + } + + MIB->eraseFromParent(); + + return true; +} + +bool M680x0InstrInfo::ExpandMOVSZX_RM(MachineInstrBuilder &MIB, bool isSigned, + const MCInstrDesc &Desc, MVT MVTDst, + MVT MVTSrc) const { + LLVM_DEBUG(dbgs() << "Expand " << *MIB.getInstr() << " to LOAD and "); + + unsigned Dst = MIB->getOperand(0).getReg(); + + // We need the subreg of Dst to make instruction verifier happy because the + // real machine instruction consumes and produces values of the same size and + // the registers the will be used here fall into different classes and this + // makes IV cry. We could of course use bigger operation but this will put + // some pressure on cache and memory so no. + unsigned SubDst = + RI.getSubReg(Dst, MVTSrc == MVT::i8 ? M680x0::MxSubRegIndex8Lo + : M680x0::MxSubRegIndex16Lo); + assert(SubDst && "No viable SUB register available"); + + // Make this a plain move + MIB->setDesc(Desc); + MIB->getOperand(0).setReg(SubDst); + + MachineBasicBlock::iterator I = MIB.getInstr(); + I++; + MachineBasicBlock &MBB = *MIB->getParent(); + DebugLoc DL = MIB->getDebugLoc(); + + if (isSigned) { + LLVM_DEBUG(dbgs() << "Sign Extend" << '\n'); + AddSExt(MBB, I, DL, Dst, MVTSrc, MVTDst); + } else { + LLVM_DEBUG(dbgs() << "Zero Extend" << '\n'); + AddZExt(MBB, I, DL, Dst, MVTSrc, MVTDst); + } + + return true; +} + +bool M680x0InstrInfo::ExpandPUSH_POP(MachineInstrBuilder &MIB, + const MCInstrDesc &Desc, + bool isPush) const { + MachineBasicBlock::iterator I = MIB.getInstr(); + I++; + MachineBasicBlock &MBB = *MIB->getParent(); + MachineOperand MO = MIB->getOperand(0); + DebugLoc DL = MIB->getDebugLoc(); + if (isPush) { + BuildMI(MBB, I, DL, Desc).addReg(RI.getStackRegister()).add(MO); + } else { + BuildMI(MBB, I, DL, Desc, MO.getReg()).addReg(RI.getStackRegister()); + } + MIB->eraseFromParent(); + return true; +} + +bool M680x0InstrInfo::ExpandCCR(MachineInstrBuilder &MIB, bool isToCCR) const { + + // Replace the pseudo instruction with the real one + if (isToCCR) { + MIB->setDesc(get(M680x0::MOV16cd)); + } else { + // FIXME M68010 or better is required + MIB->setDesc(get(M680x0::MOV16dc)); + } + + // Promote used register to the next class + auto &Opd = MIB->getOperand(1); + Opd.setReg(getRegisterInfo().getMatchingSuperReg( + Opd.getReg(), M680x0::MxSubRegIndex8Lo, &M680x0::DR16RegClass)); + + return true; +} + +bool M680x0InstrInfo::ExpandMOVEM(MachineInstrBuilder &MIB, + const MCInstrDesc &Desc, bool isRM) const { + int Reg = 0, Offset = 0, Base = 0; + auto XR32 = RI.getRegClass(M680x0::XR32RegClassID); + auto DL = MIB->getDebugLoc(); + auto MI = MIB.getInstr(); + auto &MBB = *MIB->getParent(); + + if (isRM) { + Reg = MIB->getOperand(0).getReg(); + Offset = MIB->getOperand(1).getImm(); + Base = MIB->getOperand(2).getReg(); + } else { + Offset = MIB->getOperand(0).getImm(); + Base = MIB->getOperand(1).getReg(); + Reg = MIB->getOperand(2).getReg(); + } + + // If the register is not in XR32 then it is smaller than 32 bit, we + // implicitly promote it to 32 + if (!XR32->contains(Reg)) { + Reg = RI.getMatchingMegaReg(Reg, XR32); + assert(Reg && "Has not meaningful MEGA register"); + } + + unsigned Mask = 1 << RI.getSpillRegisterOrder(Reg); + if (isRM) { + BuildMI(MBB, MI, DL, Desc) + .addImm(Mask) + .addImm(Offset) + .addReg(Base) + .addReg(Reg, RegState::ImplicitDefine) + .copyImplicitOps(*MIB); + } else { + BuildMI(MBB, MI, DL, Desc) + .addImm(Offset) + .addReg(Base) + .addImm(Mask) + .addReg(Reg, RegState::Implicit) + .copyImplicitOps(*MIB); + } + + MIB->eraseFromParent(); + + return true; +} + +/// Expand a single-def pseudo instruction to a two-addr +/// instruction with two undef reads of the register being defined. +/// This is used for mapping: +/// %d0 = SETCS_C32d +/// to: +/// %d0 = SUBX32dd %d0, %d0 +/// +static bool Expand2AddrUndef(MachineInstrBuilder &MIB, + const MCInstrDesc &Desc) { + assert(Desc.getNumOperands() == 3 && "Expected two-addr instruction."); + unsigned Reg = MIB->getOperand(0).getReg(); + MIB->setDesc(Desc); + + // MachineInstr::addOperand() will insert explicit operands before any + // implicit operands. + MIB.addReg(Reg, RegState::Undef).addReg(Reg, RegState::Undef); + // But we don't trust that. + assert(MIB->getOperand(1).getReg() == Reg && + MIB->getOperand(2).getReg() == Reg && "Misplaced operand"); + return true; +} + +bool M680x0InstrInfo::expandPostRAPseudo(MachineInstr &MI) const { + MachineInstrBuilder MIB(*MI.getParent()->getParent(), MI); + switch (MI.getOpcode()) { + case M680x0::PUSH8d: + return ExpandPUSH_POP(MIB, get(M680x0::MOV8ed), true); + case M680x0::PUSH16d: + return ExpandPUSH_POP(MIB, get(M680x0::MOV16er), true); + case M680x0::PUSH32r: + return ExpandPUSH_POP(MIB, get(M680x0::MOV32er), true); + + case M680x0::POP8d: + return ExpandPUSH_POP(MIB, get(M680x0::MOV8do), false); + case M680x0::POP16d: + return ExpandPUSH_POP(MIB, get(M680x0::MOV16ro), false); + case M680x0::POP32r: + return ExpandPUSH_POP(MIB, get(M680x0::MOV32ro), false); + + case M680x0::SETCS_C8d: + return Expand2AddrUndef(MIB, get(M680x0::SUBX8dd)); + case M680x0::SETCS_C16d: + return Expand2AddrUndef(MIB, get(M680x0::SUBX16dd)); + case M680x0::SETCS_C32d: + return Expand2AddrUndef(MIB, get(M680x0::SUBX32dd)); + } + return false; +} + +void M680x0InstrInfo::copyPhysReg(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + const DebugLoc &DL, unsigned DstReg, + unsigned SrcReg, bool KillSrc) const { + unsigned Opc = 0; + + // First deal with the normal symmetric copies. + if (M680x0::XR32RegClass.contains(DstReg, SrcReg)) + Opc = M680x0::MOV32rr; + else if (M680x0::XR16RegClass.contains(DstReg, SrcReg)) + Opc = M680x0::MOV16rr; + else if (M680x0::DR8RegClass.contains(DstReg, SrcReg)) { + Opc = M680x0::MOV8dd; + } + + if (Opc) { + BuildMI(MBB, MI, DL, get(Opc), DstReg) + .addReg(SrcReg, getKillRegState(KillSrc)); + return; + } + + // Now deal with asymmetrically sized copies. The cases that follow are upcast + // moves. + // + // NOTE + // These moves are not aware of type nature of these values and thus + // won't do any SExt or ZExt and upper bits will basically contain garbage. + MachineInstrBuilder MIB(*MBB.getParent(), MI); + if (M680x0::DR8RegClass.contains(SrcReg)) { + if (M680x0::XR16RegClass.contains(DstReg)) { + Opc = M680x0::MOVXd16d8; + } else if (M680x0::XR32RegClass.contains(DstReg)) { + Opc = M680x0::MOVXd32d8; + } + } else if (M680x0::XR16RegClass.contains(SrcReg)) { + if (M680x0::XR32RegClass.contains(DstReg)) { + Opc = M680x0::MOVXd32d16; + } + } + + if (Opc) { + BuildMI(MBB, MI, DL, get(Opc), DstReg) + .addReg(SrcReg, getKillRegState(KillSrc)); + return; + } + + bool FromCCR = SrcReg == M680x0::CCR; + bool FromSR = SrcReg == M680x0::SR; + bool ToCCR = DstReg == M680x0::CCR; + bool ToSR = DstReg == M680x0::SR; + + if (FromCCR) { + assert(M680x0::DR8RegClass.contains(DstReg) && + "Need DR8 register to copy CCR"); + Opc = M680x0::MOV8dc; + } else if (ToCCR) { + assert(M680x0::DR8RegClass.contains(SrcReg) && + "Need DR8 register to copy CCR"); + Opc = M680x0::MOV8cd; + } else if (FromSR || ToSR) { + llvm_unreachable("Cannot emit SR copy instruction"); + } + + if (Opc) { + BuildMI(MBB, MI, DL, get(Opc), DstReg) + .addReg(SrcReg, getKillRegState(KillSrc)); + return; + } + + LLVM_DEBUG(dbgs() << "Cannot copy " << RI.getName(SrcReg) << " to " + << RI.getName(DstReg) << '\n'); + llvm_unreachable("Cannot emit physreg copy instruction"); +} + +namespace { +unsigned getLoadStoreRegOpcode(unsigned Reg, const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI, + const M680x0Subtarget &STI, bool load) { + switch (TRI->getRegSizeInBits(*RC)) { + default: + llvm_unreachable("Unknown spill size"); + case 8: + if (M680x0::DR8RegClass.hasSubClassEq(RC)) { + return load ? M680x0::MOVM8mp_P : M680x0::MOVM8pm_P; + } else if (M680x0::CCRCRegClass.hasSubClassEq(RC)) { + return load ? M680x0::MOV16cp : M680x0::MOV16pc; + } + llvm_unreachable("Unknown 1-byte regclass"); + case 16: + assert(M680x0::XR16RegClass.hasSubClassEq(RC) && "Unknown 2-byte regclass"); + return load ? M680x0::MOVM16mp_P : M680x0::MOVM16pm_P; + case 32: + assert(M680x0::XR32RegClass.hasSubClassEq(RC) && "Unknown 4-byte regclass"); + return load ? M680x0::MOVM32mp_P : M680x0::MOVM32pm_P; + } +} + +unsigned getStoreRegOpcode(unsigned SrcReg, const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI, + const M680x0Subtarget &STI) { + return getLoadStoreRegOpcode(SrcReg, RC, TRI, STI, false); +} + +unsigned getLoadRegOpcode(unsigned DstReg, const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI, + const M680x0Subtarget &STI) { + return getLoadStoreRegOpcode(DstReg, RC, TRI, STI, true); +} +} // end anonymous namespace + +bool M680x0InstrInfo::getStackSlotRange(const TargetRegisterClass *RC, + unsigned SubIdx, unsigned &Size, + unsigned &Offset, + const MachineFunction &MF) const { + // The slot size must be the maximum size so we can easily use MOVEM.L + Size = 4; + Offset = 0; + return true; +} + +void M680x0InstrInfo::storeRegToStackSlot(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + unsigned SrcReg, bool isKill, int FI, + const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI) const { + const MachineFunction &MF = *MBB.getParent(); + assert(MF.getFrameInfo().getObjectSize(FI) == 4 && + "Stack slot too small for store"); + unsigned Opc = getStoreRegOpcode(SrcReg, RC, TRI, Subtarget); + DebugLoc DL = MBB.findDebugLoc(MI); + // (0,FI) <- $reg + addFrameReference(BuildMI(MBB, MI, DL, get(Opc)), FI) + .addReg(SrcReg, getKillRegState(isKill)); +} + +void M680x0InstrInfo::loadRegFromStackSlot( + MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, unsigned DstReg, + int FI, const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI) const { + const MachineFunction &MF = *MBB.getParent(); + assert(MF.getFrameInfo().getObjectSize(FI) == 4 && + "Stack slot too small for store"); + unsigned Opc = getLoadRegOpcode(DstReg, RC, TRI, Subtarget); + DebugLoc DL = MBB.findDebugLoc(MI); + addFrameReference(BuildMI(MBB, MI, DL, get(Opc), DstReg), FI); +} + +/// Return a virtual register initialized with the the global base register +/// value. Output instructions required to initialize the register in the +/// function entry block, if necessary. +/// +/// TODO: Eliminate this and move the code to M680x0MachineFunctionInfo. +unsigned M680x0InstrInfo::getGlobalBaseReg(MachineFunction *MF) const { + M680x0MachineFunctionInfo *MxFI = MF->getInfo(); + unsigned GlobalBaseReg = MxFI->getGlobalBaseReg(); + if (GlobalBaseReg != 0) + return GlobalBaseReg; + + // Create the register. The code to initialize it is inserted later, + // by the CGBR pass (below). + // + // NOTE + // Normally M680x0 uses A5 register as global base pointer but this will + // create unnecessary spill if we use less then 4 registers in code; since A5 + // is callee-save anyway we could try to allocate caller-save first and if + // lucky get one, otherwise it does not really matter which callee-save to + // use. + MachineRegisterInfo &RegInfo = MF->getRegInfo(); + GlobalBaseReg = RegInfo.createVirtualRegister(&M680x0::AR32_NOSPRegClass); + MxFI->setGlobalBaseReg(GlobalBaseReg); + return GlobalBaseReg; +} + +std::pair +M680x0InstrInfo::decomposeMachineOperandsTargetFlags(unsigned TF) const { + return std::make_pair(TF, 0u); +} + +ArrayRef> +M680x0InstrInfo::getSerializableDirectMachineOperandTargetFlags() const { + using namespace M680x0II; + static const std::pair TargetFlags[] = { + {MO_ABSOLUTE_ADDRESS, "M680x0-absolute"}, + {MO_PC_RELATIVE_ADDRESS, "M680x0-pcrel"}, + {MO_GOT, "M680x0-got"}, + {MO_GOTOFF, "M680x0-gotoff"}, + {MO_GOTPCREL, "M680x0-gotpcrel"}, + {MO_PLT, "M680x0-plt"}}; + return makeArrayRef(TargetFlags); +} + +namespace { +/// Create Global Base Reg pass. This initializes the PIC global base register +struct CGBR : public MachineFunctionPass { + static char ID; + CGBR() : MachineFunctionPass(ID) {} + + bool runOnMachineFunction(MachineFunction &MF) override { + const M680x0Subtarget &STI = MF.getSubtarget(); + M680x0MachineFunctionInfo *MxFI = MF.getInfo(); + + unsigned GlobalBaseReg = MxFI->getGlobalBaseReg(); + + // If we didn't need a GlobalBaseReg, don't insert code. + if (GlobalBaseReg == 0) + return false; + + // Insert the set of GlobalBaseReg into the first MBB of the function + MachineBasicBlock &FirstMBB = MF.front(); + MachineBasicBlock::iterator MBBI = FirstMBB.begin(); + DebugLoc DL = FirstMBB.findDebugLoc(MBBI); + const M680x0InstrInfo *TII = STI.getInstrInfo(); + + // Generate lea (__GLOBAL_OFFSET_TABLE_,%PC), %A5 + BuildMI(FirstMBB, MBBI, DL, TII->get(M680x0::LEA32q), GlobalBaseReg) + .addExternalSymbol("_GLOBAL_OFFSET_TABLE_", M680x0II::MO_GOTPCREL); + + return true; + } + + StringRef getPassName() const override { + return "M680x0 PIC Global Base Reg Initialization"; + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesCFG(); + MachineFunctionPass::getAnalysisUsage(AU); + } +}; +} // namespace + +char CGBR::ID = 0; +FunctionPass *llvm::createM680x0GlobalBaseRegPass() { return new CGBR(); } Index: lib/Target/M680x0/M680x0InstrInfo.td =================================================================== --- /dev/null +++ lib/Target/M680x0/M680x0InstrInfo.td @@ -0,0 +1,636 @@ +//== M680x0InstrInfo.td - Main M680x0 Instruction Definition -*- tablegen -*-=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file describes the M680x0 instruction set, defining the instructions +/// and properties of the instructions which are needed for code generation, +/// machine code emission, and analysis. +/// +//===----------------------------------------------------------------------===// + +include "M680x0InstrFormats.td" + +//===----------------------------------------------------------------------===// +// Profiles +//===----------------------------------------------------------------------===// + +def MxSDT_CallSeqStart : SDCallSeqStart<[SDTCisVT<0, i32>, SDTCisVT<1, i32>]>; +def MxSDT_CallSeqEnd : SDCallSeqEnd<[SDTCisVT<0, i32>, SDTCisVT<1, i32>]>; + +def MxSDT_Call : SDTypeProfile<0, -1, [SDTCisVT<0, iPTR>]>; + +def MxSDT_Ret : SDTypeProfile<0, -1, [ + /* ADJ */ SDTCisVT<0, i32> +]>; + +def MxSDT_TCRet : SDTypeProfile<0, 2, [SDTCisPtrTy<0>, SDTCisVT<1, i32>]>; + +def MxSDT_Wrapper : SDTypeProfile<1, 1, [SDTCisSameAs<0, 1>, SDTCisPtrTy<0>]>; + +def MxSDT_UnArithCCROut : SDTypeProfile<2, 1, [ + /* RES */ SDTCisInt<0>, + /* CCR */ SDTCisVT<1, i8>, + /* OPD */ SDTCisSameAs<0, 2> +]>; + +// RES, CCR <- op LHS, RHS +def MxSDT_BiArithCCROut : SDTypeProfile<2, 2, [ + /* RES */ SDTCisInt<0>, + /* CCR */ SDTCisVT<1, i8>, + /* LHS */ SDTCisSameAs<0, 2>, + /* RHS */ SDTCisSameAs<0, 3> +]>; + +// RES, CCR <- op LHS, RHS, CCR +def MxSDT_BiArithCCRInOut : SDTypeProfile<2, 3, [ + /* RES 1 */ SDTCisInt<0>, + /* CCR */ SDTCisVT<1, i8>, + /* LHS */ SDTCisSameAs<0, 2>, + /* RHS */ SDTCisSameAs<0, 3>, + /* CCR */ SDTCisSameAs<1, 4> +]>; + +// RES1, RES2, CCR <- op LHS, RHS +def MxSDT_2BiArithCCROut : SDTypeProfile<3, 2, [ + /* RES 1 */ SDTCisInt<0>, + /* RES 2 */ SDTCisSameAs<0, 1>, + /* CCR */ SDTCisVT<1, i8>, + /* LHS */ SDTCisSameAs<0, 2>, + /* RHS */ SDTCisSameAs<0, 3> +]>; + +def MxSDT_CmpTest : SDTypeProfile<1, 2, [ + /* CCR */ SDTCisVT<0, i8>, + /* Ops */ SDTCisSameAs<1, 2> +]>; + +def MxSDT_Cmov : SDTypeProfile<1, 4, [ + /* ARG */ SDTCisSameAs<0, 1>, + /* ARG */ SDTCisSameAs<1, 2>, + /* Cond */ SDTCisVT<3, i8>, + /* CCR */ SDTCisVT<4, i8> +]>; + +def MxSDT_BrCond : SDTypeProfile<0, 3, [ + /* Dest */ SDTCisVT<0, OtherVT>, + /* Cond */ SDTCisVT<1, i8>, + /* CCR */ SDTCisVT<2, i8> +]>; + +def MxSDT_SetCC : SDTypeProfile<1, 2, [ + /* BOOL */ SDTCisVT<0, i8>, + /* Cond */ SDTCisVT<1, i8>, + /* CCR */ SDTCisVT<2, i8> +]>; + +def MxSDT_SetCC_C : SDTypeProfile<1, 2, [ + /* BOOL */ SDTCisInt<0>, + /* Cond */ SDTCisVT<1, i8>, + /* CCR */ SDTCisVT<2, i8> +]>; + + +def MxSDT_SEG_ALLOCA : SDTypeProfile<1, 1,[ + /* MEM */ SDTCisVT<0, iPTR>, + /* SIZE */ SDTCisVT<1, iPTR> +]>; + + +//===----------------------------------------------------------------------===// +// Nodes +//===----------------------------------------------------------------------===// + +def MxCallSeqStart : SDNode<"ISD::CALLSEQ_START", MxSDT_CallSeqStart, + [SDNPHasChain, SDNPOutGlue]>; + +def MxCallSeqEnd : SDNode<"ISD::CALLSEQ_END", MxSDT_CallSeqEnd, + [SDNPHasChain, SDNPOptInGlue, SDNPOutGlue]>; + +def MxCall : SDNode<"M680x0ISD::CALL", MxSDT_Call, + [SDNPHasChain, SDNPOutGlue, + SDNPOptInGlue, SDNPVariadic]>; + +def MxRet : SDNode<"M680x0ISD::RET", MxSDT_Ret, + [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>; + +def MxTCRet : SDNode<"M680x0ISD::TC_RETURN", MxSDT_TCRet, + [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>; + +def MxWrapper : SDNode<"M680x0ISD::Wrapper", MxSDT_Wrapper>; +def MxWrapperPC : SDNode<"M680x0ISD::WrapperPC", MxSDT_Wrapper>; + +def MxAdd : SDNode<"M680x0ISD::ADD", MxSDT_BiArithCCROut, [SDNPCommutative]>; +def MxSub : SDNode<"M680x0ISD::SUB", MxSDT_BiArithCCROut>; +def MxOr : SDNode<"M680x0ISD::OR", MxSDT_BiArithCCROut, [SDNPCommutative]>; +def MxXor : SDNode<"M680x0ISD::XOR", MxSDT_BiArithCCROut, [SDNPCommutative]>; +def MxAnd : SDNode<"M680x0ISD::AND", MxSDT_BiArithCCROut, [SDNPCommutative]>; + +def MxAddX : SDNode<"M680x0ISD::ADDX", MxSDT_BiArithCCRInOut>; +def MxSubX : SDNode<"M680x0ISD::SUBX", MxSDT_BiArithCCRInOut>; + +def MxSMul : SDNode<"M680x0ISD::SMUL", MxSDT_BiArithCCROut, [SDNPCommutative]>; +def MxUMul : SDNode<"M680x0ISD::UMUL", MxSDT_2BiArithCCROut, [SDNPCommutative]>; + +def MxCmp : SDNode<"M680x0ISD::CMP", MxSDT_CmpTest>; +def MxBt : SDNode<"M680x0ISD::BT", MxSDT_CmpTest>; + +def MxCmov : SDNode<"M680x0ISD::CMOV", MxSDT_Cmov>; +def MxBrCond : SDNode<"M680x0ISD::BRCOND", MxSDT_BrCond, [SDNPHasChain]>; +def MxSetCC : SDNode<"M680x0ISD::SETCC", MxSDT_SetCC>; +def MxSetCC_C : SDNode<"M680x0ISD::SETCC_CARRY", MxSDT_SetCC_C>; + + +def MxSegAlloca : SDNode<"M680x0ISD::SEG_ALLOCA", MxSDT_SEG_ALLOCA, + [SDNPHasChain]>; + + +//===----------------------------------------------------------------------===// +// Operands +//===----------------------------------------------------------------------===// + +/// Size is the size of the data, either bits of a register or number of bits +/// addressed in memory. Size id is a letter that identifies size. +class MxSize { + int Num = num; + string Id = id; + string Full = full; +} + +def MxSize8 : MxSize<8, "b", "byte">; +def MxSize16 : MxSize<16, "w", "word">; +def MxSize32 : MxSize<32, "l", "long">; + +class MxOperand { + ValueType VT = vt; + string Letter = letter; + MxSize Size = size; + RegisterClass RC = rc; + dag Pat = pat; +} + +class MxRegOp + : RegisterOperand, + MxOperand; + +// REGISTER DIRECT. The operand is in the data register specified by +// the effective address register field. +def MxXRD16 : MxRegOp; +def MxXRD32 : MxRegOp; + +def MxXRD16_TC : MxRegOp; +def MxXRD32_TC : MxRegOp; + +// DATA REGISTER DIRECT. The operand is in the data register specified by +// the effective address register field. +def MxDRD8 : MxRegOp; +def MxDRD16 : MxRegOp; +def MxDRD32 : MxRegOp; + +def MxDRD16_TC : MxRegOp; +def MxDRD32_TC : MxRegOp; + +// ADDRESS REGISTER DIRECT. The operand is in the address register specified by +// the effective address register field. +def MxARD16 : MxRegOp; +def MxARD32 : MxRegOp; + +def MxARD16_TC : MxRegOp; +def MxARD32_TC : MxRegOp; + +// TODO finish parser wiring +def MxMemAsmOperand : AsmOperandClass { + let Name = "MxMemOp"; +} + +class MxMemOp + : Operand, MxOperand { + let PrintMethod = printMethod; + let MIOperandInfo = ops; + let ParserMatchClass = parserMatchClass; + let OperandType = "OPERAND_MEMORY"; +} + +// ADDRESS REGISTER INDIRECT. The address of the operand is in the address +// register specified by the register field. The reference is classified as +// a data reference with the exception of the jump and jump-to-subroutine +// instructions. +def MxARI8 : MxMemOp<(ops AR32), MxSize8, "j", "printARI8Mem">; +def MxARI16 : MxMemOp<(ops AR32), MxSize16, "j", "printARI16Mem">; +def MxARI32 : MxMemOp<(ops AR32), MxSize32, "j", "printARI32Mem">; + +def MxARI8_TC : MxMemOp<(ops AR32_TC), MxSize8, "j", "printARI8Mem">; +def MxARI16_TC : MxMemOp<(ops AR32_TC), MxSize16, "j", "printARI16Mem">; +def MxARI32_TC : MxMemOp<(ops AR32_TC), MxSize32, "j", "printARI32Mem">; + +// ADDRESS REGISTER INDIRECT WITH POSTINCREMENT. The address of the operand is +// in the address register specified by the register field. After the operand +// address is used, it is incremented by one, two, or four depending upon whether +// the size of the operand is byte, word, or long word. If the address register +// is the stack pointer and the operand size is byte, the address is incremented +// by two rather than one to keep the stack pointer on a word boundary. +// The reference is classified as a data reference. +def MxARIPI8 : MxMemOp<(ops AR32), MxSize8, "o", "printARIPI8Mem">; +def MxARIPI16 : MxMemOp<(ops AR32), MxSize16, "o", "printARIPI16Mem">; +def MxARIPI32 : MxMemOp<(ops AR32), MxSize32, "o", "printARIPI32Mem">; + +def MxARIPI8_TC : MxMemOp<(ops AR32_TC), MxSize8, "o", "printARIPI8Mem">; +def MxARIPI16_TC : MxMemOp<(ops AR32_TC), MxSize16, "o", "printARIPI16Mem">; +def MxARIPI32_TC : MxMemOp<(ops AR32_TC), MxSize32, "o", "printARIPI32Mem">; + +// ADDRESS REGISTER INDIRECT WITH PREDECREMENT. The address of the operand is in +// the address register specified by the register field. Before the operand +// address is used, it is decremented by one, two, or four depending upon whether +// the operand size is byte, word, or long word. If the address register is +// the stack pointer and the operand size is byte, the address is decremented by +// two rather than one to keep the stack pointer on a word boundary. +// The reference is classified as a data reference. +def MxARIPD8 : MxMemOp<(ops AR32), MxSize8, "e", "printARIPD8Mem">; +def MxARIPD16 : MxMemOp<(ops AR32), MxSize16, "e", "printARIPD16Mem">; +def MxARIPD32 : MxMemOp<(ops AR32), MxSize32, "e", "printARIPD32Mem">; + +def MxARIPD8_TC : MxMemOp<(ops AR32_TC), MxSize8, "e", "printARIPD8Mem">; +def MxARIPD16_TC : MxMemOp<(ops AR32_TC), MxSize16, "e", "printARIPD16Mem">; +def MxARIPD32_TC : MxMemOp<(ops AR32_TC), MxSize32, "e", "printARIPD32Mem">; + +// ADDRESS REGISTER INDIRECT WITH DISPLACEMENT. This addressing mode requires one +// word of extension. The address of the operand is the sum of the address in +// the address register and the sign-extended 16-bit displacement integer in the +// extension word. The reference is classified as a data reference with the +// exception of the jump and jump-to-subroutine instructions. +def MxARID8 : MxMemOp<(ops i16imm, AR32), MxSize8, "p", "printARID8Mem">; +def MxARID16 : MxMemOp<(ops i16imm, AR32), MxSize16, "p", "printARID16Mem">; +def MxARID32 : MxMemOp<(ops i16imm, AR32), MxSize32, "p", "printARID32Mem">; + +def MxARID8_TC : MxMemOp<(ops i16imm, AR32_TC), MxSize8, "p", "printARID8Mem">; +def MxARID16_TC : MxMemOp<(ops i16imm, AR32_TC), MxSize16, "p", "printARID16Mem">; +def MxARID32_TC : MxMemOp<(ops i16imm, AR32_TC), MxSize32, "p", "printARID32Mem">; + +// ADDRESS REGISTER INDIRECT WITH INDEX. This addressing mode requires one word +// of extension. The address of the operand is the sum of the address in the +// address register, the signextended displacement integer in the low order eight +// bits of the extension word, and the contents of the index register. +// The reference is classified as a data reference with the exception of the +// jump and jump-to-subroutine instructions +def MxARII8 : MxMemOp<(ops i8imm, AR32, XR32), MxSize8, "f", "printARII8Mem">; +def MxARII16 : MxMemOp<(ops i8imm, AR32, XR32), MxSize16, "f", "printARII16Mem">; +def MxARII32 : MxMemOp<(ops i8imm, AR32, XR32), MxSize32, "f", "printARII32Mem">; + +def MxARII8_TC : MxMemOp<(ops i8imm, AR32_TC, XR32_TC), MxSize8, "f", "printARII8Mem">; +def MxARII16_TC : MxMemOp<(ops i8imm, AR32_TC, XR32_TC), MxSize16, "f", "printARII16Mem">; +def MxARII32_TC : MxMemOp<(ops i8imm, AR32_TC, XR32_TC), MxSize32, "f", "printARII32Mem">; + +// ABSOLUTE SHORT ADDRESS. This addressing mode requires one word of extension. +// The address of the operand is the extension word. The 16-bit address is sign +// extended before it is used. The reference is classified as a data reference +// with the exception of the jump and jump-tosubroutine instructions. +def MxAS8 : MxMemOp<(ops OtherVT), MxSize8, "B", "printAS8Mem">; +def MxAS16 : MxMemOp<(ops OtherVT), MxSize16, "B", "printAS16Mem">; +def MxAS32 : MxMemOp<(ops OtherVT), MxSize32, "B", "printAS32Mem">; + +// ABSOLUTE LONG ADDRESS. This addressing mode requires two words of extension. +// The address of the operand is developed by the concatenation of the extension +// words. The high order part of the address is the first extension word; the low +// order part of the address is the second extension word. The reference is +// classified as a data reference with the exception of the jump and jump +// to-subroutine instructions. +def MxAL8 : MxMemOp<(ops OtherVT), MxSize8, "b", "printAL8Mem">; +def MxAL16 : MxMemOp<(ops OtherVT), MxSize16, "b", "printAL16Mem">; +def MxAL32 : MxMemOp<(ops OtherVT), MxSize32, "b", "printAL32Mem">; + +let OperandType = "OPERAND_PCREL" in { +// PROGRAM COUNTER WITH DISPLACEMENT. This addressing mode requires one word of +// extension. The address of the operand is the sum of the address in the program +// counter and the Sign-extended 16-bit displacement integer in the extension +// word. The value in the program counter is the address of the extension word. +// The reference is classified as a program reference. +def MxPCD8 : MxMemOp<(ops i16imm), MxSize8, "q", "printPCD8Mem">; +def MxPCD16 : MxMemOp<(ops i16imm), MxSize16, "q", "printPCD16Mem">; +def MxPCD32 : MxMemOp<(ops i16imm), MxSize32, "q", "printPCD32Mem">; + +// PROGRAM COUNTER WITH INDEX. This addressing mode requires one word of +// extension. The address is the sum of the address in the program counter, the +// sign-extended displacement integer in the lower eight bits of the extension +// word, and the contents of the index register. The value in the program +// counter is the address of the extension word. This reference is classified as +// a program reference. +def MxPCI8 : MxMemOp<(ops i8imm, XR32), MxSize8, "k", "printPCI8Mem">; +def MxPCI16 : MxMemOp<(ops i8imm, XR32), MxSize16, "k", "printPCI16Mem">; +def MxPCI32 : MxMemOp<(ops i8imm, XR32), MxSize32, "k", "printPCI32Mem">; +} // OPERAND_PCREL + +class MxOp + : Operand, + MxOperand; + +let OperandType = "OPERAND_IMMEDIATE", + PrintMethod = "printImmediate" in { +// IMMEDIATE DATA. This addressing mode requires either one or two words of +// extension depending on the size of the operation. +// Byte Operation - operand is low order byte of extension word +// Word Operation - operand is extension word +// Long Word Operation - operand is in the two extension words, +// high order 16 bits are in the first +// extension word, low order 16 bits are +// in the second extension word. +def Mxi8imm : MxOp; +def Mxi16imm : MxOp; +def Mxi32imm : MxOp; +} // OPERAND_IMMEDIATE + +let OperandType = "OPERAND_PCREL", + /* ParserMatchClass = M680x0AbsMemAsmOperand, */ + PrintMethod = "printPCRelImm" in { + +// Branch targets have OtherVT type and print as pc-relative values. +def MxBrTarget8 : Operand; +def MxBrTarget16 : Operand; +def MxBrTarget32 : Operand; + +} // OPERAND_PCREL + +// Used with MOVEM +def MxMoveMask : MxOp { + let OperandType = "OPERAND_IMMEDIATE"; + let PrintMethod = "printMoveMask"; +} + + +//===----------------------------------------------------------------------===// +// Predicates +//===----------------------------------------------------------------------===// + +def SmallCode : Predicate<"TM.getCodeModel() == CodeModel::Small">; +def KernelCode : Predicate<"TM.getCodeModel() == CodeModel::Kernel">; +def FarData : Predicate<"TM.getCodeModel() != CodeModel::Small &&" + "TM.getCodeModel() != CodeModel::Kernel">; +def NearData : Predicate<"TM.getCodeModel() == CodeModel::Small ||" + "TM.getCodeModel() == CodeModel::Kernel">; +def IsPIC : Predicate<"TM.isPositionIndependent()">; +def IsNotPIC : Predicate<"!TM.isPositionIndependent()">; +def IsM68000 : Predicate<"!Subtarget.IsM68000()">; +def IsM68010 : Predicate<"!Subtarget.IsM68010()">; +def IsM68020 : Predicate<"!Subtarget.IsM68020()">; +def IsM68030 : Predicate<"!Subtarget.IsM68030()">; +def IsM68040 : Predicate<"!Subtarget.IsM68040()">; + + +//===----------------------------------------------------------------------===// +// Condition Codes +// +// These MUST be kept in sync with codes enum in M680x0InstrInfo.h +//===----------------------------------------------------------------------===// + +def MxCONDt : PatLeaf<(i8 0)>; // True +def MxCONDf : PatLeaf<(i8 1)>; // False +def MxCONDhi : PatLeaf<(i8 2)>; // High +def MxCONDls : PatLeaf<(i8 3)>; // Less or Same +def MxCONDcc : PatLeaf<(i8 4)>; // Carry Clear +def MxCONDcs : PatLeaf<(i8 5)>; // Carry Set +def MxCONDne : PatLeaf<(i8 6)>; // Not Equal +def MxCONDeq : PatLeaf<(i8 7)>; // Equal +def MxCONDvc : PatLeaf<(i8 8)>; // Overflow Clear +def MxCONDvs : PatLeaf<(i8 9)>; // Overflow Set +def MxCONDpl : PatLeaf<(i8 10)>; // Plus +def MxCONDmi : PatLeaf<(i8 11)>; // Minus +def MxCONDge : PatLeaf<(i8 12)>; // Greater or Equal +def MxCONDlt : PatLeaf<(i8 13)>; // Less Than +def MxCONDgt : PatLeaf<(i8 14)>; // Greater Than +def MxCONDle : PatLeaf<(i8 15)>; // Less or Equal + + +//===----------------------------------------------------------------------===// +// Complex Patterns +//===----------------------------------------------------------------------===// + +// NOTE Though this CP is not stricly necessarily it will simplify instruciton +// definitions +def MxCP_ARI : ComplexPattern; + +def MxCP_ARIPI : ComplexPattern; + +def MxCP_ARIPD : ComplexPattern; + +def MxCP_ARID : ComplexPattern; + +def MxCP_ARII : ComplexPattern; + +def MxCP_AL : ComplexPattern; + +def MxCP_PCD : ComplexPattern; + +def MxCP_PCI : ComplexPattern; + + +//===----------------------------------------------------------------------===// +// Pattern Fragments +//===----------------------------------------------------------------------===// + +def MximmSExt8 : PatLeaf<(i8 imm)>; +def MximmSExt16 : PatLeaf<(i16 imm)>; +def MximmSExt32 : PatLeaf<(i32 imm)>; + +// Used for Shifts and Rotations, since M680x0 immediates in these +// instructions are 1 <= i <= 8. +// Generally, if immediate is bigger than 8 it will be moved to a register +// and then an operation is performed. TODO you need to evaluate whether +// splitting one big shift(or rotate) into a few smaller is faster than doing +// a move, if so do custom lowering +def Mximm8_1to8 : ImmLeaf= 1 && Imm <= 8; }]>; +def Mximm16_1to8 : ImmLeaf= 1 && Imm <= 8; }]>; +def Mximm32_1to8 : ImmLeaf= 1 && Imm <= 8; }]>; + +// Helper fragments for loads. +// It's always safe to treat a anyext i16 load as a i32 load if the i16 is +// known to be 32-bit aligned or better. Ditto for i8 to i16. +def Mxloadi16 : PatFrag<(ops node:$ptr), (i16 (unindexedload node:$ptr)), [{ + LoadSDNode *LD = cast(N); + ISD::LoadExtType ExtType = LD->getExtensionType(); + if (ExtType == ISD::NON_EXTLOAD) + return true; + if (ExtType == ISD::EXTLOAD) + return LD->getAlignment() >= 2 && !LD->isVolatile(); + return false; +}]>; + +def Mxloadi32 : PatFrag<(ops node:$ptr), (i32 (unindexedload node:$ptr)), [{ + LoadSDNode *LD = cast(N); + ISD::LoadExtType ExtType = LD->getExtensionType(); + if (ExtType == ISD::NON_EXTLOAD) + return true; + if (ExtType == ISD::EXTLOAD) + return LD->getAlignment() >= 4 && !LD->isVolatile(); + return false; +}]>; + +def Mxloadi8 : PatFrag<(ops node:$ptr), (i8 (load node:$ptr))>; + +def MxSExtLoadi16i8 : PatFrag<(ops node:$ptr), (i16 (sextloadi8 node:$ptr))>; +def MxSExtLoadi32i8 : PatFrag<(ops node:$ptr), (i32 (sextloadi8 node:$ptr))>; +def MxSExtLoadi32i16 : PatFrag<(ops node:$ptr), (i32 (sextloadi16 node:$ptr))>; + +def MxZExtLoadi8i1 : PatFrag<(ops node:$ptr), (i8 (zextloadi1 node:$ptr))>; +def MxZExtLoadi16i1 : PatFrag<(ops node:$ptr), (i16 (zextloadi1 node:$ptr))>; +def MxZExtLoadi32i1 : PatFrag<(ops node:$ptr), (i32 (zextloadi1 node:$ptr))>; +def MxZExtLoadi16i8 : PatFrag<(ops node:$ptr), (i16 (zextloadi8 node:$ptr))>; +def MxZExtLoadi32i8 : PatFrag<(ops node:$ptr), (i32 (zextloadi8 node:$ptr))>; +def MxZExtLoadi32i16 : PatFrag<(ops node:$ptr), (i32 (zextloadi16 node:$ptr))>; + +def MxExtLoadi8i1 : PatFrag<(ops node:$ptr), (i8 (extloadi1 node:$ptr))>; +def MxExtLoadi16i1 : PatFrag<(ops node:$ptr), (i16 (extloadi1 node:$ptr))>; +def MxExtLoadi32i1 : PatFrag<(ops node:$ptr), (i32 (extloadi1 node:$ptr))>; +def MxExtLoadi16i8 : PatFrag<(ops node:$ptr), (i16 (extloadi8 node:$ptr))>; +def MxExtLoadi32i8 : PatFrag<(ops node:$ptr), (i32 (extloadi8 node:$ptr))>; +def MxExtLoadi32i16 : PatFrag<(ops node:$ptr), (i32 (extloadi16 node:$ptr))>; + + +//===----------------------------------------------------------------------===// +// Type Fixtures +// +// Type Fixtures are ValueType related information sets that usually go together +//===----------------------------------------------------------------------===// + +// TODO make it folded like MxType8.F.Op nad MxType8.F.Pat +// TODO move strings into META subclass +class MxType { + int Size = vt.Size; + ValueType VT = vt; + string Prefix = prefix; + string Postfix = postfix; + string RLet = rLet; + MxOperand ROp = rOp; + MxOperand JOp = jOp; ComplexPattern JPat = jPat; + MxOperand OOp = oOp; ComplexPattern OPat = oPat; + MxOperand EOp = eOp; ComplexPattern EPat = ePat; + MxOperand POp = pOp; ComplexPattern PPat = pPat; + MxOperand FOp = fOp; ComplexPattern FPat = fPat; + MxOperand BOp = bOp; ComplexPattern BPat = bPat; + MxOperand QOp = qOp; ComplexPattern QPat = qPat; + MxOperand KOp = kOp; ComplexPattern KPat = kPat; + MxOperand IOp = iOp; PatFrag IPat = iPat; + PatFrag Load = load; +} + +class MxType8Class + : MxType; + +def MxType8 : MxType8Class; + +class MxType16Class + : MxType; + +def MxType16 : MxType16Class; + +class MxType32Class + : MxType; + +def MxType32 : MxType32Class; + + +def MxType8d : MxType8Class<"d", MxDRD8>; + +def MxType16d : MxType16Class<"d", MxDRD16>; +def MxType16a : MxType16Class<"a", MxARD16>; +def MxType16r : MxType16Class<"r", MxXRD16>; +def MxType32d : MxType32Class<"d", MxDRD32>; +def MxType32a : MxType32Class<"a", MxARD32>; +def MxType32r : MxType32Class<"r", MxXRD32>; + +let Postfix = "_TC" in { +def MxType16d_TC : MxType16Class<"d", MxDRD16_TC>; +def MxType16a_TC : MxType16Class<"a", MxARD16_TC>; +def MxType16r_TC : MxType16Class<"r", MxXRD16_TC>; +def MxType32d_TC : MxType32Class<"d", MxDRD32_TC>; +def MxType32a_TC : MxType32Class<"a", MxARD32_TC>; +def MxType32r_TC : MxType32Class<"r", MxXRD32_TC>; +} + + +//===----------------------------------------------------------------------===// +// Subsystems +//===----------------------------------------------------------------------===// + +include "M680x0InstrData.td" +include "M680x0InstrShiftRotate.td" +include "M680x0InstrBits.td" +include "M680x0InstrArithmetic.td" +include "M680x0InstrControl.td" + +include "M680x0InstrCompiler.td" Index: lib/Target/M680x0/M680x0InstrShiftRotate.td =================================================================== --- /dev/null +++ lib/Target/M680x0/M680x0InstrShiftRotate.td @@ -0,0 +1,86 @@ +//===------ M680x0InstrShiftRotate.td - Logical Instrs -----*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file describes the logical instructions in the M680x0 architecture. +/// Here is the current status of the file: +/// +/// Machine: +/// +/// SHL [~] ASR [~] LSR [~] SWAP [ ] +/// ROL [~] ROR [~] ROXL [ ] ROXR [ ] +/// +/// Map: +/// +/// [ ] - was not touched at all +/// [!] - requires extarnal stuff implemented +/// [~] - in progress but usable +/// [x] - done +/// +//===----------------------------------------------------------------------===// + +def MxRODI_R : MxBead1Bit<0>; +def MxRODI_L : MxBead1Bit<1>; + +def MxROOP_AS : MxBead2Bits<0b00>; +def MxROOP_LS : MxBead2Bits<0b01>; +def MxROOP_ROX : MxBead2Bits<0b10>; +def MxROOP_RO : MxBead2Bits<0b11>; + +/// ------------+---------+---+------+---+------+--------- +/// F E D C | B A 9 | 8 | 7 6 | 5 | 4 3 | 2 1 0 +/// ------------+---------+---+------+---+------+--------- +/// 1 1 1 0 | REG/IMM | D | SIZE |R/I| OP | REG +/// ------------+---------+---+------+---+------+--------- +class MxSREncoding_R + : MxEncoding, ROOP, MxBead1Bit<1>, SIZE, DIRECTION, + MxBeadReg<2>, MxBead4Bits<0b1110>>; + +class MxSREncoding_I + : MxEncoding, ROOP, MxBead1Bit<0>, SIZE, DIRECTION, + MxBead3Imm<2, 1>, MxBead4Bits<0b1110>>; + +// $reg <- $reg op $reg +class MxSR_DD + : MxInst<(outs TYPE.ROp:$dst), (ins TYPE.ROp:$src, TYPE.ROp:$opd), + MN#"."#TYPE.Prefix#"\t$opd, $dst", + [(set TYPE.VT:$dst, (NODE TYPE.VT:$src, TYPE.VT:$opd))], + MxSREncoding_R("MxEncSize"#TYPE.Size)>>; + +// $reg <- $reg op $imm +class MxSR_DI + : MxInst<(outs TYPE.ROp:$dst), (ins TYPE.ROp:$src, !cast("Mxi"#TYPE.Size#"imm"):$opd), + MN#"."#TYPE.Prefix#"\t$opd, $dst", + [(set TYPE.VT:$dst, (NODE TYPE.VT:$src, !cast("Mximm"#TYPE.Size#"_1to8"):$opd))], + MxSREncoding_I("MxEncSize"#TYPE.Size)>>; + +multiclass MxSROp { + +let Defs = [CCR] in { +let Constraints = "$src = $dst" in { + +def NAME#"8dd" : MxSR_DD; +def NAME#"16dd" : MxSR_DD; +def NAME#"32dd" : MxSR_DD; + +def NAME#"8di" : MxSR_DI; +def NAME#"16di" : MxSR_DI; +def NAME#"32di" : MxSR_DI; + +} // $src = $dst +} // Defs = [CCR] + +} // MxBiArOp_RF + +defm SHL : MxSROp<"lsl", shl, MxRODI_L, MxROOP_LS>; +defm LSR : MxSROp<"lsr", srl, MxRODI_R, MxROOP_LS>; +defm ASR : MxSROp<"asr", sra, MxRODI_R, MxROOP_AS>; + +defm ROL : MxSROp<"rol", rotl, MxRODI_L, MxROOP_RO>; +defm ROR : MxSROp<"ror", rotr, MxRODI_R, MxROOP_RO>; Index: lib/Target/M680x0/M680x0MCInstLower.h =================================================================== --- /dev/null +++ lib/Target/M680x0/M680x0MCInstLower.h @@ -0,0 +1,56 @@ +//===-- M680x0MCInstLower.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. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains code to lower M680x0 MachineInstrs to their +/// corresponding MCInst records. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_M680X0_M680X0MCINSTLOWER_H +#define LLVM_LIB_TARGET_M680X0_M680X0MCINSTLOWER_H + +#include "llvm/ADT/SmallVector.h" +#include "llvm/CodeGen/MachineOperand.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/Support/Compiler.h" + +namespace llvm { +class MCContext; +class MCInst; +class MCOperand; +class MachineInstr; +class MachineFunction; +class M680x0AsmPrinter; + +/// This class is used to lower an MachineInstr into an MCInst. +class M680x0MCInstLower { + typedef MachineOperand::MachineOperandType MachineOperandType; + MCContext &Ctx; + MachineFunction &MF; + const TargetMachine &TM; + const MCAsmInfo &MAI; + M680x0AsmPrinter &AsmPrinter; + +public: + M680x0MCInstLower(MachineFunction &MF, M680x0AsmPrinter &AP); + + /// Lower an MO_GlobalAddress or MO_ExternalSymbol operand to an MCSymbol. + MCSymbol *GetSymbolFromOperand(const MachineOperand &MO) const; + + MCOperand LowerSymbolOperand(const MachineOperand &MO, MCSymbol *Sym) const; + + Optional LowerOperand(const MachineInstr *MI, + const MachineOperand &MO) const; + + void Lower(const MachineInstr *MI, MCInst &OutMI) const; +}; +} // namespace llvm + +#endif Index: lib/Target/M680x0/M680x0MCInstLower.cpp =================================================================== --- /dev/null +++ lib/Target/M680x0/M680x0MCInstLower.cpp @@ -0,0 +1,176 @@ +//===-- M680x0MCInstLower.cpp - M680x0 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 +/// This file contains code to lower M680x0 MachineInstrs to their +/// corresponding MCInst records. +/// +//===----------------------------------------------------------------------===// + +#include "M680x0MCInstLower.h" + +#include "M680x0AsmPrinter.h" +#include "M680x0InstrInfo.h" + +#include "MCTargetDesc/M680x0BaseInfo.h" + +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineOperand.h" +#include "llvm/IR/Mangler.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCInst.h" + +using namespace llvm; + +M680x0MCInstLower::M680x0MCInstLower(MachineFunction &MF, M680x0AsmPrinter &AP) + : Ctx(MF.getContext()), MF(MF), TM(MF.getTarget()), MAI(*TM.getMCAsmInfo()), + AsmPrinter(AP) {} + +MCSymbol * +M680x0MCInstLower::GetSymbolFromOperand(const MachineOperand &MO) const { + assert((MO.isGlobal() || MO.isSymbol() || MO.isMBB()) && + "Isn't a symbol reference"); + + const DataLayout &DL = MF.getDataLayout(); + + MCSymbol *Sym = nullptr; + SmallString<128> Name; + StringRef Suffix; + + if (!Suffix.empty()) + Name += DL.getPrivateGlobalPrefix(); + + if (MO.isGlobal()) { + const GlobalValue *GV = MO.getGlobal(); + AsmPrinter.getNameWithPrefix(Name, GV); + } else if (MO.isSymbol()) { + Mangler::getNameWithPrefix(Name, MO.getSymbolName(), DL); + } else if (MO.isMBB()) { + assert(Suffix.empty()); + Sym = MO.getMBB()->getSymbol(); + } + + Name += Suffix; + if (!Sym) + Sym = Ctx.getOrCreateSymbol(Name); + + return Sym; +} + +MCOperand M680x0MCInstLower::LowerSymbolOperand(const MachineOperand &MO, + MCSymbol *Sym) const { + // FIXME: We would like an efficient form for this, so we don't have to do a + // lot of extra uniquing. + const MCExpr *Expr = nullptr; + MCSymbolRefExpr::VariantKind RefKind = MCSymbolRefExpr::VK_None; + + switch (MO.getTargetFlags()) { + default: + llvm_unreachable("Unknown target flag on GV operand"); + case M680x0II::MO_NO_FLAG: + case M680x0II::MO_ABSOLUTE_ADDRESS: + case M680x0II::MO_PC_RELATIVE_ADDRESS: + break; + case M680x0II::MO_GOTPCREL: + RefKind = MCSymbolRefExpr::VK_GOTPCREL; + break; + case M680x0II::MO_GOT: + RefKind = MCSymbolRefExpr::VK_GOT; + break; + case M680x0II::MO_GOTOFF: + RefKind = MCSymbolRefExpr::VK_GOTOFF; + break; + case M680x0II::MO_PLT: + RefKind = MCSymbolRefExpr::VK_PLT; + break; + } + + if (!Expr) { + Expr = MCSymbolRefExpr::create(Sym, RefKind, Ctx); + } + + if (!MO.isJTI() && !MO.isMBB() && MO.getOffset()) { + Expr = MCBinaryExpr::createAdd( + Expr, MCConstantExpr::create(MO.getOffset(), Ctx), Ctx); + } + + return MCOperand::createExpr(Expr); +} + +Optional +M680x0MCInstLower::LowerOperand(const MachineInstr *MI, + const MachineOperand &MO) const { + switch (MO.getType()) { + default: + MI->dump(); + llvm_unreachable("unknown operand type"); + case MachineOperand::MO_Register: + // Ignore all implicit register operands. + if (MO.isImplicit()) + return None; + return MCOperand::createReg(MO.getReg()); + case MachineOperand::MO_Immediate: + return MCOperand::createImm(MO.getImm()); + case MachineOperand::MO_MachineBasicBlock: + case MachineOperand::MO_GlobalAddress: + case MachineOperand::MO_ExternalSymbol: + return LowerSymbolOperand(MO, GetSymbolFromOperand(MO)); + case MachineOperand::MO_MCSymbol: + return LowerSymbolOperand(MO, MO.getMCSymbol()); + case MachineOperand::MO_JumpTableIndex: + return LowerSymbolOperand(MO, AsmPrinter.GetJTISymbol(MO.getIndex())); + case MachineOperand::MO_ConstantPoolIndex: + return LowerSymbolOperand(MO, AsmPrinter.GetCPISymbol(MO.getIndex())); + case MachineOperand::MO_BlockAddress: + return LowerSymbolOperand( + MO, AsmPrinter.GetBlockAddressSymbol(MO.getBlockAddress())); + case MachineOperand::MO_RegisterMask: + // Ignore call clobbers. + return None; + } +} + +void M680x0MCInstLower::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); + Optional MCOp = LowerOperand(MI, MO); + + if (MCOp.hasValue() && MCOp.getValue().isValid()) + OutMI.addOperand(MCOp.getValue()); + } + + switch (OutMI.getOpcode()) { + + // TAILJMPj, TAILJMPq - Lower to the correct jump instructions. + case M680x0::TAILJMPj: + case M680x0::TAILJMPq: { + unsigned Opcode; + switch (OutMI.getOpcode()) { + default: + llvm_unreachable("Invalid opcode"); + case M680x0::TAILJMPj: + Opcode = M680x0::JMP32j; + break; + case M680x0::TAILJMPq: + Opcode = M680x0::BRA8; + break; + } + + MCOperand Saved = OutMI.getOperand(0); + OutMI = MCInst(); + OutMI.setOpcode(Opcode); + OutMI.addOperand(Saved); + break; + } + } +} Index: lib/Target/M680x0/M680x0MachineFunction.h =================================================================== --- /dev/null +++ lib/Target/M680x0/M680x0MachineFunction.h @@ -0,0 +1,116 @@ +//===-- M680x0MachineFunctionInfo.h - M680x0 private data ---------*- 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 declares the M680x0 specific subclass of MachineFunctionInfo. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_M680X0_M680X0MACHINEFUNCTION_H +#define LLVM_LIB_TARGET_M680X0_M680X0MACHINEFUNCTION_H + +#include "llvm/CodeGen/CallingConvLower.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/Support/MachineValueType.h" + +namespace llvm { + +class M680x0MachineFunctionInfo : public MachineFunctionInfo { + MachineFunction &MF; + + /// Non-zero if the function has base pointer and makes call to + /// llvm.eh.sjlj.setjmp. When non-zero, the value is a displacement from the + /// frame pointer to a slot where the base pointer is stashed. + signed char RestoreBasePointerOffset = 0; + + /// Size of the callee-saved register portion of the stack frame in bytes. + unsigned CalleeSavedFrameSize = 0; + + /// Number of bytes function pops on return (in addition to the space used by + /// the return address). Used on windows platform for stdcall & fastcall + /// name decoration + unsigned BytesToPopOnReturn = 0; + + /// FrameIndex for return slot. + int ReturnAddrIndex = 0; + + /// The number of bytes by which return address stack slot is moved as the + /// result of tail call optimization. + int TailCallReturnAddrDelta = 0; + + /// keeps track of the virtual register initialized for use as the global + /// base register. This is used for PIC in some PIC relocation models. + unsigned GlobalBaseReg = 0; + + /// FrameIndex for start of varargs area. + int VarArgsFrameIndex = 0; + + /// Keeps track of whether this function uses sequences of pushes to pass + /// function parameters. + bool HasPushSequences = false; + + /// Some subtargets require that sret lowering includes + /// returning the value of the returned struct in a register. This field + /// holds the virtual register into which the sret argument is passed. + unsigned SRetReturnReg = 0; + + /// A list of virtual and physical registers that must be forwarded to every + /// musttail call. + SmallVector ForwardedMustTailRegParms; + + /// The number of bytes on stack consumed by the arguments being passed on + /// the stack. + unsigned ArgumentStackSize = 0; + +public: + M680x0MachineFunctionInfo() = default; + explicit M680x0MachineFunctionInfo(MachineFunction &MF) : MF(MF) {} + + bool getRestoreBasePointer() const { return RestoreBasePointerOffset != 0; } + void setRestoreBasePointer(const MachineFunction *MF); + int getRestoreBasePointerOffset() const { return RestoreBasePointerOffset; } + + unsigned getCalleeSavedFrameSize() const { return CalleeSavedFrameSize; } + void setCalleeSavedFrameSize(unsigned bytes) { CalleeSavedFrameSize = bytes; } + + unsigned getBytesToPopOnReturn() const { return BytesToPopOnReturn; } + void setBytesToPopOnReturn(unsigned bytes) { BytesToPopOnReturn = bytes; } + + int getRAIndex() const { return ReturnAddrIndex; } + void setRAIndex(int Index) { ReturnAddrIndex = Index; } + + int getTCReturnAddrDelta() const { return TailCallReturnAddrDelta; } + void setTCReturnAddrDelta(int delta) { TailCallReturnAddrDelta = delta; } + + unsigned getGlobalBaseReg() const { return GlobalBaseReg; } + void setGlobalBaseReg(unsigned Reg) { GlobalBaseReg = Reg; } + + int getVarArgsFrameIndex() const { return VarArgsFrameIndex; } + void setVarArgsFrameIndex(int Index) { VarArgsFrameIndex = Index; } + + bool getHasPushSequences() const { return HasPushSequences; } + void setHasPushSequences(bool HasPush) { HasPushSequences = HasPush; } + + unsigned getSRetReturnReg() const { return SRetReturnReg; } + void setSRetReturnReg(unsigned Reg) { SRetReturnReg = Reg; } + + unsigned getArgumentStackSize() const { return ArgumentStackSize; } + void setArgumentStackSize(unsigned size) { ArgumentStackSize = size; } + + SmallVectorImpl &getForwardedMustTailRegParms() { + return ForwardedMustTailRegParms; + } + +private: + virtual void anchor(); +}; + +} // end of namespace llvm + +#endif // M680X0_MACHINE_FUNCTION_INFO_H Index: lib/Target/M680x0/M680x0MachineFunction.cpp =================================================================== --- /dev/null +++ lib/Target/M680x0/M680x0MachineFunction.cpp @@ -0,0 +1,21 @@ +//===-- M680x0MachineFunctionInfo.cpp - M680x0 private data ----*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "M680x0MachineFunction.h" + +#include "M680x0InstrInfo.h" +#include "M680x0Subtarget.h" + +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/IR/Function.h" + +using namespace llvm; + +void M680x0MachineFunctionInfo::anchor() {} Index: lib/Target/M680x0/M680x0RegisterInfo.h =================================================================== --- /dev/null +++ lib/Target/M680x0/M680x0RegisterInfo.h @@ -0,0 +1,110 @@ +//===-- M680x0RegisterInfo.h - M680x0 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. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains the M680x0 implementation of the TargetRegisterInfo +/// class. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_M680X0_M680X0REGISTERINFO_H +#define LLVM_LIB_TARGET_M680X0_M680X0REGISTERINFO_H + +#include "M680x0.h" + +#include "llvm/CodeGen/TargetRegisterInfo.h" + +#define GET_REGINFO_HEADER +#include "M680x0GenRegisterInfo.inc" + +namespace llvm { +class M680x0Subtarget; +class TargetInstrInfo; +class Type; + +class M680x0RegisterInfo : public M680x0GenRegisterInfo { + virtual void anchor(); + + /// Physical register used as stack ptr. + unsigned StackPtr; + + /// Physical register used as frame ptr. + unsigned FramePtr; + + /// Physical register used as a base ptr in complex stack frames. I.e., when + /// we need a 3rd base, not just SP and FP, due to variable size stack + /// objects. + unsigned BasePtr; + + /// Physical register used to store GOT address if needed. + unsigned GlobalBasePtr; + +protected: + const M680x0Subtarget &Subtarget; + +public: + M680x0RegisterInfo(const M680x0Subtarget &Subtarget); + + const MCPhysReg *getCalleeSavedRegs(const MachineFunction *MF) const override; + + const uint32_t *getCallPreservedMask(const MachineFunction &MF, + CallingConv::ID) const override; + + /// Returns a register class with registers that can be used in forming tail + /// calls. + const TargetRegisterClass * + getRegsForTailCall(const MachineFunction &MF) const; + + /// Return a mega-register of the specified register Reg so its sub-register + /// of index SubIdx is Reg, its super(or mega) Reg. In other words it will + /// return a register that is not direct super register but still shares + /// physical register with Reg. + /// NOTE not sure about the term though. + unsigned getMatchingMegaReg(unsigned Reg, + const TargetRegisterClass *RC) const; + + /// Returns the Register Class of a physical register of the given type, + /// picking the biggest register class of the right type that contains this + /// physreg. + const TargetRegisterClass *getMaximalPhysRegClass(unsigned reg, MVT VT) const; + + /// Return index of a register within a register class, otherwise return -1 + int getRegisterOrder(unsigned Reg, const TargetRegisterClass &TRC) const; + + /// Return spill order index of a register, if there is none then trap + int getSpillRegisterOrder(unsigned Reg) const; + + BitVector getReservedRegs(const MachineFunction &MF) const override; + + bool requiresRegisterScavenging(const MachineFunction &MF) const override; + + bool trackLivenessAfterRegAlloc(const MachineFunction &MF) const override; + + /// FrameIndex represent objects inside a abstract stack. We must replace + /// FrameIndex with an stack/frame pointer direct reference. + void eliminateFrameIndex(MachineBasicBlock::iterator II, int SPAdj, + unsigned FIOperandNum, + RegScavenger *RS = nullptr) const override; + + bool hasBasePointer(const MachineFunction &MF) const; + + /// True if the stack can be realigned for the target. + bool canRealignStack(const MachineFunction &MF) const override; + + unsigned getFrameRegister(const MachineFunction &MF) const override; + unsigned getStackRegister() const { return StackPtr; } + unsigned getBaseRegister() const { return BasePtr; } + unsigned getGlobalBaseRegister() const { return GlobalBasePtr; } + + const TargetRegisterClass *intRegClass(unsigned Size) const; +}; + +} // end namespace llvm + +#endif Index: lib/Target/M680x0/M680x0RegisterInfo.cpp =================================================================== --- /dev/null +++ lib/Target/M680x0/M680x0RegisterInfo.cpp @@ -0,0 +1,288 @@ +//===-- M680x0RegisterInfo.cpp - CPU0 Register Information -----*- 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 the CPU0 implementation of the TargetRegisterInfo class. +/// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "m680x0-reg-info" + +#include "M680x0RegisterInfo.h" + +#include "M680x0.h" +#include "M680x0MachineFunction.h" +#include "M680x0Subtarget.h" + +#include "MCTargetDesc/M680x0MCTargetDesc.h" + +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Type.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" + +#define GET_REGINFO_TARGET_DESC +#include "M680x0GenRegisterInfo.inc" + +using namespace llvm; + +static cl::opt EnableBasePointer( + "m680x0-use-base-pointer", cl::Hidden, cl::init(true), + cl::desc("Enable use of a base pointer for complex stack frames")); + +// Pin the vtable to this file. +void M680x0RegisterInfo::anchor() {} + +M680x0RegisterInfo::M680x0RegisterInfo(const M680x0Subtarget &ST) + // FIXME not sure it this the correct value, it expects RA, but M680x0 + // passes IP anyway, how this works? + : M680x0GenRegisterInfo(M680x0::A0, + // FIXME Tablegen these + 0, 0, M680x0::PC), + Subtarget(ST) { + // FIXME would be nice to have tablegen level name aliasing + StackPtr = M680x0::SP; + FramePtr = M680x0::A6; + GlobalBasePtr = M680x0::A5; + BasePtr = M680x0::A4; +} + +//===----------------------------------------------------------------------===// +// Callee Saved Registers methods +//===----------------------------------------------------------------------===// + +const MCPhysReg * +M680x0RegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const { + return CSR_STD_SaveList; +} + +const uint32_t * +M680x0RegisterInfo::getCallPreservedMask(const MachineFunction &MF, + CallingConv::ID) const { + return CSR_STD_RegMask; +} + +const TargetRegisterClass * +M680x0RegisterInfo::getRegsForTailCall(const MachineFunction &MF) const { + return &M680x0::XR32_TCRegClass; +} + +unsigned +M680x0RegisterInfo::getMatchingMegaReg(unsigned Reg, + const TargetRegisterClass *RC) const { + for (MCSuperRegIterator Super(Reg, this); Super.isValid(); ++Super) + if (RC->contains(*Super)) + return *Super; + return 0; +} + +const TargetRegisterClass * +M680x0RegisterInfo::getMaximalPhysRegClass(unsigned reg, MVT VT) const { + assert(isPhysicalRegister(reg) && "reg must be a physical register"); + + // Pick the most sub register class of the right type that contains + // this physreg. + const TargetRegisterClass *BestRC = nullptr; + for (regclass_iterator I = regclass_begin(), E = regclass_end(); I != E; + ++I) { + const TargetRegisterClass *RC = *I; + if ((VT == MVT::Other || isTypeLegalForClass(*RC, VT)) && + RC->contains(reg) && + (!BestRC || + (BestRC->hasSubClass(RC) && RC->getNumRegs() > BestRC->getNumRegs()))) + BestRC = RC; + } + + assert(BestRC && "Couldn't find the register class"); + return BestRC; +} + +int M680x0RegisterInfo::getRegisterOrder(unsigned Reg, + const TargetRegisterClass &TRC) const { + for (unsigned i = 0; i < TRC.getNumRegs(); ++i) { + if (regsOverlap(Reg, TRC.getRegister(i))) { + return i; + } + } + return -1; +} + +int M680x0RegisterInfo::getSpillRegisterOrder(unsigned Reg) const { + int Result = getRegisterOrder(Reg, *getRegClass(M680x0::SPILLRegClassID)); + if (Result < 0) { + llvm_unreachable("Cannot determine spill order"); + } + return Result; +} + +BitVector M680x0RegisterInfo::getReservedRegs(const MachineFunction &MF) const { + const M680x0FrameLowering *TFI = getFrameLowering(MF); + + BitVector Reserved(getNumRegs()); + + // Set a register's and its sub-registers and aliases as reserved. + auto setBitVector = [&Reserved, this](unsigned Reg) { + for (MCRegAliasIterator I(Reg, this, /* self */ true); I.isValid(); ++I) { + Reserved.set(*I); + } + for (MCSubRegIterator I(Reg, this, /* self */ true); I.isValid(); ++I) { + Reserved.set(*I); + } + }; + + setBitVector(M680x0::PC); + setBitVector(M680x0::SP); + + if (TFI->hasFP(MF)) { + setBitVector(FramePtr); + } + + // Set the base-pointer register and its aliases as reserved if needed. + if (hasBasePointer(MF)) { + CallingConv::ID CC = MF.getFunction().getCallingConv(); + const uint32_t *RegMask = getCallPreservedMask(MF, CC); + if (MachineOperand::clobbersPhysReg(RegMask, getBaseRegister())) + report_fatal_error("Stack realignment in presence of dynamic allocas is " + "not supported with" + "this calling convention."); + + setBitVector(getBaseRegister()); + } + + // // Set the global-base-pointer register and its aliases as reserved if + // needed. if (getGlobalBaseRegister()) { + // for (MCSubRegIterator I(getGlobalBaseRegister(), this, + // #<{(|Self=|)}>#true); I.isValid(); ++I) + // Reserved.set(*I); + // } + + return Reserved; +} + +void M680x0RegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II, + int SPAdj, unsigned FIOperandNum, + RegScavenger *RS) const { + MachineInstr &MI = *II; + MachineFunction &MF = *MI.getParent()->getParent(); + const M680x0FrameLowering *TFI = getFrameLowering(MF); + + // We have either (i,An,Rn) or (i,An) EA form + // NOTE Base contains the FI and we need to backtrace a bit to get Disp + MachineOperand &Disp = MI.getOperand(FIOperandNum - 1); + MachineOperand &Base = MI.getOperand(FIOperandNum); + + int Imm = (int)(Disp.getImm()); + int FIndex = (int)(Base.getIndex()); + + // unsigned Opc = MI.getOpcode(); + // FIXME there is no jmp from mem yet + // bool AfterFPPop = Opc == M680x0::TAILJMPm || Opc == M680x0::TCRETURNmi; + bool AfterFPPop = false; + + unsigned BasePtr; + if (hasBasePointer(MF)) + BasePtr = (FIndex < 0 ? FramePtr : getBaseRegister()); + else if (needsStackRealignment(MF)) + BasePtr = (FIndex < 0 ? FramePtr : StackPtr); + else if (AfterFPPop) + BasePtr = StackPtr; + else + BasePtr = (TFI->hasFP(MF) ? FramePtr : StackPtr); + + Base.ChangeToRegister(BasePtr, false); + + // Now add the frame object offset to the offset from FP. + int FIOffset; + unsigned IgnoredFrameReg; + if (AfterFPPop) { + // Tail call jmp happens after FP is popped. + const MachineFrameInfo &MFI = MF.getFrameInfo(); + FIOffset = MFI.getObjectOffset(FIndex) - TFI->getOffsetOfLocalArea(); + } else { + FIOffset = TFI->getFrameIndexReference(MF, FIndex, IgnoredFrameReg); + } + + if (BasePtr == StackPtr) + FIOffset += SPAdj; + + long long Offset = FIOffset + Imm; + // if (Size == 16) { + // assert(isInt<16>(Offset) && "Cannot use disp greater 16 bit"); + // } else if (Size == 8) { + // assert(isInt<8>(Offset) && "Cannot use disp greater 8 bit"); + // } else { + // } + Disp.ChangeToImmediate(Offset); +} + +bool M680x0RegisterInfo::requiresRegisterScavenging( + const MachineFunction &MF) const { + return true; +} + +bool M680x0RegisterInfo::trackLivenessAfterRegAlloc( + const MachineFunction &MF) const { + return true; +} + +static bool CantUseSP(const MachineFrameInfo &MFI) { + return MFI.hasVarSizedObjects() || MFI.hasOpaqueSPAdjustment(); +} + +bool M680x0RegisterInfo::hasBasePointer(const MachineFunction &MF) const { + const MachineFrameInfo &MFI = MF.getFrameInfo(); + + if (!EnableBasePointer) + return false; + + // When we need stack realignment, we can't address the stack from the frame + // pointer. When we have dynamic allocas or stack-adjusting inline asm, we + // can't address variables from the stack pointer. MS inline asm can + // reference locals while also adjusting the stack pointer. When we can't + // use both the SP and the FP, we need a separate base pointer register. + bool CantUseFP = needsStackRealignment(MF); + return CantUseFP && CantUseSP(MFI); +} + +bool M680x0RegisterInfo::canRealignStack(const MachineFunction &MF) const { + if (!TargetRegisterInfo::canRealignStack(MF)) + return false; + + const MachineFrameInfo &MFI = MF.getFrameInfo(); + const MachineRegisterInfo *MRI = &MF.getRegInfo(); + + // Stack realignment requires a frame pointer. If we already started + // register allocation with frame pointer elimination, it is too late now. + if (!MRI->canReserveReg(FramePtr)) + return false; + + // If a base pointer is necessary. Check that it isn't too late to reserve it. + if (CantUseSP(MFI)) + return MRI->canReserveReg(BasePtr); + + return true; +} + +unsigned M680x0RegisterInfo::getFrameRegister(const MachineFunction &MF) const { + const TargetFrameLowering *TFI = MF.getSubtarget().getFrameLowering(); + return TFI->hasFP(MF) ? FramePtr : StackPtr; +} + +const TargetRegisterClass * +M680x0RegisterInfo::intRegClass(unsigned size) const { + // if (isInt<8>(size)) { + // return &M680x0::DR8RegClass; + // } else if (isInt<16>(size)) { + // return &M680x0::DR16RegClass; + // } + return &M680x0::DR32RegClass; +} Index: lib/Target/M680x0/M680x0RegisterInfo.td =================================================================== --- /dev/null +++ lib/Target/M680x0/M680x0RegisterInfo.td @@ -0,0 +1,133 @@ +//== M680x0RegisterInfo.td - M680x0 register definitions ----*- tablegen -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file describes the M680x0 Register file, defining the registers +/// aliases between the registers, and the register classes built out of the +/// registers. +/// +//===----------------------------------------------------------------------===// + +class MxReg ENC, + list SUBREGS = [], list SUBIDX, + list DWREGS = []> + : Register, DwarfRegNum { + let Namespace = "M680x0"; + let HWEncoding = ENC; + let SubRegs = SUBREGS; + let SubRegIndices = SUBIDX; +} + +// Subregister indices. +let Namespace = "M680x0" in { + def MxSubRegIndex8Lo : SubRegIndex<8, 0>; + def MxSubRegIndex16Lo : SubRegIndex<16, 0>; +} + +// Generate Data registers and theirs smaller variants +foreach Index = 0-7 in { + def "BD"#Index : MxReg<"d"#Index, #Index, [], [], [Index]>; + + def "WD"#Index + : MxReg<"d"#Index, #Index, + [!cast("BD"#Index)], [MxSubRegIndex8Lo], + [Index]>; + + def "D"#Index + : MxReg<"d"#Index, #Index, + [!cast("WD"#Index)], [MxSubRegIndex16Lo], + [Index]>; + +} // foreach + +// Generate Address registers and theirs smaller variants +foreach Index = 0-7 in { + def "WA"#Index + : MxReg<"a"#Index, #Index, [], [], [!add(8,Index)]>; + + def "A"#Index + : MxReg<"a"#Index, #Index, + [!cast("WA"#Index)], [MxSubRegIndex16Lo], + [!add(8,Index)]>; +} + +// Alias Registers +class MxAliasReg + : MxReg { + let Aliases = [REG]; +} + +// def BP : MxAliasReg<"bp", A5>; +// def FP : MxAliasReg<"fp", A6>; +def SP : MxAliasReg<"sp", A7>; + +// def USP : MxAliasReg<"usp", A7>; +// def SSP : MxAliasReg<"ssp", A7>; +// def ISP : MxAliasReg<"isp", A7>; + +// TODO get rid of "pseudo" registers and use just MxReg variants and use +// HWEncoding's other 13 bits to encode type(and potentially other info) of +// register. + +// Pseudo Registers +class MxPseudoReg SUBREGS = [], list SUBIDX = []> + : MxReg; + +def CCR : MxPseudoReg<"ccr">; +def SR : MxPseudoReg<"sr", [], []>; + +def PC : MxPseudoReg<"pc">; + +//===----------------------------------------------------------------------===// +// Register Classes +//===----------------------------------------------------------------------===// + +class MxRegClass regTypes, int alignment, dag regList> + : RegisterClass<"M680x0", regTypes, alignment, regList>; + +// Data Registers +def DR8 : MxRegClass<[i8], 16, (sequence "BD%u", 0, 7)>; +def DR16 : MxRegClass<[i16], 16, (sequence "WD%u", 0, 7)>; +def DR32 : MxRegClass<[i32], 32, (sequence "D%u", 0, 7)>; + +// Address Registers +def AR16 : MxRegClass<[i16], 16, (sequence "WA%u", 0, 6)>; +def AR32 : MxRegClass<[i32], 32, (add (sequence "A%u", 0, 6), SP)>; + +def AR32_NOSP : MxRegClass<[i32], 32, (add (sequence "A%u", 0, 6))>; + +// Index Register Classes +// FIXME try order D0, D1, A0, A1, ... +def XR16 : MxRegClass<[i16], 16, (add DR16, AR16)>; +def XR32 : MxRegClass<[i32], 32, (add DR32, AR32)>; + +def SPC : MxRegClass<[i32], 32, (add SP)>; + +let CopyCost = -1 in { + def CCRC : MxRegClass<[i8], 16, (add CCR)>; + def SRC : MxRegClass<[i16], 16, (add SR)>; +} + +let isAllocatable = 0 in { + def PCC : MxRegClass<[i32], 32, (add PC)>; +} + +// Register used with tail call +def DR16_TC : MxRegClass<[i16], 16, (add D0, D1)>; +def DR32_TC : MxRegClass<[i32], 32, (add D0, D1)>; + +def AR16_TC : MxRegClass<[i16], 16, (add A0, A1)>; +def AR32_TC : MxRegClass<[i32], 32, (add A0, A1)>; + +def XR16_TC : MxRegClass<[i16], 16, (add DR16_TC, AR16_TC)>; +def XR32_TC : MxRegClass<[i32], 32, (add DR32_TC, AR32_TC)>; + +// These classes provide spill/restore order if used with MOVEM instruction +def SPILL : MxRegClass<[i32], 32, (add (add (sequence "D%u", 0, 7), (sequence "A%u", 0, 6)), SP)>; +def SPILL_R : MxRegClass<[i32], 32, (add SP, (add (sequence "A%u", 6, 0), (sequence "D%u", 7, 0)))>; Index: lib/Target/M680x0/M680x0Schedule.td =================================================================== --- /dev/null +++ lib/Target/M680x0/M680x0Schedule.td @@ -0,0 +1,24 @@ +//===-- M680x0Schedule.td - M680x0 Scheduling Definitions --*- tablegen -*-===// +// +// 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 M680x0 scheduler definitions. +/// +//===----------------------------------------------------------------------===// + +/// This is a very general M680x0 Scheduling Model and best suited for the very +/// first M68000 CPU, other model must override these characteristics +class M680x0SchedModel : SchedMachineModel { + let LoadLatency = 4; // Word (Rn) + let HighLatency = 16; // Long ABS + let PostRAScheduler = 0; + let CompleteModel = 0; +} + +def GenericM680x0Model : M680x0SchedModel; Index: lib/Target/M680x0/M680x0Subtarget.h =================================================================== --- /dev/null +++ lib/Target/M680x0/M680x0Subtarget.h @@ -0,0 +1,159 @@ +//===-- M680x0Subtarget.h - Define Subtarget for the M680x0 -----*- 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 declares the M680x0 specific subclass of TargetSubtargetInfo. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_CPU0_M680X0SUBTARGET_H +#define LLVM_LIB_TARGET_CPU0_M680X0SUBTARGET_H + +#include "M680x0FrameLowering.h" +#include "M680x0ISelLowering.h" +#include "M680x0InstrInfo.h" + +#include "llvm/CodeGen/SelectionDAGTargetInfo.h" +#include "llvm/CodeGen/TargetSubtargetInfo.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/MC/MCInstrItineraries.h" + +#include + +#define GET_SUBTARGETINFO_HEADER +#include "M680x0GenSubtargetInfo.inc" + +extern bool M680x0ReserveGP; +extern bool M680x0NoCpload; + +namespace llvm { +class StringRef; + +class M680x0TargetMachine; + +class M680x0Subtarget : public M680x0GenSubtargetInfo { + virtual void anchor(); + +protected: + // These define which ISA is supported. Since each Motorola M680x0 ISA is + // built on top of the previous one whenever an ISA is selected the previous + // selected as well. + bool IsM68000 = false; + bool IsM68010 = false; + bool IsM68020 = false; + bool IsM68030 = false; + bool IsM68040 = false; + + InstrItineraryData InstrItins; + + /// Small section is used. + bool UseSmallSection = true; + + const M680x0TargetMachine &TM; + + SelectionDAGTargetInfo TSInfo; + M680x0InstrInfo InstrInfo; + M680x0FrameLowering FrameLowering; + M680x0TargetLowering TLInfo; + + /// The minimum alignment known to hold of the stack frame on + /// entry to the function and which must be maintained by every function. + unsigned stackAlignment = 8; + + Triple TargetTriple; + +public: + /// This constructor initializes the data members to match that + /// of the specified triple. + M680x0Subtarget(const Triple &TT, const std::string &CPU, + const std::string &FS, const M680x0TargetMachine &_TM); + + /// Parses features string setting specified subtarget options. Definition + /// of function is auto generated by tblgen. + void ParseSubtargetFeatures(StringRef CPU, StringRef FS); + + bool isM68000() const { return IsM68000; } + bool isM68010() const { return IsM68010; } + bool isM68020() const { return IsM68020; } + bool isM68030() const { return IsM68030; } + bool isM68040() const { return IsM68040; } + + bool useSmallSection() const { return UseSmallSection; } + + bool abiUsesSoftFloat() const; + + const Triple &getTargetTriple() const { return TargetTriple; } + + bool isTargetELF() const { return TargetTriple.isOSBinFormatELF(); } + + /// Return true if the subtarget allows calls to immediate address. + bool isLegalToCallImmediateAddr() const; + + bool isPositionIndependent() const; + + /// Classify a global variable reference for the current subtarget according + /// to how we should reference it in a non-pcrel context. + unsigned char classifyLocalReference(const GlobalValue *GV) const; + + /// Classify a global variable reference for the current subtarget according + /// to how we should reference it in a non-pcrel context. + unsigned char classifyGlobalReference(const GlobalValue *GV, + const Module &M) const; + unsigned char classifyGlobalReference(const GlobalValue *GV) const; + + /// Classify a external variable reference for the current subtarget according + /// to how we should reference it in a non-pcrel context. + unsigned char classifyExternalReference(const Module &M) const; + + /// Classify a global function reference for the current subtarget. + unsigned char classifyGlobalFunctionReference(const GlobalValue *GV, + const Module &M) const; + unsigned char classifyGlobalFunctionReference(const GlobalValue *GV) const; + + /// Classify a blockaddress reference for the current subtarget according to + /// how we should reference it in a non-pcrel context. + unsigned char classifyBlockAddressReference() const; + + unsigned getJumpTableEncoding() const; + + /// TODO this must be controlled by options like -malign-int and -mshort + unsigned getStackAlignment() const { return stackAlignment; } + + /// getSlotSize - Stack slot size in bytes. + unsigned getSlotSize() const { return 4; } + + M680x0Subtarget & + initializeSubtargetDependencies(StringRef CPU, Triple TT, StringRef FS, + const M680x0TargetMachine &TM); + + const SelectionDAGTargetInfo *getSelectionDAGInfo() const override { + return &TSInfo; + } + + const M680x0InstrInfo *getInstrInfo() const override { return &InstrInfo; } + + const M680x0FrameLowering *getFrameLowering() const override { + return &FrameLowering; + } + + const M680x0RegisterInfo *getRegisterInfo() const override { + return &InstrInfo.getRegisterInfo(); + } + + const M680x0TargetLowering *getTargetLowering() const override { + return &TLInfo; + } + + const InstrItineraryData *getInstrItineraryData() const override { + return &InstrItins; + } +}; +} // namespace llvm + +#endif Index: lib/Target/M680x0/M680x0Subtarget.cpp =================================================================== --- /dev/null +++ lib/Target/M680x0/M680x0Subtarget.cpp @@ -0,0 +1,251 @@ +//===-- M680x0Subtarget.cpp - M680x0 Subtarget Information ------*- 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 implements the M680x0 specific subclass of TargetSubtargetInfo. +/// +//===----------------------------------------------------------------------===// + +#include "M680x0Subtarget.h" + +#include "M680x0MachineFunction.h" +#include "M680x0.h" +#include "M680x0RegisterInfo.h" +#include "M680x0TargetMachine.h" + +#include "llvm/IR/Attributes.h" +#include "llvm/IR/Function.h" +#include "llvm/CodeGen/MachineJumpTableInfo.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/TargetRegistry.h" + +using namespace llvm; + +#define DEBUG_TYPE "m680x0-subtarget" + +#define GET_SUBTARGETINFO_TARGET_DESC +#define GET_SUBTARGETINFO_CTOR +#include "M680x0GenSubtargetInfo.inc" + +extern bool FixGlobalBaseReg; + +/// Select the M680x0 CPU for the given triple and cpu name. +/// FIXME: Merge with the copy in M680x0MCTargetDesc.cpp +static StringRef selectM680x0CPU(Triple TT, StringRef CPU) { + if (CPU.empty() || CPU == "generic") { + CPU = "M68000"; + } + return CPU; +} + +void M680x0Subtarget::anchor() { } + +M680x0Subtarget:: +M680x0Subtarget(const Triple &TT, const std::string &CPU, + const std::string &FS, + const M680x0TargetMachine &TM) : + M680x0GenSubtargetInfo(TT, CPU, FS), TM(TM), TSInfo(), + InstrInfo(initializeSubtargetDependencies(CPU, TT, FS, TM)), + FrameLowering(*this, this->getStackAlignment()), + TLInfo(TM, *this), TargetTriple(TT) { +} + +bool M680x0Subtarget:: +isPositionIndependent() const { return TM.isPositionIndependent(); } + +bool M680x0Subtarget:: +isLegalToCallImmediateAddr() const { return true; } + +bool M680x0Subtarget:: +abiUsesSoftFloat() const { +// return TM->Options.UseSoftFloat; + return true; +} + +M680x0Subtarget & M680x0Subtarget:: +initializeSubtargetDependencies(StringRef CPU, Triple TT, StringRef FS, + const M680x0TargetMachine &TM) { + std::string CPUName = selectM680x0CPU(TT, CPU); + + // Parse features string. + // ParseSubtargetFeatures(CPUName, FS); + // The only CPU supported thus far + IsM68000 = true; + + // Initialize scheduling itinerary for the specified CPU. + InstrItins = getInstrItineraryForCPU(CPUName); + + // Default stack alignment is 8 bytes, ??? Do I need this override? + // if (StackAlignOverride) + // stackAlignment = StackAlignOverride; + // else + stackAlignment = 8; + + return *this; +} + +//===----------------------------------------------------------------------===// +// Code Model +// +// Key assumptions: +// - Whenever possible we use pc-rel encoding since it is smaller(16 bit) than +// absolute(32 bit). +// - GOT is reachable within 16 bit offset for both Small and Medium models. +// - Code section is reachable within 16 bit offset for both models. +// +// ---------------------+-------------------------+-------------------------- +// | Small | Medium +// +-------------------------+------------+------------- +// | Static | PIC | Static | PIC +// ---------------------+------------+------------+------------+------------- +// branch | pc-rel | pc-rel | pc-rel | pc-rel +// ---------------------+------------+------------+------------+------------- +// call global | @PLT | @PLT | @PLT | @PLT +// ---------------------+------------+------------+------------+------------- +// call internal | pc-rel | pc-rel | pc-rel | pc-rel +// ---------------------+------------+------------+------------+------------- +// data local | pc-rel | pc-rel | ~pc-rel | ^pc-rel +// ---------------------+------------+------------+------------+------------- +// data local big* | pc-rel | pc-rel | absolute | @GOTOFF +// ---------------------+------------+------------+------------+------------- +// data global | pc-rel | @GOTPCREL | ~pc-rel | @GOTPCREL +// ---------------------+------------+------------+------------+------------- +// data global big* | pc-rel | @GOTPCREL | absolute | @GOTPCREL +// ---------------------+------------+------------+------------+------------- +// +// * Big data potentially cannot be reached within 16 bit offset and requires +// special handling for old(x00 and x10) CPUs. Normally these symbols go into +// separate .ldata section which mapped after normal .data and .text, but I +// don't really know how this must be done for M680x0 atm... will try to dig +// this info out from GCC. For now CPUs prior to M68020 will use static ref +// for Static Model and @GOT based references for PIC. +// +// ~ These are absolute for older CPUs for now. +// ^ These are @GOTOFF for older CPUs for now. +//===----------------------------------------------------------------------===// + +/// Classify a blockaddress reference for the current subtarget according to how +/// we should reference it in a non-pcrel context. +unsigned char M680x0Subtarget::classifyBlockAddressReference() const { + // Unless we start to support Large Code Model branching is always pc-rel + return M680x0II::MO_PC_RELATIVE_ADDRESS; +} + +unsigned char M680x0Subtarget:: +classifyLocalReference(const GlobalValue *GV) const { + switch (TM.getCodeModel()) { + default: llvm_unreachable("Unsupported code model"); + case CodeModel::Small: + case CodeModel::Kernel: { + return M680x0II::MO_PC_RELATIVE_ADDRESS; + } + case CodeModel::Medium: { + if (isPositionIndependent()) { + // On M68020 and better we can fit big any data offset into dips field. + if (IsM68020) { + return M680x0II::MO_PC_RELATIVE_ADDRESS; + } + // Otherwise we could check the data size and make sure it will fit into + // 16 bit offset. For now we will be conservative and go with @GOTOFF + return M680x0II::MO_GOTOFF; + } else { + if (IsM68020) { + return M680x0II::MO_PC_RELATIVE_ADDRESS; + } + return M680x0II::MO_ABSOLUTE_ADDRESS; + } + } + } +} + +unsigned char M680x0Subtarget:: +classifyExternalReference(const Module &M) const { + if (TM.shouldAssumeDSOLocal(M, nullptr)) + return classifyLocalReference(nullptr); + + if (isPositionIndependent()) { + return M680x0II::MO_GOTPCREL; + } else { + return M680x0II::MO_GOT; + } +} + +unsigned char M680x0Subtarget:: +classifyGlobalReference(const GlobalValue *GV) const { + return classifyGlobalReference(GV, *GV->getParent()); +} + +unsigned char M680x0Subtarget:: +classifyGlobalReference(const GlobalValue *GV, const Module &M) const { + if (TM.shouldAssumeDSOLocal(M, GV)) + return classifyLocalReference(GV); + + switch (TM.getCodeModel()) { + default: llvm_unreachable("Unsupported code model"); + case CodeModel::Small: + case CodeModel::Kernel: { + if (isPositionIndependent()) { + return M680x0II::MO_GOTPCREL; + } else { + return M680x0II::MO_PC_RELATIVE_ADDRESS; + } + } + case CodeModel::Medium: { + if (isPositionIndependent()) { + return M680x0II::MO_GOTPCREL; + } else { + if (IsM68020) { + return M680x0II::MO_PC_RELATIVE_ADDRESS; + } + return M680x0II::MO_ABSOLUTE_ADDRESS; + } + } + } +} + +unsigned M680x0Subtarget:: +getJumpTableEncoding() const { + if (isPositionIndependent()) { + // The only time we want to use GOTOFF(used when with EK_Custom32) is when + // the potential delta between the jump target and table base can be larger + // than displacement field, which is True for older CPUs(16 bit disp) + // in Medium model(can have large data way beyond 16 bit). + if (TM.getCodeModel() == CodeModel::Medium && !isM68020()) + return MachineJumpTableInfo::EK_Custom32; + + return MachineJumpTableInfo::EK_LabelDifference32; + } + + // In non-pic modes, just use the address of a block. + return MachineJumpTableInfo::EK_BlockAddress; +} + +unsigned char M680x0Subtarget:: +classifyGlobalFunctionReference(const GlobalValue *GV) const { + return classifyGlobalFunctionReference(GV, *GV->getParent()); +} + +unsigned char M680x0Subtarget:: +classifyGlobalFunctionReference(const GlobalValue *GV, const Module &M) const { + // local always use pc-rel referencing + if (TM.shouldAssumeDSOLocal(M, GV)) + return M680x0II::MO_NO_FLAG; + + // If the function is marked as non-lazy, generate an indirect call + // which loads from the GOT directly. This avoids runtime overhead + // at the cost of eager binding. + auto *F = dyn_cast_or_null(GV); + if (F && F->hasFnAttribute(Attribute::NonLazyBind)) { + return M680x0II::MO_GOTPCREL; + } + + // otherwise linker will figure this out + return M680x0II::MO_PLT; +} Index: lib/Target/M680x0/M680x0TargetMachine.h =================================================================== --- /dev/null +++ lib/Target/M680x0/M680x0TargetMachine.h @@ -0,0 +1,57 @@ +//===-- M680x0TargetMachine.h - Define TargetMachine for M680x0 ----- 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 declares the M680x0 specific subclass of TargetMachine. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_M680X0_M680X0TARGETMACHINE_H +#define LLVM_LIB_TARGET_M680X0_M680X0TARGETMACHINE_H + +#include "M680x0Subtarget.h" +#include "MCTargetDesc/M680x0MCTargetDesc.h" + +#include "llvm/CodeGen/Passes.h" +#include "llvm/CodeGen/SelectionDAGISel.h" +#include "llvm/CodeGen/TargetFrameLowering.h" +#include "llvm/Target/TargetMachine.h" + +namespace llvm { +class formatted_raw_ostream; +class M680x0RegisterInfo; + +class M680x0TargetMachine : public LLVMTargetMachine { + std::unique_ptr TLOF; + M680x0Subtarget Subtarget; + + mutable StringMap> SubtargetMap; + +public: + M680x0TargetMachine(const Target &T, const Triple &TT, StringRef CPU, + StringRef FS, const TargetOptions &Options, + Optional RM, Optional CM, + CodeGenOpt::Level OL, bool JIT); + + ~M680x0TargetMachine() override; + + const M680x0Subtarget *getSubtargetImpl() const { return &Subtarget; } + + const M680x0Subtarget *getSubtargetImpl(const Function &F) const override; + + // Pass Pipeline Configuration + TargetPassConfig *createPassConfig(PassManagerBase &PM) override; + + TargetLoweringObjectFile *getObjFileLowering() const override { + return TLOF.get(); + } +}; +} // namespace llvm + +#endif Index: lib/Target/M680x0/M680x0TargetMachine.cpp =================================================================== --- /dev/null +++ lib/Target/M680x0/M680x0TargetMachine.cpp @@ -0,0 +1,169 @@ +//===-- M680x0TargetMachine.cpp - M680x0 target machine ---------*- 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 implementation for M680x0 target machine. +/// +//===----------------------------------------------------------------------===// + +#include "M680x0.h" +#include "M680x0TargetMachine.h" + +#include "M680x0Subtarget.h" +#include "M680x0TargetObjectFile.h" + +#include "llvm/CodeGen/Passes.h" +#include "llvm/CodeGen/TargetPassConfig.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/Support/TargetRegistry.h" + +using namespace llvm; + +#define DEBUG_TYPE "m680x0" + +extern "C" void LLVMInitializeM680x0Target() { + RegisterTargetMachine X(TheM680x0Target); +} + +namespace { + +// FIXME this layout is true for M68000 original cpu, other variants will +// affect DL computation +std::string computeDataLayout(const Triple &TT, StringRef CPU, + const TargetOptions &Options) { + std::string Ret = ""; + // M680x0 is Big Endian + Ret += "E"; + + // FIXME how to wire it with the used object format? + Ret += "-m:e"; + + // M680x0 pointers are always 32 bit wide even for 16 bit cpus + Ret += "-p:32:32"; + + // M680x0 requires i8 to align on 2 byte boundry + Ret += "-i8:8:8-i16:16:16-i32:32:32"; + + // FIXME no floats at the moment + + // The registers can hold 8, 16, 32 bits + Ret += "-n8:16:32"; + + // Aggregates are 32 bit aligned and stack is 16 bit aligned + Ret += "-a:0:32-S16"; + + return Ret; +} + +Reloc::Model getEffectiveRelocModel(const Triple &TT, + Optional RM) { + // If not defined we default to static + if (!RM.hasValue()) { + return Reloc::Static; + } + + return *RM; +} + +CodeModel::Model getEffectiveCodeModel(Optional CM, + bool JIT) { + if (!CM) { + return CodeModel::Small; + } else if (CM == CodeModel::Large) { + llvm_unreachable("Large code model is not supported"); + } else if (CM == CodeModel::Kernel) { + // FIXME Kernel afaik is small cm plus some weird binding + llvm_unreachable("Kernel code model is not supported"); + } + return CM.getValue(); +} +} // end anonymous namespace + +M680x0TargetMachine::M680x0TargetMachine(const Target &T, const Triple &TT, + StringRef CPU, StringRef FS, + const TargetOptions &Options, + Optional RM, + Optional CM, + CodeGenOpt::Level OL, bool JIT) + : LLVMTargetMachine(T, computeDataLayout(TT, CPU, Options), TT, CPU, FS, + Options, getEffectiveRelocModel(TT, RM), + getEffectiveCodeModel(CM, JIT), OL), + TLOF(make_unique()), + Subtarget(TT, CPU, FS, *this) { + initAsmInfo(); +} + +M680x0TargetMachine::~M680x0TargetMachine() {} + +const M680x0Subtarget * +M680x0TargetMachine::getSubtargetImpl(const Function &F) const { + Attribute CPUAttr = F.getFnAttribute("target-cpu"); + Attribute FSAttr = F.getFnAttribute("target-features"); + + std::string CPU = !CPUAttr.hasAttribute(Attribute::None) + ? CPUAttr.getValueAsString().str() + : TargetCPU; + std::string FS = !FSAttr.hasAttribute(Attribute::None) + ? FSAttr.getValueAsString().str() + : TargetFS; + + auto &I = SubtargetMap[CPU + FS]; + if (!I) { + // This needs to be done before we create a new subtarget since any + // creation will depend on the TM and the code generation flags on the + // function that reside in TargetOptions. + resetTargetOptions(F); + I = llvm::make_unique(TargetTriple, CPU, FS, *this); + } + return I.get(); +} + +//===----------------------------------------------------------------------===// +// Pass Pipeline Configuration +//===----------------------------------------------------------------------===// + +namespace { +class M680x0PassConfig : public TargetPassConfig { +public: + M680x0PassConfig(M680x0TargetMachine &TM, PassManagerBase &PM) + : TargetPassConfig(TM, PM) {} + + M680x0TargetMachine &getM680x0TargetMachine() const { + return getTM(); + } + + const M680x0Subtarget &getM680x0Subtarget() const { + return *getM680x0TargetMachine().getSubtargetImpl(); + } + + bool addInstSelector() override; + void addPreSched2() override; + void addPreEmitPass() override; +}; +} // namespace + +TargetPassConfig *M680x0TargetMachine::createPassConfig(PassManagerBase &PM) { + return new M680x0PassConfig(*this, PM); +} + +bool M680x0PassConfig::addInstSelector() { + // Install an instruction selector. + addPass(createM680x0ISelDag(getM680x0TargetMachine())); + addPass(createM680x0GlobalBaseRegPass()); + return false; +} + +void M680x0PassConfig::addPreSched2() { + addPass(createM680x0ExpandPseudoPass()); +} + +void M680x0PassConfig::addPreEmitPass() { + addPass(createM680x0CollapseMOVEMPass()); + // addPass(createM680x0ConvertMOVToMOVMPass()); +} Index: lib/Target/M680x0/M680x0TargetObjectFile.h =================================================================== --- /dev/null +++ lib/Target/M680x0/M680x0TargetObjectFile.h @@ -0,0 +1,34 @@ +//===-- M680x0ELFTargetObjectFile.h - M680x0 Object Info ---------*- 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 declarations for M680x0 ELF object file lowering. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_M680X0_M680X0TARGETOBJECTFILE_H +#define LLVM_LIB_TARGET_M680X0_M680X0TARGETOBJECTFILE_H + +#include "M680x0TargetMachine.h" + +#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" + +namespace llvm { +class M680x0TargetMachine; +class M680x0ELFTargetObjectFile : public TargetLoweringObjectFileELF { + const M680x0TargetMachine *TM; + MCSection *SmallDataSection; + MCSection *SmallBSSSection; + +public: + void Initialize(MCContext &Ctx, const TargetMachine &TM) override; +}; +} // end namespace llvm + +#endif Index: lib/Target/M680x0/M680x0TargetObjectFile.cpp =================================================================== --- /dev/null +++ lib/Target/M680x0/M680x0TargetObjectFile.cpp @@ -0,0 +1,49 @@ +//===-- M680x0ELFTargetObjectFile.cpp - M680x0 Object Files -----*- 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 definitions for M680x0 ELF object file lowering. +/// +//===----------------------------------------------------------------------===// + +#include "M680x0TargetObjectFile.h" + +#include "M680x0Subtarget.h" +#include "M680x0TargetMachine.h" + +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/GlobalVariable.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCSectionELF.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Target/TargetMachine.h" + +using namespace llvm; + +static cl::opt SSThreshold( + "m680x0-ssection-threshold", cl::Hidden, + cl::desc("Small data and bss section threshold size (default=8)"), + cl::init(8)); + +void M680x0ELFTargetObjectFile::Initialize(MCContext &Ctx, + const TargetMachine &TM) { + TargetLoweringObjectFileELF::Initialize(Ctx, TM); + InitializeELF(TM.Options.UseInitArray); + + this->TM = &static_cast(TM); + + // FIXME do i need them explicitly? + SmallDataSection = getContext().getELFSection( + ".sdata", ELF::SHT_PROGBITS, ELF::SHF_WRITE | ELF::SHF_ALLOC); + + SmallBSSSection = getContext().getELFSection(".sbss", ELF::SHT_NOBITS, + ELF::SHF_WRITE | ELF::SHF_ALLOC); +} Index: lib/Target/M680x0/MCTargetDesc/CMakeLists.txt =================================================================== --- /dev/null +++ lib/Target/M680x0/MCTargetDesc/CMakeLists.txt @@ -0,0 +1,7 @@ +add_llvm_library(LLVMM680x0Desc + M680x0AsmBackend.cpp + M680x0MCTargetDesc.cpp + M680x0MCAsmInfo.cpp + M680x0MCCodeEmitter.cpp + M680x0ELFObjectWriter.cpp +) Index: lib/Target/M680x0/MCTargetDesc/LLVMBuild.txt =================================================================== --- /dev/null +++ lib/Target/M680x0/MCTargetDesc/LLVMBuild.txt @@ -0,0 +1,23 @@ +;===- ./lib/Target/M680x0/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 = M680x0Desc +parent = M680x0 +required_libraries = MC MCDisassembler Support M680x0AsmPrinter M680x0Info +add_to_library_groups = M680x0 Index: lib/Target/M680x0/MCTargetDesc/M680x0AsmBackend.cpp =================================================================== --- /dev/null +++ lib/Target/M680x0/MCTargetDesc/M680x0AsmBackend.cpp @@ -0,0 +1,248 @@ +//===-- M680x0AsmBackend.cpp - M680x0 Assembler Backend ---------*- 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 definitions for M680x0 assembler backend. +/// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/M680x0BaseInfo.h" +#include "MCTargetDesc/M680x0FixupKinds.h" + +#include "llvm/ADT/StringSwitch.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/BinaryFormat/MachO.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCELFObjectWriter.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCFixupKindInfo.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCMachObjectWriter.h" +#include "llvm/MC/MCObjectWriter.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSectionCOFF.h" +#include "llvm/MC/MCSectionELF.h" +#include "llvm/MC/MCSectionMachO.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +namespace { + +class M680x0AsmBackend : public MCAsmBackend { + +public: + M680x0AsmBackend(const Target &T) : MCAsmBackend(support::big) {} + + unsigned getNumFixupKinds() const override { + return llvm::M680x0::NumTargetFixupKinds; + } + + void applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, + const MCValue &Target, MutableArrayRef Data, + uint64_t Value, bool IsResolved, + const MCSubtargetInfo *STI) const override { + unsigned Size = 1 << getFixupKindLog2Size(Fixup.getKind()); + + assert(Fixup.getOffset() + Size <= Data.size() && "Invalid fixup offset!"); + + // Check that uppper bits are either all zeros or all ones. + // Specifically ignore overflow/underflow as long as the leakage is + // limited to the lower bits. This is to remain compatible with + // other assemblers. + assert(isIntN(Size * 8 + 1, Value) && + "Value does not fit in the Fixup field"); + + // Write in Big Endian + for (unsigned i = 0; i != Size; ++i) + Data[Fixup.getOffset() + i] = uint8_t(Value >> ((Size - i - 1) * 8)); + } + + bool mayNeedRelaxation(const MCInst &Inst, + const MCSubtargetInfo &STI) const override; + + bool fixupNeedsRelaxation(const MCFixup &Fixup, uint64_t Value, + const MCRelaxableFragment *DF, + const MCAsmLayout &Layout) const override; + + void relaxInstruction(const MCInst &Inst, const MCSubtargetInfo &STI, + MCInst &Res) const override; + + /// Returns the minimum size of a nop in bytes on this target. The assembler + /// will use this to emit excess padding in situations where the padding + /// required for simple alignment would be less than the minimum nop size. + unsigned getMinimumNopSize() const override { return 2; } + + /// Write a sequence of optimal nops to the output, covering \p Count bytes. + /// \return - true on success, false on failure + bool writeNopData(raw_ostream &OS, uint64_t Count) const override; +}; +} // end anonymous namespace + +/// cc—Carry clear GE—Greater than or equal +/// LS—Lower or same PL—Plus +/// CS—Carry set GT—Greater than +/// LT—Less than +/// EQ—Equal HI—Higher +/// MI—Minus VC—Overflow clear +/// LE—Less than or equal +/// NE—Not equal VS—Overflow set +static unsigned getRelaxedOpcodeBranch(const MCInst &Inst) { + unsigned Op = Inst.getOpcode(); + switch (Op) { + default: + return Op; + case M680x0::BRA8: + return M680x0::BRA16; + case M680x0::Bcc8: + return M680x0::Bcc16; + case M680x0::Bls8: + return M680x0::Bls16; + case M680x0::Blt8: + return M680x0::Blt16; + case M680x0::Beq8: + return M680x0::Beq16; + case M680x0::Bmi8: + return M680x0::Bmi16; + case M680x0::Bne8: + return M680x0::Bne16; + case M680x0::Bge8: + return M680x0::Bge16; + case M680x0::Bcs8: + return M680x0::Bcs16; + case M680x0::Bpl8: + return M680x0::Bpl16; + case M680x0::Bgt8: + return M680x0::Bgt16; + case M680x0::Bhi8: + return M680x0::Bhi16; + case M680x0::Bvc8: + return M680x0::Bvc16; + case M680x0::Ble8: + return M680x0::Ble16; + case M680x0::Bvs8: + return M680x0::Bvs16; + } +} + +static unsigned getRelaxedOpcodeArith(const MCInst &Inst) { + unsigned Op = Inst.getOpcode(); + switch (Op) { + default: + return Op; + // TODO there will be some relaxations for PCD and ARD mem for x20 + } +} + +static unsigned getRelaxedOpcode(const MCInst &Inst) { + unsigned R = getRelaxedOpcodeArith(Inst); + if (R != Inst.getOpcode()) + return R; + return getRelaxedOpcodeBranch(Inst); +} + +bool M680x0AsmBackend::mayNeedRelaxation(const MCInst &Inst, + const MCSubtargetInfo &STI) const { + // Branches can always be relaxed in either mode. + if (getRelaxedOpcodeBranch(Inst) != Inst.getOpcode()) + return true; + + // Check if this instruction is ever relaxable. + if (getRelaxedOpcodeArith(Inst) == Inst.getOpcode()) + return false; + + // Check if the relaxable operand has an expression. For the current set of + // relaxable instructions, the relaxable operand is always the last operand. + // FIXME will change for x20 mem + unsigned RelaxableOp = Inst.getNumOperands() - 1; + if (Inst.getOperand(RelaxableOp).isExpr()) + return true; + + return false; +} + +bool M680x0AsmBackend::fixupNeedsRelaxation(const MCFixup &Fixup, + uint64_t Value, + const MCRelaxableFragment *DF, + const MCAsmLayout &Layout) const { + // TODO Newer CPU can use 32 bit offsets, so check for this when ready + if (int64_t(Value) != int64_t(int16_t(Value))) { + llvm_unreachable("Cannot relax the instruction, value does not fit"); + } + // Relax if the value is too big for a (signed) i8. This means that byte-wide + // instructions have to matched by default + // + // NOTE + // A branch to the immediately following instruction automatically + // uses the 16-bit displacement format because the 8-bit + // displacement field contains $00 (zero offset). + return Value == 0 || int64_t(Value) != int64_t(int8_t(Value)); +} + +// FIXME: Can tblgen help at all here to verify there aren't other instructions +// we can relax? +void M680x0AsmBackend::relaxInstruction(const MCInst &Inst, + const MCSubtargetInfo &STI, + MCInst &Res) const { + // The only relaxations M680x0 does is from a 1byte pcrel to a 2byte PCRel. + unsigned RelaxedOp = getRelaxedOpcode(Inst); + + if (RelaxedOp == Inst.getOpcode()) { + SmallString<256> Tmp; + raw_svector_ostream OS(Tmp); + Inst.dump_pretty(OS); + OS << "\n"; + report_fatal_error("unexpected instruction to relax: " + OS.str()); + } + + Res = Inst; + Res.setOpcode(RelaxedOp); +} + +bool M680x0AsmBackend::writeNopData(raw_ostream &OS, uint64_t Count) const { + // Cannot emit NOP with size being not multiple of 16 bits. + if (Count % 2 != 0) + return false; + + uint64_t NumNops = Count / 2; + for (uint64_t i = 0; i != NumNops; ++i) { + OS << "\x4E\x71"; + } + + return true; +} + +namespace { + +class M680x0ELFAsmBackend : public M680x0AsmBackend { +public: + uint8_t OSABI; + M680x0ELFAsmBackend(const Target &T, uint8_t OSABI) + : M680x0AsmBackend(T), OSABI(OSABI) {} + + std::unique_ptr + createObjectTargetWriter() const override { + return createM680x0ELFObjectWriter(OSABI); + } +}; + +} // end anonymous namespace + +MCAsmBackend *llvm::createM680x0AsmBackend(const Target &T, + const MCSubtargetInfo &STI, + const MCRegisterInfo &MRI, + const MCTargetOptions &Options) { + // assert (TheTriple.getEnvironment() == Triple::GNU); + const Triple &TheTriple = STI.getTargetTriple(); + uint8_t OSABI = MCELFObjectTargetWriter::getOSABI(TheTriple.getOS()); + return new M680x0ELFAsmBackend(T, OSABI); +} Index: lib/Target/M680x0/MCTargetDesc/M680x0BaseInfo.h =================================================================== --- /dev/null +++ lib/Target/M680x0/MCTargetDesc/M680x0BaseInfo.h @@ -0,0 +1,337 @@ +//===-- M680x0BaseInfo.h - Top level definitions for M680X0 MC --*- 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 small standalone helper functions and enum definitions +/// for the M680x0 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_M680X0_MCTARGETDESC_M680X0BASEINFO_H +#define LLVM_LIB_TARGET_M680X0_MCTARGETDESC_M680X0BASEINFO_H + +#include "M680x0MCTargetDesc.h" + +#include "llvm/MC/MCExpr.h" +#include "llvm/Support/DataTypes.h" +#include "llvm/Support/ErrorHandling.h" + +#define GET_INSTRINFO_MI_OPS_INFO +#include "M680x0GenInstrInfo.inc" + +namespace llvm { + +namespace M680x0 { + +/// Enums for memory operand decoding. Supports these forms: +/// (d,An) +/// (d,An,Xn) +/// ([bd,An],Xn,od) +/// ([bd,An,Xn],od) +enum { + MemDisp = 0, + MemBase = 1, + MemIndex = 2, // FIXME assumes Scale 1 for now + MemOuter = 3 +}; + +/// Enums for pc-relative memory operand decoding. Supports these forms: +/// (d,PC) +/// (d,PC,Xn) +/// ([bd,PC],Xn,od) +/// ([bd,PC,Xn],od) +enum { PCRelDisp = 0, PCRelIndex = 1, PCRelOuter = 2 }; +} // namespace M680x0 + +namespace M680x0Beads { +enum { + Ctrl = 0x0, + Bits1 = 0x1, + Bits2 = 0x2, + Bits3 = 0x3, + Bits4 = 0x4, + DAReg = 0x5, + DA = 0x6, + Reg = 0x7, + Disp8 = 0x8, + Imm8 = 0x9, + Imm16 = 0xA, + Imm32 = 0xB, + Imm3 = 0xC, +}; + +// Ctrl payload +enum { + Term = 0x0, + Ignore = 0x1, +}; +} // namespace M680x0Beads + +/// This namespace holds all of the target specific flags that instruction info +/// tracks. +namespace M680x0II { +/// Target Operand Flag enum. +enum TOF { + + MO_NO_FLAG, + + /// On a symbol operand this indicates that the immediate is the absolute + /// address of the symbol. + MO_ABSOLUTE_ADDRESS, + + /// On a symbol operand this indicates that the immediate is the pc-relative + /// address of the symbol. + MO_PC_RELATIVE_ADDRESS, + + /// On a symbol operand this indicates that the immediate is the offset to + /// the GOT entry for the symbol name from the base of the GOT. + /// + /// name@GOT + MO_GOT, + + /// On a symbol operand this indicates that the immediate is the offset to + /// the location of the symbol name from the base of the GOT. + /// + /// name@GOTOFF + MO_GOTOFF, + + /// On a symbol operand this indicates that the immediate is offset to the + /// GOT entry for the symbol name from the current code location. + /// + /// name@GOTPCREL + MO_GOTPCREL, + + /// On a symbol operand this indicates that the immediate is offset to the + /// PLT entry of symbol name from the current code location. + /// + /// name@PLT + MO_PLT, +}; // enum TOF + +// enum { +// //===------------------------------------------------------------------===// +// // Instruction encodings. These are the standard/most common forms for +// // M680x0 instructions. +// // +// +// // Pseudo - This represents an instruction that is a pseudo instruction +// // or one that has not been implemented yet. It is illegal to code +// generate +// // it, but tolerated for intermediate implementation stages. +// Pseudo = 0, +// FormMask = 15 +// }; + +/// Return true if the specified TargetFlag operand is a reference to a stub +/// for a global, not the global itself. +inline static bool isGlobalStubReference(unsigned char TargetFlag) { + switch (TargetFlag) { + default: + return false; + case M680x0II::MO_GOTPCREL: // pc-relative GOT reference. + case M680x0II::MO_GOT: // normal GOT reference. + return true; + } +} + +/// Return True if the specified GlobalValue is a direct reference for a +/// symbol. +inline static bool isDirectGlobalReference(unsigned char Flag) { + switch (Flag) { + default: + return false; + case M680x0II::MO_NO_FLAG: + case M680x0II::MO_ABSOLUTE_ADDRESS: + case M680x0II::MO_PC_RELATIVE_ADDRESS: + return true; + } +} + +/// Return true if the specified global value reference is relative to a 32-bit +/// PIC base (M680x0ISD::GlobalBaseReg). If this is true, the addressing mode +/// has the PIC base register added in. +inline static bool isGlobalRelativeToPICBase(unsigned char TargetFlag) { + switch (TargetFlag) { + default: + return false; + case M680x0II::MO_GOTOFF: // isPICStyleGOT: local global. + case M680x0II::MO_GOT: // isPICStyleGOT: other global. + return true; + } +} + +/// Return True if the specified GlobalValue requires PC addressing mode. +inline static bool isPCRelGlobalReference(unsigned char Flag) { + switch (Flag) { + default: + return false; + case M680x0II::MO_GOTPCREL: + case M680x0II::MO_PC_RELATIVE_ADDRESS: + return true; + } +} + +/// Return True if the Block is referenced using PC +inline static bool isPCRelBlockReference(unsigned char Flag) { + switch (Flag) { + default: + return false; + case M680x0II::MO_PC_RELATIVE_ADDRESS: + return true; + } +} + +static inline bool isAddressRegister(unsigned RegNo) { + switch (RegNo) { + case M680x0::CCR: + case M680x0::PC: + case M680x0::SR: + default: + llvm_unreachable("Not an Address nor Data register"); + case M680x0::WA0: + case M680x0::WA1: + case M680x0::WA2: + case M680x0::WA3: + case M680x0::WA4: + case M680x0::WA5: + case M680x0::WA6: + case M680x0::WA7: + case M680x0::A0: + case M680x0::A1: + case M680x0::A2: + case M680x0::A3: + case M680x0::A4: + case M680x0::A5: + case M680x0::A6: + // case M680x0::A7: + case M680x0::SP: + return true; + case M680x0::BD0: + case M680x0::BD1: + case M680x0::BD2: + case M680x0::BD3: + case M680x0::BD4: + case M680x0::BD5: + case M680x0::BD6: + case M680x0::BD7: + case M680x0::WD0: + case M680x0::WD1: + case M680x0::WD2: + case M680x0::WD3: + case M680x0::WD4: + case M680x0::WD5: + case M680x0::WD6: + case M680x0::WD7: + case M680x0::D0: + case M680x0::D1: + case M680x0::D2: + case M680x0::D3: + case M680x0::D4: + case M680x0::D5: + case M680x0::D6: + case M680x0::D7: + return false; + } +} + +static inline bool isPCRelOpd(unsigned Opd) { + switch (Opd) { + default: + return false; + case M680x0::MIOpTypes::MxPCD32: + case M680x0::MIOpTypes::MxPCD16: + case M680x0::MIOpTypes::MxPCD8: + case M680x0::MIOpTypes::MxPCI32: + case M680x0::MIOpTypes::MxPCI16: + case M680x0::MIOpTypes::MxPCI8: + case MCOI::OPERAND_PCREL: + return true; + } +} + +static inline unsigned getDispSize(unsigned Opd) { + switch (Opd) { + default: + return 0; + case M680x0::MIOpTypes::MxAL16: + case M680x0::MIOpTypes::MxAL32: + case M680x0::MIOpTypes::MxAL8: + return 32; + case M680x0::MIOpTypes::MxARID16: + case M680x0::MIOpTypes::MxARID16_TC: + case M680x0::MIOpTypes::MxARID32: + case M680x0::MIOpTypes::MxARID32_TC: + case M680x0::MIOpTypes::MxARID8: + case M680x0::MIOpTypes::MxARID8_TC: + case M680x0::MIOpTypes::MxPCD16: + case M680x0::MIOpTypes::MxPCD32: + case M680x0::MIOpTypes::MxPCD8: + case M680x0::MIOpTypes::MxAS16: + case M680x0::MIOpTypes::MxAS32: + case M680x0::MIOpTypes::MxAS8: + return 16; + case M680x0::MIOpTypes::MxARII16: + case M680x0::MIOpTypes::MxARII16_TC: + case M680x0::MIOpTypes::MxARII32: + case M680x0::MIOpTypes::MxARII32_TC: + case M680x0::MIOpTypes::MxARII8: + case M680x0::MIOpTypes::MxARII8_TC: + case M680x0::MIOpTypes::MxPCI16: + case M680x0::MIOpTypes::MxPCI32: + case M680x0::MIOpTypes::MxPCI8: + return 8; + } +} + +static inline unsigned getMaskedSpillRegister(unsigned order) { + switch (order) { + default: + return 0; + case 0: + return M680x0::D0; + case 1: + return M680x0::D1; + case 2: + return M680x0::D2; + case 3: + return M680x0::D3; + case 4: + return M680x0::D4; + case 5: + return M680x0::D5; + case 6: + return M680x0::D6; + case 7: + return M680x0::D7; + case 8: + return M680x0::A0; + case 9: + return M680x0::A1; + case 10: + return M680x0::A2; + case 11: + return M680x0::A3; + case 12: + return M680x0::A4; + case 13: + return M680x0::A5; + case 14: + return M680x0::A6; + case 15: + return M680x0::A7; + } +} + +} // namespace M680x0II + +} // namespace llvm + +#endif Index: lib/Target/M680x0/MCTargetDesc/M680x0ELFObjectWriter.cpp =================================================================== --- /dev/null +++ lib/Target/M680x0/MCTargetDesc/M680x0ELFObjectWriter.cpp @@ -0,0 +1,128 @@ +//===---------- M680x0ELFObjectWriter.cpp - M680x0 ELF Writer ---*- 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 definitions for M680x0 ELF Writers +/// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/M680x0FixupKinds.h" +#include "MCTargetDesc/M680x0MCTargetDesc.h" + +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCELFObjectWriter.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCValue.h" +#include "llvm/Support/ErrorHandling.h" + +using namespace llvm; + +namespace { +class M680x0ELFObjectWriter : public MCELFObjectTargetWriter { +public: + M680x0ELFObjectWriter(uint8_t OSABI); + + ~M680x0ELFObjectWriter() override; + +protected: + unsigned getRelocType(MCContext &Ctx, const MCValue &Target, + const MCFixup &Fixup, bool IsPCRel) const override; +}; +} // namespace + +M680x0ELFObjectWriter::M680x0ELFObjectWriter(uint8_t OSABI) + : MCELFObjectTargetWriter(false, OSABI, ELF::EM_68K, /* RELA */ true) {} + +M680x0ELFObjectWriter::~M680x0ELFObjectWriter() {} + +enum M680x0RelType { RT_32, RT_16, RT_8 }; + +static M680x0RelType +getType(unsigned Kind, MCSymbolRefExpr::VariantKind &Modifier, bool &IsPCRel) { + switch (Kind) { + default: + llvm_unreachable("Unimplemented"); + case FK_Data_4: + case FK_PCRel_4: + return RT_32; + case FK_PCRel_2: + case FK_Data_2: + return RT_16; + case FK_PCRel_1: + case FK_Data_1: + return RT_8; + } +} + +// FIXME Should i split reloc types between pre x20 and the rest? +unsigned M680x0ELFObjectWriter::getRelocType(MCContext &Ctx, + const MCValue &Target, + const MCFixup &Fixup, + bool IsPCRel) const { + MCSymbolRefExpr::VariantKind Modifier = Target.getAccessVariant(); + unsigned Kind = Fixup.getKind(); + M680x0RelType Type = getType(Kind, Modifier, IsPCRel); + switch (Modifier) { + default: + llvm_unreachable("Unimplemented"); + case MCSymbolRefExpr::VK_None: + switch (Type) { + case RT_32: + return IsPCRel ? ELF::R_M680x0_PC32 : ELF::R_M680x0_32; + case RT_16: + return IsPCRel ? ELF::R_M680x0_PC16 : ELF::R_M680x0_16; + case RT_8: + return IsPCRel ? ELF::R_M680x0_PC8 : ELF::R_M680x0_8; + } + // case MCSymbolRefExpr::VK_GOT: + // switch (Type) { + // case RT_32: + // return IsPCRel ? ELF::R_M680x0_GOTPC32 : ELF::R_M680x0_GOT32; + // case RT_16: + // return IsPCRel ? ELF::R_M680x0_GOTPC16 : ELF::R_M680x0_GOT16; + // case RT_8: + // llvm_unreachable("Unimplemented"); + // } + case MCSymbolRefExpr::VK_GOTPCREL: + switch (Type) { + case RT_32: + return ELF::R_M680x0_GOTPCREL32; + case RT_16: + return ELF::R_M680x0_GOTPCREL16; + case RT_8: + return ELF::R_M680x0_GOTPCREL8; + } + case MCSymbolRefExpr::VK_GOTOFF: + assert(!IsPCRel); + switch (Type) { + case RT_32: + return ELF::R_M680x0_GOTOFF32; + case RT_16: + return ELF::R_M680x0_GOTOFF16; + case RT_8: + return ELF::R_M680x0_GOTOFF8; + } + case MCSymbolRefExpr::VK_PLT: + switch (Type) { + case RT_32: + return ELF::R_M680x0_PLT32; + case RT_16: + return ELF::R_M680x0_PLT16; + case RT_8: + return ELF::R_M680x0_PLT8; + } + } +} + +std::unique_ptr +llvm::createM680x0ELFObjectWriter(uint8_t OSABI) { + return llvm::make_unique(OSABI); +} Index: lib/Target/M680x0/MCTargetDesc/M680x0FixupKinds.h =================================================================== --- /dev/null +++ lib/Target/M680x0/MCTargetDesc/M680x0FixupKinds.h @@ -0,0 +1,65 @@ +//===-- M680x0FixupKinds.h - M680x0 Specific Fixup Entries ------*- 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 M680x0 specific fixup entries. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_M680x0_MCTARGETDESC_M680x0FIXUPKINDS_H +#define LLVM_LIB_TARGET_M680x0_MCTARGETDESC_M680x0FIXUPKINDS_H + +#include "llvm/MC/MCFixup.h" + +namespace llvm { +namespace M680x0 { +enum Fixups { + // Marker + LastTargetFixupKind, + NumTargetFixupKinds = LastTargetFixupKind - FirstTargetFixupKind +}; +} + +static inline unsigned getFixupKindLog2Size(unsigned Kind) { + switch (Kind) { + default: + llvm_unreachable("invalid fixup kind!"); + case FK_PCRel_1: + case FK_SecRel_1: + case FK_Data_1: + return 0; + case FK_PCRel_2: + case FK_SecRel_2: + case FK_Data_2: + return 1; + case FK_PCRel_4: + case FK_SecRel_4: + case FK_Data_4: + return 2; + } +} + +static inline MCFixupKind getFixupForSize(unsigned Size, bool isPCRel) { + switch (Size) { + default: + llvm_unreachable("Invalid generic fixup size!"); + case 8: + return isPCRel ? FK_PCRel_1 : FK_Data_1; + case 16: + return isPCRel ? FK_PCRel_2 : FK_Data_2; + case 32: + return isPCRel ? FK_PCRel_4 : FK_Data_4; + case 64: + return isPCRel ? FK_PCRel_8 : FK_Data_8; + } +} + +} // namespace llvm + +#endif Index: lib/Target/M680x0/MCTargetDesc/M680x0MCAsmInfo.h =================================================================== --- /dev/null +++ lib/Target/M680x0/MCTargetDesc/M680x0MCAsmInfo.h @@ -0,0 +1,32 @@ +//===-- M680x0MCAsmInfo.h - M680x0 Asm Info --------------------*- 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 the declarations of the M680x0 MCAsmInfo properties. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_M680X0_MCTARGETDESC_M680X0MCASMINFO_H +#define LLVM_LIB_TARGET_M680X0_MCTARGETDESC_M680X0MCASMINFO_H + +#include "llvm/MC/MCAsmInfoELF.h" + +namespace llvm { + class Triple; + + class M680x0ELFMCAsmInfo : public MCAsmInfoELF { + void anchor() override; + + public: + explicit M680x0ELFMCAsmInfo(const Triple &Triple); + }; + +} // namespace llvm + +#endif Index: lib/Target/M680x0/MCTargetDesc/M680x0MCAsmInfo.cpp =================================================================== --- /dev/null +++ lib/Target/M680x0/MCTargetDesc/M680x0MCAsmInfo.cpp @@ -0,0 +1,43 @@ +//===-- M680x0MCAsmInfo.cpp - M680x0 Asm Properties -------------*- 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 the definitions of the M680x0 MCAsmInfo properties. +/// +//===----------------------------------------------------------------------===// + +#include "M680x0MCAsmInfo.h" + +#include "llvm/ADT/Triple.h" + +using namespace llvm; + +void M680x0ELFMCAsmInfo::anchor() {} + +// TODO get back to it when it comes to printing +M680x0ELFMCAsmInfo::M680x0ELFMCAsmInfo(const Triple &T) { + CodePointerSize = 4; + CalleeSaveStackSlotSize = 4; + + IsLittleEndian = false; + + TextAlignFillValue = 0x90; + + // Debug Information + SupportsDebugInformation = true; + + // Exceptions handling + ExceptionsType = ExceptionHandling::DwarfCFI; + + // Always enable the integrated assembler by default. + // Clang also enabled it when the OS is Solaris but that is redundant here. + UseIntegratedAssembler = true; + + CommentString = ";"; +} Index: lib/Target/M680x0/MCTargetDesc/M680x0MCCodeEmitter.cpp =================================================================== --- /dev/null +++ lib/Target/M680x0/MCTargetDesc/M680x0MCCodeEmitter.cpp @@ -0,0 +1,419 @@ +//===-- M680x0MCCodeEmitter.cpp - Convert M680x0 code emitter ---*- 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 defintions for M680x0 code emitter. +/// +//===----------------------------------------------------------------------===// + +#include "M680x0RegisterInfo.h" + +#include "MCTargetDesc/M680x0BaseInfo.h" +#include "MCTargetDesc/M680x0FixupKinds.h" +#include "MCTargetDesc/M680x0MCTargetDesc.h" + +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCSymbol.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +#define DEBUG_TYPE "m680x0-mccodeemitter" + +namespace { +class M680x0MCCodeEmitter : public MCCodeEmitter { + M680x0MCCodeEmitter(const M680x0MCCodeEmitter &) = delete; + void operator=(const M680x0MCCodeEmitter &) = delete; + const MCInstrInfo &MCII; + MCContext &Ctx; + +public: + M680x0MCCodeEmitter(const MCInstrInfo &mcii, MCContext &ctx) + : MCII(mcii), Ctx(ctx) {} + + ~M680x0MCCodeEmitter() override {} + + // TableGen'erated function + const uint8_t *getGenInstrBeads(const MCInst &MI) const; + + unsigned EncodeBits(unsigned ThisByte, uint8_t Bead, const MCInst &MI, + const MCInstrDesc &Desc, uint64_t &Buffer, + unsigned Offset, SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + unsigned EncodeReg(unsigned ThisByte, uint8_t Bead, const MCInst &MI, + const MCInstrDesc &Desc, uint64_t &Buffer, unsigned Offset, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + unsigned EncodeImm(unsigned ThisByte, uint8_t Bead, const MCInst &MI, + const MCInstrDesc &Desc, uint64_t &Buffer, unsigned Offset, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + void encodeInstruction(const MCInst &MI, raw_ostream &OS, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const override; +}; + +} // end anonymous namespace + +unsigned M680x0MCCodeEmitter::EncodeBits(unsigned ThisByte, uint8_t Bead, + const MCInst &MI, + const MCInstrDesc &Desc, + uint64_t &Buffer, unsigned Offset, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + unsigned Num = 0; + switch (Bead & 0xF) { + case M680x0Beads::Bits1: + Num = 1; + break; + case M680x0Beads::Bits2: + Num = 2; + break; + case M680x0Beads::Bits3: + Num = 3; + break; + case M680x0Beads::Bits4: + Num = 4; + break; + } + unsigned char Val = (Bead & 0xF0) >> 4; + + LLVM_DEBUG(dbgs() << "\tEncodeBits" + << " Num: " << Num << " Val: 0x"); + LLVM_DEBUG(dbgs().write_hex(Val) << "\n"); + + Buffer |= (Val << Offset); + + return Num; +} + +unsigned M680x0MCCodeEmitter::EncodeReg(unsigned ThisByte, uint8_t Bead, + const MCInst &MI, + const MCInstrDesc &Desc, + uint64_t &Buffer, unsigned Offset, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + bool DA, Reg; + switch (Bead & 0xF) { + case M680x0Beads::DAReg: + Reg = true; + DA = true; + break; + case M680x0Beads::DA: + Reg = false; + DA = true; + break; + case M680x0Beads::Reg: + Reg = true; + DA = false; + break; + } + + unsigned Op = (Bead & 0x70) >> 4; + bool Alt = (Bead & 0x80); + LLVM_DEBUG(dbgs() << "\tEncodeReg" + << " Op: " << Op << ", DA: " << DA << ", Reg: " << Reg + << ", Alt: " << Alt << "\n"); + + assert(Op < Desc.NumMIOperands); + MIOperandInfo MIO = Desc.MIOpInfo[Op]; + bool isPCRel = M680x0II::isPCRelOpd(MIO.Type); + MCOperand MCO; + if (MIO.isTargetType() && MIO.OpsNum > 1) { + if (isPCRel) { + assert(Alt && + "PCRel addresses use Alt bead register encoding by default"); + MCO = MI.getOperand(MIO.MINo + M680x0::PCRelIndex); + } else { + MCO = + MI.getOperand(MIO.MINo + (Alt ? M680x0::MemIndex : M680x0::MemBase)); + } + } else { + assert(!Alt && "You cannot use Alt register with a simple operand"); + MCO = MI.getOperand(MIO.MINo); + } + + unsigned RegNum = MCO.getReg(); + auto RI = Ctx.getRegisterInfo(); + + unsigned Written = 0; + if (Reg) { + uint32_t Val = RI->getEncodingValue(RegNum); + Buffer |= Val << Offset; + Offset += 3; + Written += 3; + } + + if (DA) { + Buffer |= (char)M680x0II::isAddressRegister(RegNum) << Offset; + Written++; + } + + return Written; +} +/// Checks if an unsigned integer fits into the given bit width. non-templated +/// version +constexpr static inline bool uintDoesFit(unsigned N, uint64_t x) { + return N >= 64 || x <= (UINT64_MAX >> (64 - N)); +} + +/// Checks if an integer fits into the given bit width. non-templated version +constexpr static inline bool intDoesFit(unsigned N, int64_t x) { + return N >= 64 || + (-(INT64_C(1) << (N - 1)) <= x && x < (INT64_C(1) << (N - 1))); +} + +static unsigned EmitConstant(uint64_t Val, unsigned Size, unsigned Pad, + uint64_t &Buffer, unsigned Offset) { + // assert (Size && (Size == 8 || Size == 16 || Size == 32)); + assert(Size + Offset <= 64 && "Value does not fit"); + assert(uintDoesFit(Size, Val)); + + // Pad the instruction with zeros if any + // FIXME Actually emit zeros, since there might be trash in the buffer. + Size += Pad; + + // Writing Value in host's endianness + Buffer |= Val << Offset; + return Size; +} + +unsigned M680x0MCCodeEmitter::EncodeImm(unsigned ThisByte, uint8_t Bead, + const MCInst &MI, + const MCInstrDesc &Desc, + uint64_t &Buffer, unsigned Offset, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + unsigned ThisWord = ThisByte / 2; + unsigned Size = 0; + unsigned Pad = 0; + unsigned FixOffset = 0; + int64_t Addendum = 0; + bool NoExpr = false; + + unsigned Type = Bead & 0xF; + unsigned Op = (Bead & 0x70) >> 4; + bool Alt = (Bead & 0x80); + + assert(Op < Desc.NumMIOperands); + MIOperandInfo MIO = Desc.MIOpInfo[Op]; + bool isPCRel = M680x0II::isPCRelOpd(MIO.Type); + + // The PC value upon instruction reading of a short jump will point to the + // next instruction, thus we need to compensate 2 bytes, which is the diff + // between the patch point and the PC. + if (isPCRel && ThisWord == 0) { + Addendum -= 2; + } + + switch (Type) { + // ??? what happens if it is not byte aligned + // ??? is it even possible + case M680x0Beads::Disp8: + Size = 8; + Pad = 0; + FixOffset = ThisByte + 1; + Addendum += 1; + break; + case M680x0Beads::Imm8: + Size = 8; + Pad = 8; + FixOffset = ThisByte; + break; + case M680x0Beads::Imm16: + Size = 16; + Pad = 0; + FixOffset = ThisByte; + break; + case M680x0Beads::Imm32: + Size = 32; + Pad = 0; + FixOffset = ThisByte; + break; + case M680x0Beads::Imm3: + Size = 3; + Pad = 0; + NoExpr = true; + break; + } + + LLVM_DEBUG(dbgs() << "\tEncodeImm" + << " Op: " << Op << ", Size: " << Size << ", Alt: " << Alt + << "\n"); + + MCOperand MCO; + if (MIO.isTargetType()) { + + if (isPCRel) { + assert(!Alt && "You cannot use ALT operand with PCRel"); + MCO = MI.getOperand(MIO.MINo + M680x0::PCRelDisp); + } else { + MCO = + MI.getOperand(MIO.MINo + (Alt ? M680x0::MemOuter : M680x0::MemDisp)); + } + + if (MCO.isExpr()) { + assert(!NoExpr && "Cannot use expression here"); + const MCExpr *Expr = MCO.getExpr(); + + // This only makes sense for PCRel instructions since PC points to the + // extension word and Disp8 for example is right justified and requires + // correction. E.g. R_M680x0_PC32 is calculated as S + A - P, P for Disp8 + // will be EXTENSION_WORD + 1 thus we need to have A equal to 1 to + // compensate. + // TODO count extension words + if (isPCRel && Addendum != 0) { + Expr = MCBinaryExpr::createAdd( + Expr, MCConstantExpr::create(Addendum, Ctx), Ctx); + } + + Fixups.push_back(MCFixup::create( + FixOffset, Expr, getFixupForSize(Size, isPCRel), MI.getLoc())); + // Write zeros + return EmitConstant(0, Size, Pad, Buffer, Offset); + } + + } else { + // assert (!Alt && "You cannot use Alt immediate with a simple operand"); + MCO = MI.getOperand(MIO.MINo); + if (MCO.isExpr()) { + assert(!NoExpr && "Cannot use expression here"); + const MCExpr *Expr = MCO.getExpr(); + + if (Addendum != 0) { + Expr = MCBinaryExpr::createAdd( + Expr, MCConstantExpr::create(Addendum, Ctx), Ctx); + } + + Fixups.push_back(MCFixup::create( + FixOffset, Expr, getFixupForSize(Size, isPCRel), MI.getLoc())); + // Write zeros + return EmitConstant(0, Size, Pad, Buffer, Offset); + } + } + + int64_t I = MCO.getImm(); + + // Store 8 as 0, thus making range 1-8 + if (Type == M680x0Beads::Imm3 && Alt) { + assert(I && "Cannot encode Alt Imm3 zero value"); + I %= 8; + } else { + assert(intDoesFit(Size, I)); + } + + uint64_t Imm = I; + + // 32 bit Imm requires HI16 first then LO16 + if (Size == 32) { + Offset += EmitConstant((Imm >> 16) & 0xFFFF, 16, Pad, Buffer, Offset); + EmitConstant(Imm & 0xFFFF, 16, Pad, Buffer, Offset); + return Size; + } + + return EmitConstant(Imm & (UINT64_MAX >> (64 - Size)), Size, Pad, Buffer, + Offset); +} + +#include "M680x0GenMCCodeBeads.inc" + +void M680x0MCCodeEmitter::encodeInstruction(const MCInst &MI, raw_ostream &OS, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + unsigned Opcode = MI.getOpcode(); + const MCInstrDesc &Desc = MCII.get(Opcode); + // uint64_t TSFlags = Desc.TSFlags; + + LLVM_DEBUG(dbgs() << "EncodeInstruction: " << MCII.getName(Opcode) << "(" + << Opcode << ")\n"); + + const uint8_t *Beads = getGenInstrBeads(MI); + if (!*Beads) { + llvm_unreachable("*** Instruction does not have Beads defined"); + } + + uint64_t Buffer = 0; + unsigned Offset = 0; + unsigned ThisByte = 0; + + while (*Beads) { + uint8_t Bead = *Beads; + Beads++; + + // Check for control beads + if (!(Bead & 0xF)) { + switch (Bead >> 4) { + case M680x0Beads::Ignore: + continue; + } + } + + switch (Bead & 0xF) { + default: + llvm_unreachable("Unknown Bead code"); + break; + case M680x0Beads::Bits1: + case M680x0Beads::Bits2: + case M680x0Beads::Bits3: + case M680x0Beads::Bits4: + Offset += + EncodeBits(ThisByte, Bead, MI, Desc, Buffer, Offset, Fixups, STI); + break; + case M680x0Beads::DAReg: + case M680x0Beads::DA: + case M680x0Beads::Reg: + Offset += + EncodeReg(ThisByte, Bead, MI, Desc, Buffer, Offset, Fixups, STI); + break; + case M680x0Beads::Disp8: + case M680x0Beads::Imm8: + case M680x0Beads::Imm16: + case M680x0Beads::Imm32: + case M680x0Beads::Imm3: + Offset += + EncodeImm(ThisByte, Bead, MI, Desc, Buffer, Offset, Fixups, STI); + break; + } + + // Since M680x0 is Big Endian we need to rotate each instruction word + while (Offset / 16) { + OS.write((char)((Buffer >> 8) & 0xFF)); + OS.write((char)((Buffer >> 0) & 0xFF)); + Buffer >>= 16; + Offset -= 16; + ThisByte += 2; + } + } + + // while (Offset >= 8) { + // OS.write((char)(Buffer & 0xFF)); + // Buffer >>= 8; + // Offset -= 8; + // ThisByte++; + // } + + assert(Offset == 0 && "M680x0 Instructions are % 2 bytes"); + assert((ThisByte && !(ThisByte % 2)) && "M680x0 Instructions are % 2 bytes"); +} + +MCCodeEmitter *llvm::createM680x0MCCodeEmitter(const MCInstrInfo &MCII, + const MCRegisterInfo &MRI, + MCContext &Ctx) { + return new M680x0MCCodeEmitter(MCII, Ctx); +} Index: lib/Target/M680x0/MCTargetDesc/M680x0MCTargetDesc.h =================================================================== --- /dev/null +++ lib/Target/M680x0/MCTargetDesc/M680x0MCTargetDesc.h @@ -0,0 +1,66 @@ +//===-- M680x0MCTargetDesc.h - M680x0 Target Descriptions -------*- 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 provides M680x0 specific target descriptions. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_M680X0_MCTARGETDESC_M680X0MCTARGETDESC_H +#define LLVM_LIB_TARGET_M680X0_MCTARGETDESC_M680X0MCTARGETDESC_H + +#include "llvm/MC/MCInstrDesc.h" +#include "llvm/MC/MCObjectWriter.h" +#include "llvm/Support/DataTypes.h" + +namespace llvm { +class MCAsmBackend; +class MCCodeEmitter; +class MCContext; +class MCInstrInfo; +class MCRegisterInfo; +class MCSubtargetInfo; +class MCRelocationInfo; +class MCTargetOptions; +class Target; +class Triple; +class StringRef; +class raw_ostream; +class raw_pwrite_stream; + +extern Target TheM680x0Target; + +MCAsmBackend *createM680x0AsmBackend(const Target &T, + const MCSubtargetInfo &STI, + const MCRegisterInfo &MRI, + const MCTargetOptions &Options); + +MCCodeEmitter *createM680x0MCCodeEmitter(const MCInstrInfo &MCII, + const MCRegisterInfo &MRI, + MCContext &Ctx); + +/// Construct an M680x0 ELF object writer. +std::unique_ptr +createM680x0ELFObjectWriter(uint8_t OSABI); + +} // namespace llvm + +// Defines symbolic names for M680x0 registers. This defines a mapping from +// register name to register number. +#define GET_REGINFO_ENUM +#include "M680x0GenRegisterInfo.inc" + +// Defines symbolic names for the M680x0 instructions. +#define GET_INSTRINFO_ENUM +#include "M680x0GenInstrInfo.inc" + +#define GET_SUBTARGETINFO_ENUM +#include "M680x0GenSubtargetInfo.inc" + +#endif Index: lib/Target/M680x0/MCTargetDesc/M680x0MCTargetDesc.cpp =================================================================== --- /dev/null +++ lib/Target/M680x0/MCTargetDesc/M680x0MCTargetDesc.cpp @@ -0,0 +1,135 @@ +//===-- M680x0MCTargetDesc.cpp - M680x0 Target Descriptions -----*- 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 provides M680x0 target specific descriptions. +/// +//===----------------------------------------------------------------------===// + +#include "M680x0MCTargetDesc.h" + +#include "M680x0MCAsmInfo.h" + +#include "InstPrinter/M680x0InstPrinter.h" + +#include "llvm/MC/MCELFStreamer.h" +#include "llvm/MC/MCInstPrinter.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCSymbol.h" +#include "llvm/MC/MachineLocation.h" +#include "llvm/Support/CommandLine.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 "M680x0GenInstrInfo.inc" + +#define GET_SUBTARGETINFO_MC_DESC +#include "M680x0GenSubtargetInfo.inc" + +#define GET_REGINFO_MC_DESC +#include "M680x0GenRegisterInfo.inc" + +static StringRef ParseM680x0Triple(const Triple &TT, StringRef CPU) { + std::string FS = ""; + return FS; +} + +static MCInstrInfo *createM680x0MCInstrInfo() { + MCInstrInfo *X = new MCInstrInfo(); + InitM680x0MCInstrInfo(X); // defined in M680x0GenInstrInfo.inc + return X; +} + +static MCRegisterInfo *createM680x0MCRegisterInfo(const Triple &TT) { + MCRegisterInfo *X = new MCRegisterInfo(); + InitM680x0MCRegisterInfo(X, llvm::M680x0::A0, 0, 0, llvm::M680x0::PC); + return X; +} + +static MCSubtargetInfo * +createM680x0MCSubtargetInfo(const Triple &TT, StringRef CPU, StringRef FS) { + std::string ArchFS = ParseM680x0Triple(TT, CPU); + if (!FS.empty()) { + if (!ArchFS.empty()) + ArchFS = ArchFS + "," + FS.str(); + else + ArchFS = FS; + } + return createM680x0MCSubtargetInfoImpl(TT, CPU, ArchFS); +} + +static MCAsmInfo *createM680x0MCAsmInfo(const MCRegisterInfo &MRI, + const Triple &TT) { + MCAsmInfo *MAI = new M680x0ELFMCAsmInfo(TT); + + // Initialize initial frame state. + // Calculate amount of bytes used for return address storing + int stackGrowth = -4; + + // Initial state of the frame pointer is SP+stackGrowth. + MCCFIInstruction Inst = MCCFIInstruction::createDefCfa( + nullptr, MRI.getDwarfRegNum(llvm::M680x0::SP, true), -stackGrowth); + MAI->addInitialFrameState(Inst); + + //??? Do i need this? + // // Add return address to move list + // MCCFIInstruction Inst2 = MCCFIInstruction::createOffset( + // nullptr, MRI.getDwarfRegNum(M680x0::PC, true), stackGrowth); + // MAI->addInitialFrameState(Inst2); + + return MAI; +} + +static MCRelocationInfo *createM680x0MCRelocationInfo(const Triple &TheTriple, + MCContext &Ctx) { + // Default to the stock relocation info. + return llvm::createMCRelocationInfo(TheTriple, Ctx); +} + +static MCInstPrinter *createM680x0MCInstPrinter(const Triple &T, + unsigned SyntaxVariant, + const MCAsmInfo &MAI, + const MCInstrInfo &MII, + const MCRegisterInfo &MRI) { + return new M680x0InstPrinter(MAI, MII, MRI); +} + +extern "C" void LLVMInitializeM680x0TargetMC() { + Target &T = TheM680x0Target; + + // Register the MC asm info. + RegisterMCAsmInfoFn X(T, createM680x0MCAsmInfo); + + // Register the MC instruction info. + TargetRegistry::RegisterMCInstrInfo(T, createM680x0MCInstrInfo); + + // Register the MC register info. + TargetRegistry::RegisterMCRegInfo(T, createM680x0MCRegisterInfo); + + // Register the MC subtarget info. + TargetRegistry::RegisterMCSubtargetInfo(T, createM680x0MCSubtargetInfo); + + // Register the code emitter. + TargetRegistry::RegisterMCCodeEmitter(T, createM680x0MCCodeEmitter); + + // Register the MCInstPrinter. + TargetRegistry::RegisterMCInstPrinter(T, createM680x0MCInstPrinter); + + // Register the MC relocation info. + TargetRegistry::RegisterMCRelocationInfo(T, createM680x0MCRelocationInfo); + + // Register the asm backend. + TargetRegistry::RegisterMCAsmBackend(T, createM680x0AsmBackend); +} Index: lib/Target/M680x0/README.md =================================================================== --- /dev/null +++ lib/Target/M680x0/README.md @@ -0,0 +1,4 @@ +Motorola 680x0 LLVM Target +========================== + +To build follow the usual LLVM/Clang workflow. Index: lib/Target/M680x0/TODO.md =================================================================== --- /dev/null +++ lib/Target/M680x0/TODO.md @@ -0,0 +1,24 @@ +# TODO + +## Global +### TLS + - All of it + +### Assembler/Disassembler + - All of it + +### Dynamic Stack Allocation + - Part related to stack-split and tls + +### ISA x10, x20, x30, x40, x50, x60 and all in between + +## Instructions + +### Floats + +### JSR + - can be relaxed to use shorter BSR + +### GOT + - is random GOT register is fine? + - does PLT really do no require GOT ref? or is it platform depended Index: lib/Target/M680x0/TargetInfo/CMakeLists.txt =================================================================== --- /dev/null +++ lib/Target/M680x0/TargetInfo/CMakeLists.txt @@ -0,0 +1,3 @@ +add_llvm_library(LLVMM680x0Info + M680x0TargetInfo.cpp +) Index: lib/Target/M680x0/TargetInfo/LLVMBuild.txt =================================================================== --- /dev/null +++ lib/Target/M680x0/TargetInfo/LLVMBuild.txt @@ -0,0 +1,23 @@ +;===- ./lib/Target/M680x0/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 = M680x0Info +parent = M680x0 +required_libraries = Support +add_to_library_groups = M680x0 Index: lib/Target/M680x0/TargetInfo/M680x0TargetInfo.cpp =================================================================== --- /dev/null +++ lib/Target/M680x0/TargetInfo/M680x0TargetInfo.cpp @@ -0,0 +1,28 @@ +//===-- M680x0TargetInfo.cpp - M680x0 Target Implementation -----*- 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 M680x0 target initializer. +/// +//===----------------------------------------------------------------------===// + +#include "M680x0.h" + +#include "MCTargetDesc/M680x0MCTargetDesc.h" + +#include "llvm/Support/TargetRegistry.h" + +using namespace llvm; + +Target llvm::TheM680x0Target; + +extern "C" void LLVMInitializeM680x0TargetInfo() { + RegisterTarget X( + TheM680x0Target, "m680x0", "Motorola 68000 family", "M680x0"); +} Index: test/CodeGen/M680x0/ASM/Alloc/dyn_alloca_aligned.ll =================================================================== --- /dev/null +++ test/CodeGen/M680x0/ASM/Alloc/dyn_alloca_aligned.ll @@ -0,0 +1,9 @@ +; RUN: llc < %s -mtriple=m68k-linux-gnu -verify-machineinstrs | FileCheck %s -check-prefix=x00 +define i32 @A(i32 %Size) { +; x00: sub.l %d1, %d0 +; x00-NEXT: and.l #-128 +; x00-NEXT: move.l %d0, %sp + %A = alloca i8, i32 %Size, align 128 + %A_addr = ptrtoint i8* %A to i32 + ret i32 %A_addr +} Index: test/CodeGen/M680x0/ASM/Arith/add-with-overflow.ll =================================================================== --- /dev/null +++ test/CodeGen/M680x0/ASM/Arith/add-with-overflow.ll @@ -0,0 +1,62 @@ +; RUN: llc < %s -mtriple=m680x0-linux -verify-machineinstrs | FileCheck %s -check-prefix=x00 + +declare {i32, i1} @llvm.sadd.with.overflow.i32(i32, i32) +declare {i32, i1} @llvm.uadd.with.overflow.i32(i32, i32) + + +; x00-LABEL: test5: +; x00: add.l +; x00-NEXT: bvs +define fastcc i32 @test5(i32 %v1, i32 %v2, i32* %X) nounwind { +entry: + %t = call {i32, i1} @llvm.sadd.with.overflow.i32(i32 %v1, i32 %v2) + %sum = extractvalue {i32, i1} %t, 0 + %obit = extractvalue {i32, i1} %t, 1 + br i1 %obit, label %overflow, label %normal + +normal: + store i32 0, i32* %X + br label %overflow + +overflow: + ret i32 %sum +} + +; x00-LABEL: test6: +; x00: add.l +; x00-NEXT: bcs +define fastcc i1 @test6(i32 %v1, i32 %v2, i32* %X) nounwind { +entry: + %t = call {i32, i1} @llvm.uadd.with.overflow.i32(i32 %v1, i32 %v2) + %sum = extractvalue {i32, i1} %t, 0 + %obit = extractvalue {i32, i1} %t, 1 + br i1 %obit, label %carry, label %normal + +normal: + store i32 0, i32* %X + br label %carry + +carry: + ret i1 false +} + +; x00-LABEL: test7: +; x00: move.l (4,%sp), %d0 +; x00-NEXT: add.l (8,%sp), %d0 +; x00-NEXT: scs %d1 +; x00-NEXT: rts +define {i32, i1} @test7(i32 %v1, i32 %v2) nounwind { + %t = call {i32, i1} @llvm.uadd.with.overflow.i32(i32 %v1, i32 %v2) + ret {i32, i1} %t +} + +; x00-LABEL: test10: +; x00: add.l #1 +; x00-NEXT: svs +; x00-NEXT: rts +define fastcc i1 @test10(i32 %x) nounwind { +entry: + %t = call {i32, i1} @llvm.sadd.with.overflow.i32(i32 %x, i32 1) + %obit = extractvalue {i32, i1} %t, 1 + ret i1 %obit +} Index: test/CodeGen/M680x0/ASM/Arith/add.ll =================================================================== --- /dev/null +++ test/CodeGen/M680x0/ASM/Arith/add.ll @@ -0,0 +1,84 @@ +; RUN: llc < %s -mtriple=m680x0-linux -verify-machineinstrs | FileCheck %s -check-prefix=x00 + +; x00-LABEL: test1: +; x00: move.l (12,%sp), %d0 +; x00-NEXT: add.l (4,%sp), %d0 +; x00-NEXT: move.l (8,%sp), %d1 +; x00-NEXT: rts + +define i64 @test1(i64 %A, i32 %B) nounwind { + %tmp12 = zext i32 %B to i64 + %tmp3 = shl i64 %tmp12, 32 + %tmp5 = add i64 %tmp3, %A + ret i64 %tmp5 +} + + +; x00-LABEL: test2: +; x00: move.l %d0, [[Addr:%a[0-7]]] +; x00-NEXT: add.l #128, ([[Addr]]) + +define void @test2(i32* inreg %a) nounwind { + %aa = load i32, i32* %a + %b = add i32 %aa, 128 + store i32 %b, i32* %a + ret void +} + + +; x00-LABEL: test2_fast: +; x00: add.l #128, (%a0) + +define fastcc void @test2_fast(i32* inreg %a) nounwind { + %aa = load i32, i32* %a + %b = add i32 %aa, 128 + store i32 %b, i32* %a + ret void +} + + +; x00-LABEL: test3: +; x00 move.l (%a0), %d0 +; x00-NEXT move.l #-2147483648, %d1 +; x00-NEXT add.l (4,%a0), %d1 +; x00-NEXT addx.l #0, %d0 +; x00-NEXT move.l %d1, (4,%a0) +; x00-NEXT move.l %d0, (%a0) +; x00-NEXT rts +define fastcc void @test3(i64* inreg %a) nounwind { + %aa = load i64, i64* %a + %b = add i64 %aa, 2147483648 + store i64 %b, i64* %a + ret void +} + + +; x00-LABEL: test4: +; x00 move.l #128, %d0 +; x00-NEXT add.l (4,%a0), %d0 +; x00-NEXT move.l (%a0), %d1 +; x00-NEXT move.l %d0, (4,%a0) +; x00-NEXT addx.l #0, %d1 +; x00-NEXT move.l %d1, (%a0) +; x00-NEXT rts +define fastcc void @test4(i64* inreg %a) nounwind { + %aa = load i64, i64* %a + %b = add i64 %aa, 128 + store i64 %b, i64* %a + ret void +} + +; x00-LABEL: test9: +; x00: sub.l #10 +; x00-NEXT: seq +; x00-NEXT: and.l +; x00-NEXT: sub.l +; x00-NEXT: move.l +; x00-NEXT: rts + +define fastcc i32 @test9(i32 %x, i32 %y) nounwind readnone { + %cmp = icmp eq i32 %x, 10 + %sub = sext i1 %cmp to i32 + %cond = add i32 %sub, %y + ret i32 %cond +} Index: test/CodeGen/M680x0/ASM/Arith/divide-by-constant.ll =================================================================== --- /dev/null +++ test/CodeGen/M680x0/ASM/Arith/divide-by-constant.ll @@ -0,0 +1,127 @@ +; RUN: llc < %s -mtriple=m680x0-linux -verify-machineinstrs | FileCheck %s -check-prefix=x00 + +; TODO fold the shifts +; x00-LABEL: test1: +; x00: mulu #-1985 +; x00-NEXT: lsr.l #8 +; x00-NEXT: lsr.l #8 +; x00-NEXT: lsr.w #5 +define zeroext i16 @test1(i16 zeroext %x) nounwind { +entry: + %div = udiv i16 %x, 33 + ret i16 %div +} + +; x00-LABEL: test2: +; x00: mulu #-21845 +; x00-NEXT: lsr.l #8 +; x00-NEXT: lsr.l #8 +; x00-NEXT: lsr.w #1 +define zeroext i16 @test2(i8 signext %x, i16 zeroext %c) { +entry: + %div = udiv i16 %c, 3 + ret i16 %div +} + + +; x00-LABEL: test3: +; x00: move.b (11 +; x00-NEXT: and.l #255 +; x00-NEXT: muls #171 +; x00-NEXT: move.w #9 +; x00-NEXT: lsr.w +define zeroext i8 @test3(i8 zeroext %x, i8 zeroext %c) { +entry: + %div = udiv i8 %c, 3 + ret i8 %div +} + +; x00-LABEL: test4: +; x00: muls #1986 +define signext i16 @test4(i16 signext %x) nounwind { +entry: + %div = sdiv i16 %x, 33 ; [#uses=1] + ret i16 %div +} + +; x00-LABEL: test5: +; x00: jsr __udivsi3 +define i32 @test5(i32 %A) nounwind { + %tmp1 = udiv i32 %A, 1577682821 ; [#uses=1] + ret i32 %tmp1 +} + +; TODO fold shift +; x00-LABEL: test6: +; x00: muls #26215 +; x00: move.w #15 +; x00: lsr.w +; x00: asr.w #2 +define signext i16 @test6(i16 signext %x) nounwind { +entry: + %div = sdiv i16 %x, 10 + ret i16 %div +} + +; x00-LABEL: test7: +; x00: jsr __udivsi3 +define i32 @test7(i32 %x) nounwind { + %div = udiv i32 %x, 28 + ret i32 %div +} + +; x00-LABEL: test8: +; x00: lsr.b #1 +; x00: and.l #255 +; x00: muls #211 +; x00: move.w #13 +; x00: lsr.w +define i8 @test8(i8 %x) nounwind { + %div = udiv i8 %x, 78 + ret i8 %div +} + +; x00-LABEL: test9: +; x00: lsr.b #2 +; x00: and.l #255 +; x00: muls #71 +; x00: move.w #11 +; x00: lsr.w +define i8 @test9(i8 %x) nounwind { + %div = udiv i8 %x, 116 + ret i8 %div +} + +; x00-LABEL: testsize1: +; x00: asr.l +; x00: lsr.l +; x00: asr.l +define i32 @testsize1(i32 %x) minsize nounwind { +entry: + %div = sdiv i32 %x, 32 + ret i32 %div +} + +; x00-LABEL: testsize2: +; x00: jsr __divsi3 +define i32 @testsize2(i32 %x) minsize nounwind { +entry: + %div = sdiv i32 %x, 33 + ret i32 %div +} + +; x00-LABEL: testsize3: +; x00: lsr.l #5 +define i32 @testsize3(i32 %x) minsize nounwind { +entry: + %div = udiv i32 %x, 32 + ret i32 %div +} + +; x00-LABEL: testsize4: +; x00: jsr __udivsi3 +define i32 @testsize4(i32 %x) minsize nounwind { +entry: + %div = udiv i32 %x, 33 + ret i32 %div +} Index: test/CodeGen/M680x0/ASM/Arith/imul.ll =================================================================== --- /dev/null +++ test/CodeGen/M680x0/ASM/Arith/imul.ll @@ -0,0 +1,135 @@ +; RUN: llc < %s -O0 -mtriple=m680x0-linux | FileCheck %s --check-prefix=x00 + +; x00-LABEL: mul4_32: +; x00: lsl.l +define i32 @mul4_32(i32 %A) { + %mul = mul i32 %A, 4 + ret i32 %mul +} + +; x00-LABEL: mul4_64: +; x00: lsr.l +; x00: lsl.l +; x00: or.l +; x00: lsl.l +define i64 @mul4_64(i64 %A) { + %mul = mul i64 %A, 4 + ret i64 %mul +} + +; x00-LABEL: mul4096_32: +; x00: lsl.l +define i32 @mul4096_32(i32 %A) { + %mul = mul i32 %A, 4096 + ret i32 %mul +} + +; x00-LABEL: mul4096_64: +; x00: lsr.l +; x00: lsl.l +; x00: or.l +; x00: lsl.l +define i64 @mul4096_64(i64 %A) { + %mul = mul i64 %A, 4096 + ret i64 %mul +} + +; x00-LABEL: mulmin4096_32: +; x00: lsl.l +; x00-NEXT: neg.l +define i32 @mulmin4096_32(i32 %A) { + %mul = mul i32 %A, -4096 + ret i32 %mul +} + +; x00-LABEL: mulmin4096_64: +; x00: lsr.l +; x00: lsl.l +; x00: or.l +; x00: lsl.l +; x00: neg.l +; x00: negx.l +define i64 @mulmin4096_64(i64 %A) { + %mul = mul i64 %A, -4096 + ret i64 %mul +} + +; No i32 multiply for M68000 +; x00-LABEL: mul3_32: +; x00: jsr __mulsi3 +define i32 @mul3_32(i32 %A) { + %mul = mul i32 %A, 3 + ret i32 %mul +} + +; x00-LABEL: mul3_64: +; x00: jsr __muldi3 +define i64 @mul3_64(i64 %A) { + %mul = mul i64 %A, 3 + ret i64 %mul +} + +; x00-LABEL: mul40_32: +; x00: jsr __mulsi3 +define i32 @mul40_32(i32 %A) { + %mul = mul i32 %A, 40 + ret i32 %mul +} + +; x00-LABEL: mul40_64: +; x00: jsr __muldi3 +define i64 @mul40_64(i64 %A) { + %mul = mul i64 %A, 40 + ret i64 %mul +} + +; x00-LABEL: mul4_32_minsize: +; x00: lsl.l +define i32 @mul4_32_minsize(i32 %A) minsize { + %mul = mul i32 %A, 4 + ret i32 %mul +} + +; X64-LABEL: mul40_32_minsize: +; X64: jsr __mulsi3 +define i32 @mul40_32_minsize(i32 %A) minsize { + %mul = mul i32 %A, 40 + ret i32 %mul +} + +; x00-LABEL: mul33_32: +; x00: __mulsi3 +define i32 @mul33_32(i32 %A) { + %mul = mul i32 %A, 33 + ret i32 %mul +} + +; x00-LABEL: mul31_32: +; x00: __mulsi3 +define i32 @mul31_32(i32 %A) { + %mul = mul i32 %A, 31 + ret i32 %mul +} + +; x00-LABEL: mul0_32: +; x00: move.l #0 +define i32 @mul0_32(i32 %A) { + %mul = mul i32 %A, 0 + ret i32 %mul +} + +; x00-LABEL: mul4294967295_32: +; x00: neg.l %d0 +define i32 @mul4294967295_32(i32 %A) { + %mul = mul i32 %A, 4294967295 + ret i32 %mul +} + +; x00-LABEL: mul18446744073709551615_64: +; x00: neg.l %d0 +; x00: negx.l %d1 +; x00: rts +define i64 @mul18446744073709551615_64(i64 %A) { + %mul = mul i64 %A, 18446744073709551615 + ret i64 %mul +} Index: test/CodeGen/M680x0/ASM/Arith/lshr.ll =================================================================== --- /dev/null +++ test/CodeGen/M680x0/ASM/Arith/lshr.ll @@ -0,0 +1,16 @@ +; RUN: llc < %s -mtriple=m680x0-linux | FileCheck %s --check-prefix=x00 + +; Function Attrs: norecurse nounwind readnone +; x00-LABEL c_isspace: +; x00: move.l #8388639, %d0 +; x00: lsr.l %d1, %d0 +; x00: and.l #1, %d0 +define zeroext i1 @c_isspace(i32 %c) local_unnamed_addr #0 { +entry: + %switch.tableidx = add i32 %c, -9 + %switch.cast = trunc i32 %switch.tableidx to i24 + %switch.downshift = lshr i24 -8388577, %switch.cast + %0 = and i24 %switch.downshift, 1 + %switch.masked = icmp ne i24 %0, 0 + ret i1 %switch.masked +} Index: test/CodeGen/M680x0/ASM/Arith/mul64.ll =================================================================== --- /dev/null +++ test/CodeGen/M680x0/ASM/Arith/mul64.ll @@ -0,0 +1,10 @@ +; RUN: llc < %s -mtriple=m680x0-linux -verify-machineinstrs | FileCheck %s --check-prefix=x00 + +; Currenlty making the libcall is ok, x20 supports i32 mul/div which +; yields saner expansion for i64 mul +; x00-LABEL: foo: +; x00: jsr __muldi3 +define i64 @foo(i64 %t, i64 %u) nounwind { + %k = mul i64 %t, %u + ret i64 %k +} Index: test/CodeGen/M680x0/ASM/Arith/sdiv-exact.ll =================================================================== --- /dev/null +++ test/CodeGen/M680x0/ASM/Arith/sdiv-exact.ll @@ -0,0 +1,18 @@ +; RUN: llc -mtriple=m680x0-linux -verify-machineinstrs < %s | FileCheck %s -check-prefix=x00 + +; x00-LABEL: test1: +; x00: move.l #-1030792151 +; x00: jsr __mulsi3 +define i32 @test1(i32 %x) { + %div = sdiv exact i32 %x, 25 + ret i32 %div +} + +; x00-LABEL: test2: +; x00: asr.l #3 +; x00: move.l #-1431655765 +; x00: jsr __mulsi3 +define i32 @test2(i32 %x) { + %div = sdiv exact i32 %x, 24 + ret i32 %div +} Index: test/CodeGen/M680x0/ASM/Arith/smul-with-overflow.ll =================================================================== --- /dev/null +++ test/CodeGen/M680x0/ASM/Arith/smul-with-overflow.ll @@ -0,0 +1,68 @@ +; RUN: llc < %s -mtriple=m680x0-linux -verify-machineinstrs | FileCheck %s -check-prefix=x00 + +declare i32 @printf(i8*, ...) nounwind +declare {i32, i1} @llvm.smul.with.overflow.i32(i32, i32) +declare { i63, i1 } @llvm.smul.with.overflow.i63(i63, i63) + +@ok = internal constant [4 x i8] c"%d\0A\00" +@no = internal constant [4 x i8] c"no\0A\00" + +; x00-LABEL: test1: +; x00: jsr __muldi3 +; x00: sne +define fastcc i1 @test1(i32 %v1, i32 %v2) nounwind { +entry: + %t = call {i32, i1} @llvm.smul.with.overflow.i32(i32 %v1, i32 %v2) + %sum = extractvalue {i32, i1} %t, 0 + %obit = extractvalue {i32, i1} %t, 1 + br i1 %obit, label %overflow, label %normal + +normal: + %t1 = tail call i32 (i8*, ...) @printf( i8* getelementptr ([4 x i8], [4 x i8]* @ok, i32 0, i32 0), i32 %sum ) nounwind + ret i1 true + +overflow: + %t2 = tail call i32 (i8*, ...) @printf( i8* getelementptr ([4 x i8], [4 x i8]* @no, i32 0, i32 0) ) nounwind + ret i1 false +} + +; x00-LABEL: test2: +; x00: jsr __muldi3 +; x00: sne +define fastcc i1 @test2(i32 %v1, i32 %v2) nounwind { +entry: + %t = call {i32, i1} @llvm.smul.with.overflow.i32(i32 %v1, i32 %v2) + %sum = extractvalue {i32, i1} %t, 0 + %obit = extractvalue {i32, i1} %t, 1 + br i1 %obit, label %overflow, label %normal + +overflow: + %t2 = tail call i32 (i8*, ...) @printf( i8* getelementptr ([4 x i8], [4 x i8]* @no, i32 0, i32 0) ) nounwind + ret i1 false + +normal: + %t1 = tail call i32 (i8*, ...) @printf( i8* getelementptr ([4 x i8], [4 x i8]* @ok, i32 0, i32 0), i32 %sum ) nounwind + ret i1 true +} + +; x00-LABEL: test3: +; x00: add.l +; x00-NEXT: add.l +define i32 @test3(i32 %a, i32 %b) nounwind readnone { +entry: + %tmp0 = add i32 %b, %a + %tmp1 = call { i32, i1 } @llvm.smul.with.overflow.i32(i32 %tmp0, i32 2) + %tmp2 = extractvalue { i32, i1 } %tmp1, 0 + ret i32 %tmp2 +} + +; x00-LABEL: test4: +; x00: add.l +; x00: jsr __muldi3 +define i32 @test4(i32 %a, i32 %b) nounwind readnone { +entry: + %tmp0 = add i32 %b, %a + %tmp1 = call { i32, i1 } @llvm.smul.with.overflow.i32(i32 %tmp0, i32 4) + %tmp2 = extractvalue { i32, i1 } %tmp1, 0 + ret i32 %tmp2 +} Index: test/CodeGen/M680x0/ASM/Arith/sub-with-overflow.ll =================================================================== --- /dev/null +++ test/CodeGen/M680x0/ASM/Arith/sub-with-overflow.ll @@ -0,0 +1,56 @@ +; RUN: llc < %s -mtriple=m680x0-linux -verify-machineinstrs | FileCheck %s -check-prefix=x00 + +declare i32 @printf(i8*, ...) nounwind +declare {i32, i1} @llvm.ssub.with.overflow.i32(i32, i32) +declare {i32, i1} @llvm.usub.with.overflow.i32(i32, i32) + +@ok = internal constant [4 x i8] c"%d\0A\00" +@no = internal constant [4 x i8] c"no\0A\00" + +; x00-LABEL: func1: +; x00: sub.l (20,%sp) +; x00-NEXT: bvs +define i1 @func1(i32 %v1, i32 %v2) nounwind { +entry: + %t = call {i32, i1} @llvm.ssub.with.overflow.i32(i32 %v1, i32 %v2) + %sum = extractvalue {i32, i1} %t, 0 + %obit = extractvalue {i32, i1} %t, 1 + br i1 %obit, label %overflow, label %normal + +normal: + %t1 = tail call i32 (i8*, ...) @printf( i8* getelementptr ([4 x i8], [4 x i8]* @ok, i32 0, i32 0), i32 %sum ) nounwind + ret i1 true + +overflow: + %t2 = tail call i32 (i8*, ...) @printf( i8* getelementptr ([4 x i8], [4 x i8]* @no, i32 0, i32 0) ) nounwind + ret i1 false +} + +; x00-LABEL: func2: +; x00: sub.l (20,%sp) +; x00-NEXT: bcs +define i1 @func2(i32 %v1, i32 %v2) nounwind { +entry: + %t = call {i32, i1} @llvm.usub.with.overflow.i32(i32 %v1, i32 %v2) + %sum = extractvalue {i32, i1} %t, 0 + %obit = extractvalue {i32, i1} %t, 1 + br i1 %obit, label %carry, label %normal + +normal: + %t1 = tail call i32 (i8*, ...) @printf( i8* getelementptr ([4 x i8], [4 x i8]* @ok, i32 0, i32 0), i32 %sum ) nounwind + ret i1 true + +carry: + %t2 = tail call i32 (i8*, ...) @printf( i8* getelementptr ([4 x i8], [4 x i8]* @no, i32 0, i32 0) ) nounwind + ret i1 false +} + +; x00-LABEL: func3: +; x00: sub.l #1 +; x00-NEXT: svs +define i1 @func3(i32 %x) nounwind { +entry: + %t = call {i32, i1} @llvm.ssub.with.overflow.i32(i32 %x, i32 1) + %obit = extractvalue {i32, i1} %t, 1 + ret i1 %obit +} Index: test/CodeGen/M680x0/ASM/Arith/sub.ll =================================================================== --- /dev/null +++ test/CodeGen/M680x0/ASM/Arith/sub.ll @@ -0,0 +1,12 @@ +; RUN: llc -mtriple=m680x0-linux < %s -verify-machineinstrs | FileCheck %s -check-prefix=x00 + +; x00-LABEL: test1: +; x00: eori.l #31 +; x00-NEXT: move.l #32 +; x00-NEXT: sub.l +; x00-NEXT: rts +define i32 @test1(i32 %x) { + %xor = xor i32 %x, 31 + %sub = sub i32 32, %xor + ret i32 %sub +} Index: test/CodeGen/M680x0/ASM/Arith/umul-with-overflow.ll =================================================================== --- /dev/null +++ test/CodeGen/M680x0/ASM/Arith/umul-with-overflow.ll @@ -0,0 +1,38 @@ +; RUN: llc < %s -mtriple=m680x0-linux-gnu -verify-machineinstrs| FileCheck %s -check-prefix=x00 + +declare {i32, i1} @llvm.umul.with.overflow.i32(i32 %a, i32 %b) + +; x00-LABEL: a: +; x00-NOT: asr +; x00: jsr __muldi3 +; x00-NEXT cmpi.l #0, %d1 +; x00-NEXT sne +define i1 @a(i32 %x) nounwind { + %res = call {i32, i1} @llvm.umul.with.overflow.i32(i32 %x, i32 3) + %obil = extractvalue {i32, i1} %res, 1 + ret i1 %obil +} + +; x00-LABEL: test2: +; x00: add.l +; x00-NEXT: add.l +; x00-NEXT: rts +define i32 @test2(i32 %a, i32 %b) nounwind readnone { +entry: + %tmp0 = add i32 %b, %a + %tmp1 = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 %tmp0, i32 2) + %tmp2 = extractvalue { i32, i1 } %tmp1, 0 + ret i32 %tmp2 +} + +; x00-LABEL: test3: +; x00: add.l +; x00-NOT: asr +; x00: jsr __muldi3 +define i32 @test3(i32 %a, i32 %b) nounwind readnone { +entry: + %tmp0 = add i32 %b, %a + %tmp1 = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 %tmp0, i32 4) + %tmp2 = extractvalue { i32, i1 } %tmp1, 0 + ret i32 %tmp2 +} Index: test/CodeGen/M680x0/ASM/CConv/c-args-inreg.ll =================================================================== --- /dev/null +++ test/CodeGen/M680x0/ASM/CConv/c-args-inreg.ll @@ -0,0 +1,28 @@ +; RUN: llc < %s -mtriple=m680x0-pc-linux -relocation-model=pic -verify-machineinstrs | FileCheck -check-prefix=x00 %s + +; +; Pass first two arguments in registers %d0 and %d1 + +; x00-LABEL: foo_inreg: +; x00: move.l %d0, [[Reg:%a[0-7]]] +; x00: move.l %d1, ([[Reg]]) +; x00: rts + +define void @foo_inreg(i32* nocapture inreg %out, i32 inreg %in) nounwind { +entry: + store i32 %in, i32* %out, align 4 + ret void +} + +; x00-LABEL: bar_inreg: +; x00-DAG: move.l %d0, [[Out:%a[0-7]]] +; x00-DAG: move.l %d1, [[In:%a[0-7]]] +; x00: move.l ([[In]]), ([[Out]]) +; x00: rts + +define void @bar_inreg(i32* nocapture inreg %pOut, i32* nocapture inreg %pIn) nounwind { +entry: + %0 = load i32, i32* %pIn, align 4 + store i32 %0, i32* %pOut, align 4 + ret void +} Index: test/CodeGen/M680x0/ASM/CConv/c-args.ll =================================================================== --- /dev/null +++ test/CodeGen/M680x0/ASM/CConv/c-args.ll @@ -0,0 +1,66 @@ +; RUN: llc < %s -mtriple=m680x0-pc-linux -relocation-model=pic -verify-machineinstrs | FileCheck -check-prefix=x00 %s + +; +; C Call passes all arguments on stack ... + +; x00-LABEL: test1: +; x00: move.l (4,%sp), [[Reg:%a[0-7]]] +; x00: move.l (8,%sp), ([[Reg]]) +; x00: rts +define void @test1(i32* nocapture %out, i32 %in) nounwind { +entry: + store i32 %in, i32* %out, align 4 + ret void +} + +; x00-LABEL: test2: +; x00-DAG: move.l (4,%sp), [[Out:%a[0-7]]] +; x00-DAG: move.l (8,%sp), [[In:%a[0-7]]] +; x00: move.l ([[In]]), ([[Out]]) +; x00: rts +define void @test2(i32* nocapture %pOut, i32* nocapture %pIn) nounwind { +entry: + %0 = load i32, i32* %pIn, align 4 + store i32 %0, i32* %pOut, align 4 + ret void +} + +; x00-LABEL: test3: +; x00: move.l (4,%sp), [[Reg:%a[0-7]]] +; x00: move.b (11,%sp), ([[Reg]]) +; x00: rts +define void @test3(i8* nocapture %out, i8 %in) nounwind { +entry: + store i8 %in, i8* %out, align 1 + ret void +} + +; x00-LABEL: test4: +; x00: move.l (4,%sp), [[Reg:%a[0-7]]] +; x00: move.w (10,%sp), ([[Reg]]) +; x00: rts +define void @test4(i16* nocapture %out, i16 %in) nounwind { +entry: + store i16 %in, i16* %out, align 2 + ret void +} + +; x00-LABEL: test5: +; x00: move.b (7,%sp), [[Reg:%d[0-7]]] +; x00: add.b (11,%sp), [[Reg]] +; x00: rts +define i8 @test5(i8 %a, i8 %b) nounwind { +entry: + %add = add i8 %a, %b + ret i8 %add +} + +; x00-LABEL: test6: +; x00: move.w (6,%sp), [[Reg:%d[0-7]]] +; x00: add.w (10,%sp), [[Reg]] +; x00: rts +define i16 @test6(i16 %a, i16 %b) nounwind { +entry: + %add = add i16 %a, %b + ret i16 %add +} Index: test/CodeGen/M680x0/ASM/CConv/c-call.ll =================================================================== --- /dev/null +++ test/CodeGen/M680x0/ASM/CConv/c-call.ll @@ -0,0 +1,55 @@ +; RUN: llc < %s -mtriple=m680x0-pc-linux -relocation-model=pic -verify-machineinstrs | FileCheck -check-prefix=x00 %s + +; +; Pass all arguments on the stack in reverse order + +; x00-LABEL: test1: +; x00-DAG: move.l #5, (16,%sp) +; x00-DAG: move.l #4, (12,%sp) +; x00-DAG: move.l #3, (8,%sp) +; x00-DAG: move.l #2, (4,%sp) +; x00-DAG: move.l #1, (%sp) +; x00-NEXT: jsr (test1_callee@PLT,%pc) +; x00-NEXT: move.l + +define i32 @test1() nounwind uwtable { +entry: + call void @test1_callee(i32 1, i32 2, i32 3, i32 4, i32 5) nounwind + ret i32 0 +} + +declare void @test1_callee(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e); + +; x00-LABEL: test2: +; x00-DAG: move.l #5, (16,%sp) +; x00-DAG: move.l #4, (12,%sp) +; x00-DAG: move.l #3, (8,%sp) +; x00-DAG: move.l #2, (4,%sp) +; x00-DAG: move.l #1, (%sp) +; x00-NEXT: jsr (test2_callee@PLT,%pc) +; x00-NEXT: move.w + +define i16 @test2() nounwind uwtable { +entry: + call void @test2_callee(i16 1, i16 2, i16 3, i16 4, i16 5) nounwind + ret i16 0 +} + +declare void @test2_callee(i16 %a, i16 %b, i16 %c, i16 %d, i16 %e); + +; x00-LABEL: test3: +; x00-DAG: move.l #5, (16,%sp) +; x00-DAG: move.l #4, (12,%sp) +; x00-DAG: move.l #3, (8,%sp) +; x00-DAG: move.l #2, (4,%sp) +; x00-DAG: move.l #1, (%sp) +; x00-NEXT: jsr (test3_callee@PLT,%pc) +; x00-NEXT: move.b + +define i8 @test3() nounwind uwtable { +entry: + call void @test3_callee(i8 1, i8 2, i8 3, i8 4, i8 5) nounwind + ret i8 0 +} + +declare void @test3_callee(i8 %a, i8 %b, i8 %c, i8 %d, i8 %e); Index: test/CodeGen/M680x0/ASM/CConv/fastcc-args.ll =================================================================== --- /dev/null +++ test/CodeGen/M680x0/ASM/CConv/fastcc-args.ll @@ -0,0 +1,40 @@ +; RUN: llc < %s -mtriple=m680x0-pc-linux -relocation-model=pic -verify-machineinstrs | FileCheck -check-prefix=x00 %s + +; +; C Call passes all arguments on stack ... + +; x00-LABEL: test1: +; x00: move.l %d0, (%a0) +; x00-NEXT: rts + +define fastcc void @test1(i32* nocapture %out, i32 %in) nounwind { +entry: + store i32 %in, i32* %out, align 4 + ret void +} + +; x00-LABEL: test2: +; x00: move.l (%a1), (%a0) +; x00-NEXT: rts + +define fastcc void @test2(i32* nocapture %pOut, i32* nocapture %pIn) nounwind { +entry: + %0 = load i32, i32* %pIn, align 4 + store i32 %0, i32* %pOut, align 4 + ret void +} + + +; x00-LABEL: test3: +; x00: add.l %d1, %d0 +; x00-NEXT: add.l %a0, %d0 +; x00-NEXT: add.l %a1, %d0 +; x00-NEXT: add.l (4,%sp), %d0 + +define fastcc i32 @test3(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) nounwind { + %1 = add i32 %a, %b + %2 = add i32 %1, %c + %3 = add i32 %2, %d + %4 = add i32 %3, %e + ret i32 %4 +} Index: test/CodeGen/M680x0/ASM/CConv/fastcc-call.ll =================================================================== --- /dev/null +++ test/CodeGen/M680x0/ASM/CConv/fastcc-call.ll @@ -0,0 +1,41 @@ +; RUN: llc < %s -mtriple=m680x0-pc-linux -relocation-model=pic -verify-machineinstrs | FileCheck -check-prefix=x00 %s + +; +; Pass first 4 arguments in registers %d0,%d1,%a0,%a1 the rest goes onto stack + +; x00-LABEL: foo1: +; x00-DAG: move.l #5, (%sp) +; x00-DAG: move.l #4, %a1 +; x00-DAG: move.l #3, %a0 +; x00-DAG: move.l #2, %d1 +; x00-DAG: move.l #1, %d0 +; x00-NEXT: jsr (bar1@PLT,%pc) + +define i32 @foo1() nounwind uwtable { +entry: + call fastcc void @bar1(i32 1, i32 2, i32 3, i32 4, i32 5) nounwind + ret i32 0 +} + +declare fastcc void @bar1(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e); + + +; +; Pass pointers in %a registers if there are any free left + +; x00-LABEL: foo2: +; x00-DAG: lea (8,%sp), %a0 +; x00-DAG: lea (4,%sp), %a1 +; x00-DAG: move.l #2, %d0 +; x00-DAG: move.l #4, %d1 +; x00-DAG: jsr (bar2@PLT,%pc) + +define i32 @foo2() nounwind uwtable { +entry: + %a = alloca i32, align 4 + %b = alloca i32, align 4 + call fastcc void @bar2(i32* %a, i32 2, i32* %b, i32 4) nounwind + ret i32 0 +} + +declare fastcc void @bar2(i32* %a, i32 %b, i32* %c, i32 %d); Index: test/CodeGen/M680x0/ASM/CodeModel/medium-pic.ll =================================================================== --- /dev/null +++ test/CodeGen/M680x0/ASM/CodeModel/medium-pic.ll @@ -0,0 +1,219 @@ +; RUN: llc < %s -O2 -mtriple=m680x0-linux-gnu -verify-machineinstrs \ +; RUN: -code-model=medium -relocation-model=pic \ +; RUN: | FileCheck %s -check-prefix=x00 + +@ptr = external global i32* +@dst = external global i32 +@src = external global i32 + +; x00-LABEL: test0: +; x00: move.l (dst@GOTPCREL,%pc), %a0 +; x00-NEXT: move.l (ptr@GOTPCREL,%pc), %a1 +; x00-NEXT: %a0, (%a1) +; x00-NEXT: move.l (src@GOTPCREL,%pc), %a1 +; x00-NEXT: (%a1), (%a0) +; x00-NEXT: rts +define void @test0() nounwind { +entry: + store i32* @dst, i32** @ptr + %tmp.s = load i32, i32* @src + store i32 %tmp.s, i32* @dst + ret void +} + +@ptr2 = global i32* null +@dst2 = global i32 0 +@src2 = global i32 0 + +; x00-LABEL: test1: +; x00: move.l (dst2@GOTPCREL,%pc), %a0 +; x00-NEXT: move.l (ptr2@GOTPCREL,%pc), %a1 +; x00-NEXT: %a0, (%a1) +; x00-NEXT: move.l (src2@GOTPCREL,%pc), %a1 +; x00-NEXT: (%a1), (%a0) +; x00-NEXT: rts +define void @test1() nounwind { +entry: + store i32* @dst2, i32** @ptr2 + %tmp.s = load i32, i32* @src2 + store i32 %tmp.s, i32* @dst2 + ret void +} + +declare i8* @malloc(i32) + +; x00-LABEL: test2: +; x00: move.l #40, (%sp) +; x00: jsr (malloc@PLT,%pc) +define void @test2() nounwind { +entry: + %ptr = call i8* @malloc(i32 40) + ret void +} + +@pfoo = external global void(...)* +declare void(...)* @afoo(...) + +; x00-LABEL: test3: +; x00: jsr (afoo@PLT,%pc) +; x00-NEXT: move.l %d0, %a0 +; x00-NEXT: move.l (pfoo@GOTPCREL,%pc), %a1 +; x00-NEXT: move.l %a0, (%a1) +; x00-NEXT: jsr (%a0) +define void @test3() nounwind { +entry: + %tmp = call void(...)*(...) @afoo() + store void(...)* %tmp, void(...)** @pfoo + %tmp1 = load void(...)*, void(...)** @pfoo + call void(...) %tmp1() + ret void +} + +declare void @foo(...) + +; x00-LABEL: test4: +; x00: jsr (foo@PLT,%pc) +define void @test4() nounwind { +entry: + call void(...) @foo() + ret void +} + +@ptr6 = internal global i32* null +@dst6 = internal global i32 0 +@src6 = internal global i32 0 + +; x00-LABEL: test5: +; x00 lea (dst6,%pc), %a0 +; x00 move.l %a0, (ptr6,%pc) +; x00 move.l (src6,%pc), (%a0) +define void @test5() nounwind { +entry: + store i32* @dst6, i32** @ptr6 + %tmp.s = load i32, i32* @src6 + store i32 %tmp.s, i32* @dst6 + ret void +} + + +;;; Test constant pool references. +;; FIXME no constant pool atm +;; 00: .LCPI6_0: +;; 00-LABEL: test6: +;; 00: calll .L6$pb +;; 00: .L6$pb: +;; 00: addl $_GLOBAL_OFFSET_TABLE_+(.L{{.*}}-.L6$pb), +;; 00: fldl .LCPI6_0@GOTOFF( +;define double @test6(i32 %a.u) nounwind { +;entry: +; %tmp = icmp eq i32 %a.u,0 +; %retval = select i1 %tmp, double 4.561230e+02, double 1.234560e+02 +; ret double %retval +;} + + +; Test jump table references. +; +; x00-LABEL: test7: +; x00: move.l (4,%sp), %d0 +; x00-NEXT: add.l #-1, %d0 +; x00-NEXT: move.l %d0, %d1 +; x00-NEXT: sub.l #12, %d1 +; x00-NEXT: bhi .LBB{{.*}}_14 +; x00: lea (_GLOBAL_OFFSET_TABLE_@GOTPCREL,%pc), %a0 +; x00: lsl.l #2, %d0 +; x00: add.l #.LJTI{{.*}}_0@GOTOFF, %a1 +; x00: add.l (0,%a1,%d0), %a0 +; x00-NEXT: jmp (%a0) +; +; x00: .LBB{{.*}}_2: +; x00-NEXT: bra foo1@PLT +; x00: .LBB{{.*}}_8: +; x00-NEXT: bra foo1@PLT +; x00: .LBB{{.*}}_14: +; x00-NEXT: bra foo6@PLT +; x00: .LBB{{.*}}_9: +; x00-NEXT: bra foo2@PLT +; x00: .LBB{{.*}}_10: +; x00-NEXT: bra foo6@PLT +; x00: .LBB{{.*}}_12: +; x00-NEXT: bra foo4@PLT +; x00: .LBB{{.*}}_3: +; x00-NEXT: bra foo2@PLT +; x00: .LBB{{.*}}_5: +; x00-NEXT: bra foo3@PLT +; x00: .LBB{{.*}}_6: +; x00-NEXT: bra foo4@PLT +; x00: .LBB{{.*}}_11: +; x00-NEXT: bra foo3@PLT +; x00: .LBB{{.*}}_4: +; x00-NEXT: bra foo6@PLT +; x00: .LBB{{.*}}_7: +; x00-NEXT: bra foo5@PLT +; x00: .LBB{{.*}}_13: +; x00-NEXT: bra foo5@PLT +; +; x00: .LJTI{{.*}}_0: +; x00: .long .LBB{{.*}}_2@GOTOFF +; x00: .long .LBB{{.*}}_8@GOTOFF +; x00: .long .LBB{{.*}}_14@GOTOFF +; x00: .long .LBB{{.*}}_9@GOTOFF +; x00: .long .LBB{{.*}}_10@GOTOFF +; x00: .long .LBB{{.*}}_12@GOTOFF +; x00: .long .LBB{{.*}}_3@GOTOFF +; x00: .long .LBB{{.*}}_5@GOTOFF +; x00: .long .LBB{{.*}}_6@GOTOFF +; x00: .long .LBB{{.*}}_11@GOTOFF +; x00: .long .LBB{{.*}}_4@GOTOFF +; x00: .long .LBB{{.*}}_7@GOTOFF +; x00: .long .LBB{{.*}}_13@GOTOFF +define void @test7(i32 %n.u) nounwind { +entry: + switch i32 %n.u, label %bb12 [i32 1, label %bb i32 2, label %bb6 i32 4, label %bb7 i32 5, label %bb8 i32 6, label %bb10 i32 7, label %bb1 i32 8, label %bb3 i32 9, label %bb4 i32 10, label %bb9 i32 11, label %bb2 i32 12, label %bb5 i32 13, label %bb11 ] +bb: + tail call void(...) @foo1() + ret void +bb1: + tail call void(...) @foo2() + ret void +bb2: + tail call void(...) @foo6() + ret void +bb3: + tail call void(...) @foo3() + ret void +bb4: + tail call void(...) @foo4() + ret void +bb5: + tail call void(...) @foo5() + ret void +bb6: + tail call void(...) @foo1() + ret void +bb7: + tail call void(...) @foo2() + ret void +bb8: + tail call void(...) @foo6() + ret void +bb9: + tail call void(...) @foo3() + ret void +bb10: + tail call void(...) @foo4() + ret void +bb11: + tail call void(...) @foo5() + ret void +bb12: + tail call void(...) @foo6() + ret void +} + +declare void @foo1(...) +declare void @foo2(...) +declare void @foo6(...) +declare void @foo3(...) +declare void @foo4(...) +declare void @foo5(...) Index: test/CodeGen/M680x0/ASM/CodeModel/medium-pie-global-access.ll =================================================================== --- /dev/null +++ test/CodeGen/M680x0/ASM/CodeModel/medium-pie-global-access.ll @@ -0,0 +1,102 @@ +; RUN: llc < %s -O2 -mtriple=m680x0-linux-gnu -verify-machineinstrs \ +; RUN: -code-model=medium -relocation-model=pic \ +; RUN: | FileCheck %s -check-prefix=x00 + +; External Linkage +@a = global i32 0, align 4 + +; x00-LABEL: my_access_global_a: +; x00: lea (_GLOBAL_OFFSET_TABLE_@GOTPCREL,%pc), %a0 +; x00: move.l #a@GOTOFF, %d0 +; x00: move.l (0,%a0,%d0), %d0 +; x00-NEXT: rts +define i32 @my_access_global_a() #0 { +entry: + %0 = load i32, i32* @a, align 4 + ret i32 %0 +} + +; WeakAny Linkage +@b = weak global i32 0, align 4 + +; x00-LABEL: my_access_global_b: +; x00: lea (_GLOBAL_OFFSET_TABLE_@GOTPCREL,%pc), %a0 +; x00-NEXT: move.l #b@GOTOFF, %d0 +; x00-NEXT: move.l (0,%a0,%d0), %d0 +define i32 @my_access_global_b() #0 { +entry: + %0 = load i32, i32* @b, align 4 + ret i32 %0 +} + +; Internal Linkage +@c = internal global i32 0, align 4 + +; x00-LABEL: my_access_global_c: +; x00: lea (_GLOBAL_OFFSET_TABLE_@GOTPCREL,%pc), %a0 +; x00-NEXT: move.l #c@GOTOFF, %d0 +; x00-NEXT: move.l (0,%a0,%d0), %d0 +define i32 @my_access_global_c() #0 { +entry: + %0 = load i32, i32* @c, align 4 + ret i32 %0 +} + +; External Linkage, only declaration. +@d = external global i32, align 4 + +; x00-LABEL: my_access_global_load_d: +; x00: move.l (d@GOTPCREL,%pc), %a0 +; x00-NEXT: move.l (%a0), %d0 +define i32 @my_access_global_load_d() #0 { +entry: + %0 = load i32, i32* @d, align 4 + ret i32 %0 +} + +; External Linkage, only declaration, store a value. +; x00-LABEL: my_access_global_store_d: +; x00: move.l (d@GOTPCREL,%pc), %a0 +; x00-NEXT: move.l #2, (%a0) +define i32 @my_access_global_store_d() #0 { +entry: + store i32 2, i32* @d, align 4 + ret i32 0 +} + +; External Linkage, function pointer access. +declare i32 @access_fp(i32 ()*) +declare i32 @foo() + +; x00-LABEL: my_access_fp_foo: +; x00: move.l (foo@GOTPCREL,%pc), (%sp) +; x00-NEXT: jsr (access_fp@PLT,%pc) +define i32 @my_access_fp_foo() #0 { +entry: + %call = call i32 @access_fp(i32 ()* @foo) + ret i32 %call +} + +; LinkOnceODR Linkage, function pointer access. + +$bar = comdat any + +define linkonce_odr i32 @bar() comdat { +entry: + ret i32 0 +} + +; x00-LABEL: my_access_fp_bar: +; x00: lea (_GLOBAL_OFFSET_TABLE_@GOTPCREL,%pc), %a0 +; x00-NEXT: add.l #bar@GOTOFF, %a0 +; x00-NEXT: move.l %a0, (%sp) +; x00-NEXT: jsr (access_fp@PLT,%pc) +define i32 @my_access_fp_bar() #0 { +entry: + %call = call i32 @access_fp(i32 ()* @bar) + ret i32 %call +} + +!llvm.module.flags = !{!0, !1} +!0 = !{i32 1, !"PIC Level", i32 1} +!1 = !{i32 1, !"PIE Level", i32 1} Index: test/CodeGen/M680x0/ASM/CodeModel/medium-pie.ll =================================================================== --- /dev/null +++ test/CodeGen/M680x0/ASM/CodeModel/medium-pie.ll @@ -0,0 +1,44 @@ +; RUN: llc < %s -O0 -mtriple=m680x0-linux-gnu -verify-machineinstrs \ +; RUN: -code-model=medium -relocation-model=pic \ +; RUN:| FileCheck %s -check-prefix=x00 + +define weak void @weak_foo() { + ret void +} + +define weak_odr void @weak_odr_foo() { + ret void +} + +define internal void @internal_foo() { + ret void +} + +declare i32 @ext_baz() + +define void @foo() { + ret void +} + + +; x00-LABEL: bar: +; x00: jsr (foo,%pc) +; x00: jsr (weak_odr_foo,%pc) +; x00: jsr (weak_foo,%pc) +; x00: jsr (internal_foo,%pc) +; x00: jsr (ext_baz@PLT,%pc) +define void @bar() { +entry: + call void @foo() + call void @weak_odr_foo() + call void @weak_foo() + call void @internal_foo() + call i32 @ext_baz() + ret void +} + +; -fpie for local global data tests should be added here + +!llvm.module.flags = !{!0, !1} +!0 = !{i32 1, !"PIC Level", i32 1} +!1 = !{i32 1, !"PIE Level", i32 1} Index: test/CodeGen/M680x0/ASM/CodeModel/medium-static.ll =================================================================== --- /dev/null +++ test/CodeGen/M680x0/ASM/CodeModel/medium-static.ll @@ -0,0 +1,212 @@ +; RUN: llc < %s -O2 -mtriple=m680x0-linux-gnu -verify-machineinstrs \ +; RUN: -code-model=medium -relocation-model=static \ +; RUN: | FileCheck %s -check-prefix=x00 + +@ptr = external global i32* +@dst = external global i32 +@src = external global i32 + +; x00-LABEL: test0: +; x00: move.l #dst, ptr +; x00-NEXT: move.l src, dst +; x00-NEXT: rts +define void @test0() nounwind { +entry: + store i32* @dst, i32** @ptr + %tmp.s = load i32, i32* @src + store i32 %tmp.s, i32* @dst + ret void +} + +@ptr2 = global i32* null +@dst2 = global i32 0 +@src2 = global i32 0 + +; x00-LABEL: test1: +; x00: move.l #dst2, ptr2 +; x00-NEXT: move.l src2, dst2 +; x00-NEXT: rts +define void @test1() nounwind { +entry: + store i32* @dst2, i32** @ptr2 + %tmp.s = load i32, i32* @src2 + store i32 %tmp.s, i32* @dst2 + ret void +} + +declare i8* @malloc(i32) + +; x00-LABEL: test2: +; x00: move.l #40, (%sp) +; x00: jsr malloc +define void @test2() nounwind { +entry: + %ptr = call i8* @malloc(i32 40) + ret void +} + +@pfoo = external global void(...)* +declare void(...)* @afoo(...) + +; x00-LABEL: test3: +; x00: jsr afoo +; x00-NEXT: move.l %d0, %a0 +; x00-NEXT: move.l %a0, pfoo +; x00-NEXT: jsr (%a0) +define void @test3() nounwind { +entry: + %tmp = call void(...)*(...) @afoo() + store void(...)* %tmp, void(...)** @pfoo + %tmp1 = load void(...)*, void(...)** @pfoo + call void(...) %tmp1() + ret void +} + +declare void @foo(...) + +; x00-LABEL: test4: +; x00: jsr foo +define void @test4() nounwind { +entry: + call void(...) @foo() + ret void +} + +@ptr6 = internal global i32* null +@dst6 = internal global i32 0 +@src6 = internal global i32 0 + +; x00-LABEL: test5: +; x00 lea (dst6,%pc), %a0 +; x00 move.l %a0, (ptr6,%pc) +; x00 move.l (src6,%pc), (%a0) +define void @test5() nounwind { +entry: + store i32* @dst6, i32** @ptr6 + %tmp.s = load i32, i32* @src6 + store i32 %tmp.s, i32* @dst6 + ret void +} + + +;;; Test constant pool references. +;; FIXME no constant pool atm +;; 00: .LCPI6_0: +;; 00-LABEL: test6: +;; 00: calll .L6$pb +;; 00: .L6$pb: +;; 00: addl $_GLOBAL_OFFSET_TABLE_+(.L{{.*}}-.L6$pb), +;; 00: fldl .LCPI6_0@GOTOFF( +;define double @test6(i32 %a.u) nounwind { +;entry: +; %tmp = icmp eq i32 %a.u,0 +; %retval = select i1 %tmp, double 4.561230e+02, double 1.234560e+02 +; ret double %retval +;} + + +; Test jump table references. +; +; x00-LABEL: test7: +; +; x00: move.l (4,%sp), %d0 +; x00-NEXT: add.l #-1, %d0 +; x00-NEXT: move.l %d0, %d1 +; x00-NEXT: sub.l #12, %d1 +; x00-NEXT: bhi .LBB{{.*}}_14 +; x00: lsl.l #2, %d0 +; x00-NEXT: move.l #.LJTI{{.*}}_0, %a0 +; x00-NEXT: move.l (0,%a0,%d0), %a0 +; x00-NEXT: jmp (%a0) +; +; x00: .LBB{{.*}}_2: +; x00-NEXT: bra foo1 +; x00: .LBB{{.*}}_8: +; x00-NEXT: bra foo1 +; x00: .LBB{{.*}}_14: +; x00-NEXT: bra foo6 +; x00: .LBB{{.*}}_9: +; x00-NEXT: bra foo2 +; x00: .LBB{{.*}}_10: +; x00-NEXT: bra foo6 +; x00: .LBB{{.*}}_12: +; x00-NEXT: bra foo4 +; x00: .LBB{{.*}}_3: +; x00-NEXT: bra foo2 +; x00: .LBB{{.*}}_5: +; x00-NEXT: bra foo3 +; x00: .LBB{{.*}}_6: +; x00-NEXT: bra foo4 +; x00: .LBB{{.*}}_11: +; x00-NEXT: bra foo3 +; x00: .LBB{{.*}}_4: +; x00-NEXT: bra foo6 +; x00: .LBB{{.*}}_7: +; x00-NEXT: bra foo5 +; x00: .LBB{{.*}}_13: +; x00-NEXT: bra foo5 +; +; x00: .p2align 2 +; x00: .long .LBB{{.*}}_2 +; x00: .long .LBB{{.*}}_8 +; x00: .long .LBB{{.*}}_14 +; x00: .long .LBB{{.*}}_9 +; x00: .long .LBB{{.*}}_10 +; x00: .long .LBB{{.*}}_12 +; x00: .long .LBB{{.*}}_3 +; x00: .long .LBB{{.*}}_5 +; x00: .long .LBB{{.*}}_6 +; x00: .long .LBB{{.*}}_11 +; x00: .long .LBB{{.*}}_4 +; x00: .long .LBB{{.*}}_7 +; x00: .long .LBB{{.*}}_13 +define void @test7(i32 %n.u) nounwind { +entry: + switch i32 %n.u, label %bb12 [i32 1, label %bb i32 2, label %bb6 i32 4, label %bb7 i32 5, label %bb8 i32 6, label %bb10 i32 7, label %bb1 i32 8, label %bb3 i32 9, label %bb4 i32 10, label %bb9 i32 11, label %bb2 i32 12, label %bb5 i32 13, label %bb11 ] +bb: + tail call void(...) @foo1() + ret void +bb1: + tail call void(...) @foo2() + ret void +bb2: + tail call void(...) @foo6() + ret void +bb3: + tail call void(...) @foo3() + ret void +bb4: + tail call void(...) @foo4() + ret void +bb5: + tail call void(...) @foo5() + ret void +bb6: + tail call void(...) @foo1() + ret void +bb7: + tail call void(...) @foo2() + ret void +bb8: + tail call void(...) @foo6() + ret void +bb9: + tail call void(...) @foo3() + ret void +bb10: + tail call void(...) @foo4() + ret void +bb11: + tail call void(...) @foo5() + ret void +bb12: + tail call void(...) @foo6() + ret void +} + +declare void @foo1(...) +declare void @foo2(...) +declare void @foo6(...) +declare void @foo3(...) +declare void @foo4(...) +declare void @foo5(...) Index: test/CodeGen/M680x0/ASM/CodeModel/small-pic.ll =================================================================== --- /dev/null +++ test/CodeGen/M680x0/ASM/CodeModel/small-pic.ll @@ -0,0 +1,219 @@ +; RUN: llc < %s -O2 -mtriple=m680x0-linux-gnu -verify-machineinstrs \ +; RUN: -code-model=small -relocation-model=pic \ +; RUN: | FileCheck %s -check-prefix=x00 + +@ptr = external global i32* +@dst = external global i32 +@src = external global i32 + +; x00-LABEL: test0: +; x00: move.l (dst@GOTPCREL,%pc), %a0 +; x00-NEXT: move.l (ptr@GOTPCREL,%pc), %a1 +; x00-NEXT: %a0, (%a1) +; x00-NEXT: move.l (src@GOTPCREL,%pc), %a1 +; x00-NEXT: (%a1), (%a0) +; x00-NEXT: rts +define void @test0() nounwind { +entry: + store i32* @dst, i32** @ptr + %tmp.s = load i32, i32* @src + store i32 %tmp.s, i32* @dst + ret void +} + +@ptr2 = global i32* null +@dst2 = global i32 0 +@src2 = global i32 0 + +; x00-LABEL: test1: +; x00: move.l (dst2@GOTPCREL,%pc), %a0 +; x00-NEXT: move.l (ptr2@GOTPCREL,%pc), %a1 +; x00-NEXT: %a0, (%a1) +; x00-NEXT: move.l (src2@GOTPCREL,%pc), %a1 +; x00-NEXT: (%a1), (%a0) +; x00-NEXT: rts +define void @test1() nounwind { +entry: + store i32* @dst2, i32** @ptr2 + %tmp.s = load i32, i32* @src2 + store i32 %tmp.s, i32* @dst2 + ret void +} + +declare i8* @malloc(i32) + +; x00-LABEL: test2: +; x00: move.l #40, (%sp) +; x00: jsr (malloc@PLT,%pc) +define void @test2() nounwind { +entry: + %ptr = call i8* @malloc(i32 40) + ret void +} + +@pfoo = external global void(...)* +declare void(...)* @afoo(...) + +; x00-LABEL: test3: +; x00: jsr (afoo@PLT,%pc) +; x00-NEXT: move.l %d0, %a0 +; x00-NEXT: move.l (pfoo@GOTPCREL,%pc), %a1 +; x00-NEXT: move.l %a0, (%a1) +; x00-NEXT: jsr (%a0) +define void @test3() nounwind { +entry: + %tmp = call void(...)*(...) @afoo() + store void(...)* %tmp, void(...)** @pfoo + %tmp1 = load void(...)*, void(...)** @pfoo + call void(...) %tmp1() + ret void +} + +declare void @foo(...) + +; x00-LABEL: test4: +; x00: jsr (foo@PLT,%pc) +define void @test4() nounwind { +entry: + call void(...) @foo() + ret void +} + +@ptr6 = internal global i32* null +@dst6 = internal global i32 0 +@src6 = internal global i32 0 + +; x00-LABEL: test5: +; x00 lea (dst6,%pc), %a0 +; x00 move.l %a0, (ptr6,%pc) +; x00 move.l (src6,%pc), (%a0) +define void @test5() nounwind { +entry: + store i32* @dst6, i32** @ptr6 + %tmp.s = load i32, i32* @src6 + store i32 %tmp.s, i32* @dst6 + ret void +} + + +;;; Test constant pool references. +;; FIXME no constant pool atm +;; 00: .LCPI6_0: +;; 00-LABEL: test6: +;; 00: calll .L6$pb +;; 00: .L6$pb: +;; 00: addl $_GLOBAL_OFFSET_TABLE_+(.L{{.*}}-.L6$pb), +;; 00: fldl .LCPI6_0@GOTOFF( +;define double @test6(i32 %a.u) nounwind { +;entry: +; %tmp = icmp eq i32 %a.u,0 +; %retval = select i1 %tmp, double 4.561230e+02, double 1.234560e+02 +; ret double %retval +;} + + +; Test jump table references. +; +; x00-LABEL: test7: +; +; x00: move.l (4,%sp), %d0 +; x00-NEXT: add.l #-1, %d0 +; x00-NEXT: move.l %d0, %d1 +; x00-NEXT: sub.l #12, %d1 +; x00-NEXT: bhi .LBB{{.*}}_14 +; x00: lsl.l #2, %d0 +; x00-NEXT: lea (.LJTI{{.*}}_0,%pc), %a0 +; x00-NEXT: add.l (0,%a0,%d0), %a0 +; x00-NEXT: jmp (%a0) +; +; x00: .LBB{{.*}}_2: +; x00-NEXT: bra foo1@PLT +; x00: .LBB{{.*}}_8: +; x00-NEXT: bra foo1@PLT +; x00: .LBB{{.*}}_14: +; x00-NEXT: bra foo6@PLT +; x00: .LBB{{.*}}_9: +; x00-NEXT: bra foo2@PLT +; x00: .LBB{{.*}}_10: +; x00-NEXT: bra foo6@PLT +; x00: .LBB{{.*}}_12: +; x00-NEXT: bra foo4@PLT +; x00: .LBB{{.*}}_3: +; x00-NEXT: bra foo2@PLT +; x00: .LBB{{.*}}_5: +; x00-NEXT: bra foo3@PLT +; x00: .LBB{{.*}}_6: +; x00-NEXT: bra foo4@PLT +; x00: .LBB{{.*}}_11: +; x00-NEXT: bra foo3@PLT +; x00: .LBB{{.*}}_4: +; x00-NEXT: bra foo6@PLT +; x00: .LBB{{.*}}_7: +; x00-NEXT: bra foo5@PLT +; x00: .LBB{{.*}}_13: +; x00-NEXT: bra foo5@PLT +; +; x00: .p2align 2 +; x00: .long .LBB{{.*}}_2-.LJTI{{.*}}_0 +; x00: .long .LBB{{.*}}_8-.LJTI{{.*}}_0 +; x00: .long .LBB{{.*}}_14-.LJTI{{.*}}_0 +; x00: .long .LBB{{.*}}_9-.LJTI{{.*}}_0 +; x00: .long .LBB{{.*}}_10-.LJTI{{.*}}_0 +; x00: .long .LBB{{.*}}_12-.LJTI{{.*}}_0 +; x00: .long .LBB{{.*}}_3-.LJTI{{.*}}_0 +; x00: .long .LBB{{.*}}_5-.LJTI{{.*}}_0 +; x00: .long .LBB{{.*}}_6-.LJTI{{.*}}_0 +; x00: .long .LBB{{.*}}_11-.LJTI{{.*}}_0 +; x00: .long .LBB{{.*}}_4-.LJTI{{.*}}_0 +; x00: .long .LBB{{.*}}_7-.LJTI{{.*}}_0 +; x00: .long .LBB{{.*}}_13-.LJTI{{.*}}_0 +define void @test7(i32 %n.u) nounwind { +entry: + switch i32 %n.u, label %bb12 [i32 1, label %bb i32 2, label %bb6 i32 4, label %bb7 i32 5, label %bb8 i32 6, label %bb10 i32 7, label %bb1 i32 8, label %bb3 i32 9, label %bb4 i32 10, label %bb9 i32 11, label %bb2 i32 12, label %bb5 i32 13, label %bb11 ] +bb: + tail call void(...) @foo1() + ret void +bb1: + tail call void(...) @foo2() + ret void +bb2: + tail call void(...) @foo6() + ret void +bb3: + tail call void(...) @foo3() + ret void +bb4: + tail call void(...) @foo4() + ret void +bb5: + tail call void(...) @foo5() + ret void +bb6: + tail call void(...) @foo1() + ret void +bb7: + tail call void(...) @foo2() + ret void +bb8: + tail call void(...) @foo6() + ret void +bb9: + tail call void(...) @foo3() + ret void +bb10: + tail call void(...) @foo4() + ret void +bb11: + tail call void(...) @foo5() + ret void +bb12: + tail call void(...) @foo6() + ret void +} + +declare void @foo1(...) +declare void @foo2(...) +declare void @foo6(...) +declare void @foo3(...) +declare void @foo4(...) +declare void @foo5(...) Index: test/CodeGen/M680x0/ASM/CodeModel/small-pie-global-access.ll =================================================================== --- /dev/null +++ test/CodeGen/M680x0/ASM/CodeModel/small-pie-global-access.ll @@ -0,0 +1,95 @@ +; RUN: llc < %s -O2 -mtriple=m680x0-linux-gnu -verify-machineinstrs \ +; RUN: -code-model=small -relocation-model=pic \ +; RUN: | FileCheck %s -check-prefix=x00 + +; External Linkage +@a = global i32 0, align 4 + +; x00-LABEL: my_access_global_a: +; x00: move.l (a,%pc), %d0 +; x00-NEXT: rts +define i32 @my_access_global_a() #0 { +entry: + %0 = load i32, i32* @a, align 4 + ret i32 %0 +} + +; WeakAny Linkage +@b = weak global i32 0, align 4 + +; x00-LABEL: my_access_global_b: +; x00: move.l (b,%pc), %d0 +define i32 @my_access_global_b() #0 { +entry: + %0 = load i32, i32* @b, align 4 + ret i32 %0 +} + +; Internal Linkage +@c = internal global i32 0, align 4 + +; x00-LABEL: my_access_global_c: +; x00: move.l (c,%pc), %d0 +define i32 @my_access_global_c() #0 { +entry: + %0 = load i32, i32* @c, align 4 + ret i32 %0 +} + +; External Linkage, only declaration. +@d = external global i32, align 4 + +; x00-LABEL: my_access_global_load_d: +; x00: move.l (d@GOTPCREL,%pc), %a0 +; x00-NEXT: move.l (%a0), %d0 +define i32 @my_access_global_load_d() #0 { +entry: + %0 = load i32, i32* @d, align 4 + ret i32 %0 +} + +; External Linkage, only declaration, store a value. +; x00-LABEL: my_access_global_store_d: +; x00: move.l (d@GOTPCREL,%pc), %a0 +; x00-NEXT: move.l #2, (%a0) +define i32 @my_access_global_store_d() #0 { +entry: + store i32 2, i32* @d, align 4 + ret i32 0 +} + +; External Linkage, function pointer access. +declare i32 @access_fp(i32 ()*) +declare i32 @foo() + +; x00-LABEL: my_access_fp_foo: +; x00: move.l (foo@GOTPCREL,%pc), (%sp) +; x00-NEXT: jsr (access_fp@PLT,%pc) +define i32 @my_access_fp_foo() #0 { +entry: + %call = call i32 @access_fp(i32 ()* @foo) + ret i32 %call +} + +; LinkOnceODR Linkage, function pointer access. + +$bar = comdat any + +define linkonce_odr i32 @bar() comdat { +entry: + ret i32 0 +} + +; x00-LABEL: my_access_fp_bar: +; x00: lea (bar,%pc), %a0 +; x00-NEXT: move.l %a0, (%sp) +; x00-NEXT: jsr (access_fp@PLT,%pc) +define i32 @my_access_fp_bar() #0 { +entry: + %call = call i32 @access_fp(i32 ()* @bar) + ret i32 %call +} + +!llvm.module.flags = !{!0, !1} +!0 = !{i32 1, !"PIC Level", i32 1} +!1 = !{i32 1, !"PIE Level", i32 1} Index: test/CodeGen/M680x0/ASM/CodeModel/small-pie.ll =================================================================== --- /dev/null +++ test/CodeGen/M680x0/ASM/CodeModel/small-pie.ll @@ -0,0 +1,44 @@ +; RUN: llc < %s -O0 -mtriple=m680x0-linux-gnu -verify-machineinstrs \ +; RUN: -code-model=small -relocation-model=pic \ +; RUN: | FileCheck %s -check-prefix=x00 + +define weak void @weak_foo() { + ret void +} + +define weak_odr void @weak_odr_foo() { + ret void +} + +define internal void @internal_foo() { + ret void +} + +declare i32 @ext_baz() + +define void @foo() { + ret void +} + + +; x00-LABEL: bar: +; x00: jsr (foo,%pc) +; x00: jsr (weak_odr_foo,%pc) +; x00: jsr (weak_foo,%pc) +; x00: jsr (internal_foo,%pc) +; x00: jsr (ext_baz@PLT,%pc) +define void @bar() { +entry: + call void @foo() + call void @weak_odr_foo() + call void @weak_foo() + call void @internal_foo() + call i32 @ext_baz() + ret void +} + +; -fpie for local global data tests should be added here + +!llvm.module.flags = !{!0, !1} +!0 = !{i32 1, !"PIC Level", i32 1} +!1 = !{i32 1, !"PIE Level", i32 1} Index: test/CodeGen/M680x0/ASM/CodeModel/small-static.ll =================================================================== --- /dev/null +++ test/CodeGen/M680x0/ASM/CodeModel/small-static.ll @@ -0,0 +1,216 @@ +; RUN: llc < %s -O2 -mtriple=m680x0-linux-gnu -verify-machineinstrs \ +; RUN: -code-model=small -relocation-model=static \ +; RUN: | FileCheck %s -check-prefix=x00 + +@ptr = external global i32* +@dst = external global i32 +@src = external global i32 + +; x00-LABEL: test0: +; x00: lea (dst,%pc), %a0 +; x00: lea (ptr,%pc), %a1 +; x00-NEXT: move.l %a0, (%a1) +; x00-NEXT: move.l (src,%pc), (%a0) +; x00-NEXT: rts +define void @test0() nounwind { +entry: + store i32* @dst, i32** @ptr + %tmp.s = load i32, i32* @src + store i32 %tmp.s, i32* @dst + ret void +} + +@ptr2 = global i32* null +@dst2 = global i32 0 +@src2 = global i32 0 + +; x00-LABEL: test1: +; x00: lea (dst2,%pc), %a0 +; x00-NEXT move.l %a0, (ptr2,%pc) +; x00-NEXT move.l (src2,%pc), (dst2,%pc) +; x00-NEXT rts +define void @test1() nounwind { +entry: + store i32* @dst2, i32** @ptr2 + %tmp.s = load i32, i32* @src2 + store i32 %tmp.s, i32* @dst2 + ret void +} + +declare i8* @malloc(i32) + +; x00-LABEL: test2: +; x00: move.l #40, (%sp) +; x00: jsr malloc +define void @test2() nounwind { +entry: + %ptr = call i8* @malloc(i32 40) + ret void +} + +@pfoo = external global void(...)* +declare void(...)* @afoo(...) + +; x00-LABEL: test3: +; x00: jsr afoo +; x00-NEXT: move.l %d0, %a0 +; x00-NEXT: lea (pfoo,%pc), %a1 +; x00-NEXT: move.l %a0, (%a1) +; x00-NEXT: jsr (%a0) +define void @test3() nounwind { +entry: + %tmp = call void(...)*(...) @afoo() + store void(...)* %tmp, void(...)** @pfoo + %tmp1 = load void(...)*, void(...)** @pfoo + call void(...) %tmp1() + ret void +} + +declare void @foo(...) + +; x00-LABEL: test4: +; x00: jsr foo +define void @test4() nounwind { +entry: + call void(...) @foo() + ret void +} + +@ptr6 = internal global i32* null +@dst6 = internal global i32 0 +@src6 = internal global i32 0 + +; x00-LABEL: test5: +; x00 lea (dst6,%pc), %a0 +; x00 move.l %a0, (ptr6,%pc) +; x00 move.l (src6,%pc), (%a0) +define void @test5() nounwind { +entry: + store i32* @dst6, i32** @ptr6 + %tmp.s = load i32, i32* @src6 + store i32 %tmp.s, i32* @dst6 + ret void +} + + +;;; Test constant pool references. +;; FIXME no constant pool atm +;; 00: .LCPI6_0: +;; 00-LABEL: test6: +;; 00: calll .L6$pb +;; 00: .L6$pb: +;; 00: addl $_GLOBAL_OFFSET_TABLE_+(.L{{.*}}-.L6$pb), +;; 00: fldl .LCPI6_0@GOTOFF( +;define double @test6(i32 %a.u) nounwind { +;entry: +; %tmp = icmp eq i32 %a.u,0 +; %retval = select i1 %tmp, double 4.561230e+02, double 1.234560e+02 +; ret double %retval +;} + + +; Test jump table references. +; +; x00-LABEL: test7: +; +; x00: move.l (4,%sp), %d0 +; x00-NEXT: add.l #-1, %d0 +; x00-NEXT: move.l %d0, %d1 +; x00-NEXT: sub.l #12, %d1 +; x00-NEXT: bhi .LBB{{.*}}_14 +; x00: lsl.l #2, %d0 +; x00-NEXT: lea (.LJTI{{.*}}_0,%pc), %a0 +; x00-NEXT: move.l (0,%a0,%d0), %a0 +; x00-NEXT: jmp (%a0) +; +; x00: .LBB{{.*}}_2: +; x00-NEXT: bra foo1 +; x00: .LBB{{.*}}_8: +; x00-NEXT: bra foo1 +; x00: .LBB{{.*}}_14: +; x00-NEXT: bra foo6 +; x00: .LBB{{.*}}_9: +; x00-NEXT: bra foo2 +; x00: .LBB{{.*}}_10: +; x00-NEXT: bra foo6 +; x00: .LBB{{.*}}_12: +; x00-NEXT: bra foo4 +; x00: .LBB{{.*}}_3: +; x00-NEXT: bra foo2 +; x00: .LBB{{.*}}_5: +; x00-NEXT: bra foo3 +; x00: .LBB{{.*}}_6: +; x00-NEXT: bra foo4 +; x00: .LBB{{.*}}_11: +; x00-NEXT: bra foo3 +; x00: .LBB{{.*}}_4: +; x00-NEXT: bra foo6 +; x00: .LBB{{.*}}_7: +; x00-NEXT: bra foo5 +; x00: .LBB{{.*}}_13: +; x00-NEXT: bra foo5 +; +; x00: .p2align 2 +; x00: .long .LBB{{.*}}_2 +; x00: .long .LBB{{.*}}_8 +; x00: .long .LBB{{.*}}_14 +; x00: .long .LBB{{.*}}_9 +; x00: .long .LBB{{.*}}_10 +; x00: .long .LBB{{.*}}_12 +; x00: .long .LBB{{.*}}_3 +; x00: .long .LBB{{.*}}_5 +; x00: .long .LBB{{.*}}_6 +; x00: .long .LBB{{.*}}_11 +; x00: .long .LBB{{.*}}_4 +; x00: .long .LBB{{.*}}_7 +; x00: .long .LBB{{.*}}_13 +define void @test7(i32 %n.u) nounwind { +entry: + switch i32 %n.u, label %bb12 [i32 1, label %bb i32 2, label %bb6 i32 4, label %bb7 i32 5, label %bb8 i32 6, label %bb10 i32 7, label %bb1 i32 8, label %bb3 i32 9, label %bb4 i32 10, label %bb9 i32 11, label %bb2 i32 12, label %bb5 i32 13, label %bb11 ] +bb: + tail call void(...) @foo1() + ret void +bb1: + tail call void(...) @foo2() + ret void +bb2: + tail call void(...) @foo6() + ret void +bb3: + tail call void(...) @foo3() + ret void +bb4: + tail call void(...) @foo4() + ret void +bb5: + tail call void(...) @foo5() + ret void +bb6: + tail call void(...) @foo1() + ret void +bb7: + tail call void(...) @foo2() + ret void +bb8: + tail call void(...) @foo6() + ret void +bb9: + tail call void(...) @foo3() + ret void +bb10: + tail call void(...) @foo4() + ret void +bb11: + tail call void(...) @foo5() + ret void +bb12: + tail call void(...) @foo6() + ret void +} + +declare void @foo1(...) +declare void @foo2(...) +declare void @foo6(...) +declare void @foo3(...) +declare void @foo4(...) +declare void @foo5(...) Index: test/CodeGen/M680x0/ASM/Control/cmp.ll =================================================================== --- /dev/null +++ test/CodeGen/M680x0/ASM/Control/cmp.ll @@ -0,0 +1,210 @@ +; RUN: llc < %s -mtriple=m680x0-linux-gnu -verify-machineinstrs | FileCheck %s --check-prefix=x00 + +; x00-LABEL: test1: +; x00: cmpi.l #0, (%a0) +; x00: beq +define i32 @test1(i32* %y) nounwind { + %tmp = load i32, i32* %y ; [#uses=1] + %tmp.upgrd.1 = icmp eq i32 %tmp, 0 ; [#uses=1] + br i1 %tmp.upgrd.1, label %cond_true, label %cond_false + +cond_false: ; preds = %0 + ret i32 0 + +cond_true: ; preds = %0 + ret i32 1 +} + +; x00-LABEL: test2: +; x00: and.l #536870911 +; TODO There is no need for cmpi.l, and.l sets Z as well +; x00: cmpi.l #0 +; x00: beq +define i32 @test2(i32* %y) nounwind { + %tmp = load i32, i32* %y ; [#uses=1] + %tmp1 = shl i32 %tmp, 3 ; [#uses=1] + %tmp1.upgrd.2 = icmp eq i32 %tmp1, 0 ; [#uses=1] + br i1 %tmp1.upgrd.2, label %cond_true, label %cond_false + +cond_false: ; preds = %0 + ret i32 0 + +cond_true: ; preds = %0 + ret i32 1 +} + +; x00-LABEL: test2b: +; x00: and.b #31 +; x00: cmpi.b #0 +; x00: beq +define i8 @test2b(i8* %y) nounwind { + %tmp = load i8, i8* %y ; [#uses=1] + %tmp1 = shl i8 %tmp, 3 ; [#uses=1] + %tmp1.upgrd.2 = icmp eq i8 %tmp1, 0 ; [#uses=1] + br i1 %tmp1.upgrd.2, label %cond_true, label %cond_false + +cond_false: ; preds = %0 + ret i8 0 + +cond_true: ; preds = %0 + ret i8 1 +} + +; x00-LABEL: test3: +; x00: or.l +; x00-NEXT: seq %d0 +; x00-NEXT: move.l %d0, %d1 +; x00-NEXT: and.l #255, %d1 +; x00-NEXT: move.l #0, %d0 +define i64 @test3(i64 %x) nounwind { + %t = icmp eq i64 %x, 0 + %r = zext i1 %t to i64 + ret i64 %r +} + +; x00-LABEL: test4: +; x00: sub.l #1 +; x00: subx.l +; x00: slt +define i64 @test4(i64 %x) nounwind { + %t = icmp slt i64 %x, 1 + %r = zext i1 %t to i64 + ret i64 %r +} + +; x00-LABEL: test6: +; x00: move.l (12,%sp), %d0 +; x00: or.l (8,%sp), %d0 +; x00: bne +; x00: bra +define i32 @test6() nounwind align 2 { + %A = alloca {i64, i64}, align 8 + %B = getelementptr inbounds {i64, i64}, {i64, i64}* %A, i64 0, i32 1 + %C = load i64, i64* %B + %D = icmp eq i64 %C, 0 + br i1 %D, label %T, label %F +T: + ret i32 1 + +F: + ret i32 0 +} + +; x00-LABEL: test7: +; x00: cmpi.l #0, (4,%sp) +; x00: seq +define i32 @test7(i64 %res) nounwind { +entry: + %lnot = icmp ult i64 %res, 4294967296 + %lnot.ext = zext i1 %lnot to i32 + ret i32 %lnot.ext +} + +; x00-LABEL: test8: +; x00: move.l (4,%sp), %d0 +; x00: sub.l #3, %d0 +; x00: scs %d0 +define i32 @test8(i64 %res) nounwind { +entry: + %lnot = icmp ult i64 %res, 12884901888 + %lnot.ext = zext i1 %lnot to i32 + ret i32 %lnot.ext +} + +; x00-LABEL: test11: +; x00: move.l (4,%sp), %d0 +; x00: and.l #-32768, %d0 +; x00: eori.l #32768, %d0 +; x00: seq +define i32 @test11(i64 %l) nounwind { +entry: + %shr.mask = and i64 %l, -140737488355328 + %cmp = icmp eq i64 %shr.mask, 140737488355328 + %conv = zext i1 %cmp to i32 + ret i32 %conv +} + +; x00-LABEL: test13: +; x00: move.b (7,%sp) +; x00: and.b #8 +; x00: cmpi.b #0 +define i32 @test13(i32 %mask, i32 %base, i32 %intra) { + %and = and i32 %mask, 8 + %tobool = icmp ne i32 %and, 0 + %cond = select i1 %tobool, i32 %intra, i32 %base + ret i32 %cond +} + +; x00-LABEL: test14: +; x00: move.l +; x00: lsr.l #7 +; x00: cmpi.l #0 +; x00: bpl +define i32 @test14(i32 %mask, i32 %base, i32 %intra) #0 { + %s = lshr i32 %mask, 7 + %tobool = icmp sgt i32 %s, -1 + %cond = select i1 %tobool, i32 %intra, i32 %base + ret i32 %cond +} + +; x00-LABEL: test15: +; x00: scc +; x00: seq +; x00: or.b +define zeroext i1 @test15(i32 %bf.load, i32 %n) { + %bf.lshr = lshr i32 %bf.load, 16 + %cmp2 = icmp eq i32 %bf.lshr, 0 + %cmp5 = icmp uge i32 %bf.lshr, %n + %.cmp5 = or i1 %cmp2, %cmp5 + ret i1 %.cmp5 +} + +; x00-LABEL: test16: +; x00: move.w #15 +; x00: move.w (6,%sp), %d0 +; x00: lsr.w +; x00: eori.b #1 +define i8 @test16(i16 signext %L) { + %lshr = lshr i16 %L, 15 + %trunc = trunc i16 %lshr to i8 + %not = xor i8 %trunc, 1 + ret i8 %not +} + +; x00-LABEL: test18: +; x00: move.l #31 +; x00: move.l +; x00: lsr.l +; x00: eori.b #1 +define i8 @test18(i64 %L) { + %lshr = lshr i64 %L, 63 + %trunc = trunc i64 %lshr to i8 + %not = xor i8 %trunc, 1 + ret i8 %not +} + +@d = global i8 0, align 1 + + +; x00-LABEL: test20 +; x00: and.l +; x00: sne +; x00: add.l +; x00: sne +; x00: cmpi.l +; x00: sne +define void @test20(i32 %bf.load, i8 %x1, i8* %b_addr) { + %bf.shl = shl i32 %bf.load, 8 + %bf.ashr = ashr exact i32 %bf.shl, 8 + %tobool4 = icmp ne i32 %bf.ashr, 0 + %conv = zext i1 %tobool4 to i32 + %conv6 = zext i8 %x1 to i32 + %add = add nuw nsw i32 %conv, %conv6 + %tobool7 = icmp ne i32 %add, 0 + %frombool = zext i1 %tobool7 to i8 + store i8 %frombool, i8* %b_addr, align 1 + %tobool14 = icmp ne i32 %bf.shl, 0 + %frombool15 = zext i1 %tobool14 to i8 + store i8 %frombool15, i8* @d, align 1 + ret void +} Index: test/CodeGen/M680x0/ASM/Control/long-setcc.ll =================================================================== --- /dev/null +++ test/CodeGen/M680x0/ASM/Control/long-setcc.ll @@ -0,0 +1,32 @@ +; RUN: llc < %s -mtriple=m680x0-linux -verify-machineinstrs | FileCheck %s -check-prefix=x00 + +; x00: t1 +; x00: lsr.l +; x00-NOT: lsr.l +; x00: rts + +define i1 @t1(i64 %x) nounwind { + %B = icmp slt i64 %x, 0 + ret i1 %B +} + + +; x00: t2 +; x00: cmpi.l +; x00-NEXT: seq +; x00-NEXT: rts + +define i1 @t2(i64 %x) nounwind { + %tmp = icmp ult i64 %x, 4294967296 + ret i1 %tmp +} + + +; x00: t3 +; x00: move.b #0, %d0 +; x00: rts + +define i1 @t3(i32 %x) nounwind { + %tmp = icmp ugt i32 %x, -1 + ret i1 %tmp +} Index: test/CodeGen/M680x0/ASM/Control/setcc.ll =================================================================== --- /dev/null +++ test/CodeGen/M680x0/ASM/Control/setcc.ll @@ -0,0 +1,91 @@ +; RUN: llc < %s -mtriple=m680x0-linux -verify-machineinstrs | FileCheck %s -check-prefix=x00 + +;; TODO All these can be improved + +; x00-LABEL: t1: +; x00: move.w +; x00: and.l #65535, %d0 +; x00: sub.l #26, %d0 +; x00: shi %d0 +; x00: and.l #255, %d0 +; x00: lsl.l #5, %d0 + +define zeroext i16 @t1(i16 zeroext %x) nounwind readnone ssp { +entry: + %0 = icmp ugt i16 %x, 26 ; [#uses=1] + %iftmp.1.0 = select i1 %0, i16 32, i16 0 ; [#uses=1] + ret i16 %iftmp.1.0 +} + + +; x00-LABEL: t2: +; x00: move.w +; x00: and.l #65535, %d0 +; x00: sub.l #26, %d0 +; x00: scs %d0 +; x00: and.l #255, %d0 +; x00: lsl.l #5, %d0 + +define zeroext i16 @t2(i16 zeroext %x) nounwind readnone ssp { +entry: + %0 = icmp ult i16 %x, 26 ; [#uses=1] + %iftmp.0.0 = select i1 %0, i16 32, i16 0 ; [#uses=1] + ret i16 %iftmp.0.0 +} + +; x00-LABEL: t3: +; x00: move.l #0, %d2 +; x00: sub.l #18, %d1 +; x00: subx.l %d2, %d0 +; x00: scs +; x00: and.l +; x00: lsl.l + +define fastcc i64 @t3(i64 %x) nounwind readnone ssp { +entry: + %0 = icmp ult i64 %x, 18 ; [#uses=1] + %iftmp.2.0 = select i1 %0, i64 64, i64 0 ; [#uses=1] + ret i64 %iftmp.2.0 +} + + +; x00-LABEL: t5: +; x00: move.l #31 +; x00: move.l (4 +; x00: lsr.l +; x00: eori.b +; +; Should be: +; cmp.l +; smi +; since we are intereseted in sign bit only + +define i8 @t5(i32 %a) { +entry: + %.lobit = lshr i32 %a, 31 + %trunc = trunc i32 %.lobit to i8 + %.not = xor i8 %trunc, 1 + ret i8 %.not +} + + +; x00-LABEL: t6: +; x00: move.l #31 +; x00: move.l (4 +; x00: lsr.l +; x00: eori.b +; x00: and.l +; +; Should be: +; cmp.l +; smi +; since we are intereseted in sign bit only +; and.l in the end is superfluous + +define zeroext i1 @t6(i32 %a) { +entry: + %.lobit = lshr i32 %a, 31 + %trunc = trunc i32 %.lobit to i1 + %.not = xor i1 %trunc, 1 + ret i1 %.not +} Index: test/CodeGen/M680x0/ASM/Std/varargs.ll =================================================================== --- /dev/null +++ test/CodeGen/M680x0/ASM/Std/varargs.ll @@ -0,0 +1,31 @@ +; RUN: llc < %s -mtriple=m680x0-linux -verify-machineinstrs | FileCheck %s -check-prefix=x00 + +%struct.va_list = type { i8* } + +; x00-LABEL test: +; x00: lea (16,%sp), %a +; x00: add.l #4, %a0 +; x00: move.l %a0, (4,%sp) +define i32 @test(i32 %X, ...) { + ; Initialize variable argument processing + %ap = alloca %struct.va_list + %ap2 = bitcast %struct.va_list* %ap to i8* + call void @llvm.va_start(i8* %ap2) + + ; Read a single integer argument + %tmp = va_arg i8* %ap2, i32 + + ; Demonstrate usage of llvm.va_copy and llvm.va_end + %aq = alloca i8* + %aq2 = bitcast i8** %aq to i8* + call void @llvm.va_copy(i8* %aq2, i8* %ap2) + call void @llvm.va_end(i8* %aq2) + + ; Stop processing of arguments. + call void @llvm.va_end(i8* %ap2) + ret i32 %tmp +} + +declare void @llvm.va_start(i8*) +declare void @llvm.va_copy(i8*, i8*) +declare void @llvm.va_end(i8*) Index: test/CodeGen/M680x0/OBJ/Arith/Classes/MxBiArOp_FMI.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Arith/Classes/MxBiArOp_FMI.mir @@ -0,0 +1,88 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj -o - \ +# RUN: | extract-section .text \ +# RUN: | FileCheck %s -check-prefixes=ADD8fi,ADD32fi,ADD8pi,ADD32pi,ADD8ji,ADD32ji + +#------------------------------------------------------------------------------ +# MxBiArOp_FMI class used for binary arithmetic operations and operates on +# memory and immediate data. It uses MxArithImmEncoding class. +#------------------------------------------------------------------------------ + + +--- # ARII +# -------------------------------+-------+-----------+----------- +# F E D C B A 9 8 | 7 6 | 5 4 3 | 2 1 0 +# -------------------------------+-------+-----------+----------- +# OPWORD x x x x x x x x | SIZE | MODE | REG +# -------------------------------+-------+-----------+----------- +# ADD8fi: 0 0 0 0 0 1 1 0 . 0 0 1 1 0 0 0 0 +# ADD8fi-SAME: 0 0 0 0 0 0 0 0 . 1 1 1 1 1 1 1 1 +# ADD8fi-SAME: 0 0 0 0 1 0 0 0 . 0 0 0 0 0 0 0 0 +# -------------------------------+------------------------------- +# ADD8fi-SAME: 0 0 0 0 0 1 1 0 . 0 0 1 1 0 0 1 1 +# ADD8fi-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# ADD8fi-SAME: 1 0 0 1 1 0 0 0 . 1 1 1 1 1 1 1 1 +# -------------------------------+------------------------------- +# ADD32fi-SAME: 0 0 0 0 0 1 1 0 . 1 0 1 1 0 0 1 0 +# ADD32fi-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# ADD32fi-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# ADD32fi-SAME: 0 0 0 1 1 0 0 0 . 0 0 0 0 1 1 0 1 +# ---+-----------+---+-------+---+------------------------------- +# BRIEF DA | REG | L | SCALE | 0 | DISPLACEMENT +# ---+-----------+---+-------+---+------------------------------- +name: MxBiArOp_FMI_ARII +body: | + bb.0: + ADD8fi 0, $a0, $d0, -1, implicit-def $ccr + ADD8fi -1, $a3, $a1, 0, implicit-def $ccr + ADD32fi 13, $a2, $d1, -1, implicit-def $ccr + +... +--- # ARID +# -------------------------------+-------+-----------+----------- +# F E D C B A 9 8 | 7 6 | 5 4 3 | 2 1 0 +# -------------------------------+-------+-----------+----------- +# OPWORD x x x x x x x x | SIZE | MODE | REG +# -------------------------------+-------+-----------+----------- +# ADD8pi-SAME: 0 0 0 0 0 1 1 0 . 0 0 1 0 1 0 0 0 +# ADD8pi-SAME: 0 0 0 0 0 0 0 0 . 1 1 1 1 1 1 1 1 +# ADD8pi-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# -------------------------------+------------------------------- +# ADD8pi-SAME: 0 0 0 0 0 1 1 0 . 0 0 1 0 1 0 1 1 +# ADD8pi-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# ADD8pi-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# -------------------------------+------------------------------- +# ADD32pi-SAME: 0 0 0 0 0 1 1 0 . 1 0 1 0 1 0 1 0 +# ADD32pi-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# ADD32pi-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# ADD32pi-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 1 1 0 1 +name: MxBiArOp_FMI_ARID +body: | + bb.0: + ADD8pi 0, $a0, -1, implicit-def $ccr + ADD8pi -1, $a3, 0, implicit-def $ccr + ADD32pi 13, $a2, -1, implicit-def $ccr + +... +--- # ARI +# -------------------------------+-------+-----------+----------- +# F E D C B A 9 8 | 7 6 | 5 4 3 | 2 1 0 +# -------------------------------+-------+-----------+----------- +# OPWORD x x x x x x x x | SIZE | MODE | REG +# -------------------------------+-------+-----------+----------- +# ADD8ji-SAME: 0 0 0 0 0 1 1 0 . 0 0 0 1 0 0 0 0 +# ADD8ji-SAME: 0 0 0 0 0 0 0 0 . 1 1 1 1 1 1 1 1 +# -------------------------------+------------------------------- +# ADD8ji-SAME: 0 0 0 0 0 1 1 0 . 0 0 0 1 0 0 1 1 +# ADD8ji-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# -------------------------------+------------------------------- +# ADD32ji-SAME: 0 0 0 0 0 1 1 0 . 1 0 0 1 0 0 1 0 +# ADD32ji-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# ADD32ji-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +name: MxBiArOp_FMI_ARI +body: | + bb.0: + ADD8ji $a0, -1, implicit-def $ccr + ADD8ji $a3, 0, implicit-def $ccr + ADD32ji $a2, -1, implicit-def $ccr + +... Index: test/CodeGen/M680x0/OBJ/Arith/Classes/MxBiArOp_FMR.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Arith/Classes/MxBiArOp_FMR.mir @@ -0,0 +1,73 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj -o - \ +# RUN: | extract-section .text \ +# RUN: | FileCheck %s -check-prefixes=ADD8fd,ADD32fd,ADD8pd,ADD32pd,ADD8jd,ADD32jd + +#------------------------------------------------------------------------------ +# MxBiArOp_FMR class used for binary arithmetic operations and operates on +# register and memory; the result is store to memory. It uses MxArithEncoding +# encoding class and MxOpModeEAd opmode class. +#------------------------------------------------------------------------------ + +--- # ARII +# ---------------+-----------+-----------+-----------+----------- +# F E D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# ---------------+-----------+-----------+-----------+----------- +# OPWORD x x x x | REG | OPMODE | MODE | REG +# ---------------+-----------+-----------+-----------+----------- +# ADD8fd: 1 1 0 1 0 0 0 1 . 0 0 1 1 0 0 0 0 +# ADD8fd-SAME: 0 0 0 1 1 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# ADD8fd-SAME: 1 1 0 1 0 0 0 1 . 0 0 1 1 0 0 0 0 +# ADD8fd-SAME: 0 0 0 1 1 0 0 0 . 1 1 1 1 1 1 1 1 +# --------------------------------------------------------------- +# ADD32fd-SAME: 1 1 0 1 0 0 0 1 . 1 0 1 1 0 0 0 1 +# ADD32fd-SAME: 0 0 0 1 1 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# ADD32fd-SAME: 1 1 0 1 0 0 1 1 . 1 0 1 1 0 0 1 0 +# ADD32fd-SAME: 1 0 1 0 1 0 0 0 . 0 0 0 0 0 0 0 0 +# ---+-----------+---+-------+---+------------------------------- +# BRIEF DA | REG | L | SCALE | 0 | DISPLACEMENT +# ---+-----------+---+-------+---+------------------------------- +name: MxBiArOp_FMR_ARII +body: | + bb.0: + ADD8fd 0, $a0, $d1, $bd0, implicit-def $ccr + ADD8fd -1, $a0, $d1, $bd0, implicit-def $ccr + ADD32fd 0, $a1, $d1, $d0, implicit-def $ccr + ADD32fd 0, $a2, $a2, $d1, implicit-def $ccr + +... +--- # ARID +# ---------------+-----------+-----------+-----------+----------- +# F E D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# ---------------+-----------+-----------+-----------+----------- +# OPWORD x x x x | REG | OPMODE | MODE | REG +# ---------------+-----------+-----------+-----------+----------- +# ADD8pd-SAME: 1 1 0 1 0 0 0 1 . 0 0 1 0 1 0 0 0 +# ADD8pd-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# ADD32pd-SAME: 1 1 0 1 0 0 0 1 . 1 0 1 0 1 0 0 1 +# ADD32pd-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +name: MxBiArOp_FMR_ARID +body: | + bb.0: + ADD8pd 0, $a0, $bd0, implicit-def $ccr + ADD32pd -1, $a1, $d0, implicit-def $ccr + +... +--- # ARI +# ---------------+-----------+-----------+-----------+----------- +# F E D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# ---------------+-----------+-----------+-----------+----------- +# OPWORD x x x x | REG | OPMODE | MODE | REG +# ---------------+-----------+-----------+-----------+----------- +# ADD8jd-SAME: 1 1 0 1 0 0 0 1 . 0 0 0 1 0 0 0 0 +# --------------------------------------------------------------- +# ADD32jd-SAME: 1 1 0 1 0 1 1 1 . 1 0 0 1 0 0 0 1 +name: MxBiArOp_FMR_ARI +body: | + bb.0: + ADD8jd $a0, $bd0, implicit-def $ccr + ADD32jd $a1, $d3, implicit-def $ccr + +... Index: test/CodeGen/M680x0/OBJ/Arith/Classes/MxBiArOp_RFRI.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Arith/Classes/MxBiArOp_RFRI.mir @@ -0,0 +1,41 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj -o - \ +# RUN: | extract-section .text \ +# RUN: | FileCheck %s -check-prefixes=XOR16di,XOR32di + +#------------------------------------------------------------------------------ +# MxBiArOp_RFRI class used for binary arithmetic operations and operates on +# register and immediate data. It uses MxArithImmEncoding class. This is special +# case for XOR. +#------------------------------------------------------------------------------ + + +# -------------------------------+-------+-----------+----------- +# F E D C B A 9 8 | 7 6 | 5 4 3 | 2 1 0 +# -------------------------------+-------+-----------+----------- +# x x x x x x x x | SIZE | MODE | REG +# -------------------------------+-------+-----------+----------- +# XOR16di: 0 0 0 0 1 0 1 0 . 0 1 0 0 0 0 0 0 +# XOR16di-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# -------------------------------+------------------------------- +# XOR16di-SAME: 0 0 0 0 1 0 1 0 . 0 1 0 0 0 0 1 1 +# XOR16di-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# -------------------------------+------------------------------- +# XOR32di-SAME: 0 0 0 0 1 0 1 0 . 1 0 0 0 0 0 0 0 +# XOR32di-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# XOR32di-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# -------------------------------+------------------------------- +# XOR32di-SAME: 0 0 0 0 1 0 1 0 . 1 0 0 0 0 0 0 0 +# XOR32di-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 1 +# XOR32di-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# -------------------------------+------------------------------- +# XOR32di-SAME: 0 0 0 0 1 0 1 0 . 1 0 0 0 0 1 1 1 +# XOR32di-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 1 1 1 +# XOR32di-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +name: MxBiArOp_RFRI +body: | + bb.0: + $wd0 = XOR16di $wd0, 0, implicit-def $ccr + $wd3 = XOR16di $wd3, -1, implicit-def $ccr + $d0 = XOR32di $d0, -1, implicit-def $ccr + $d0 = XOR32di $d0, 131071, implicit-def $ccr + $d7 = XOR32di $d7, 458752, implicit-def $ccr Index: test/CodeGen/M680x0/OBJ/Arith/Classes/MxBiArOp_RFRI_xEA.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Arith/Classes/MxBiArOp_RFRI_xEA.mir @@ -0,0 +1,45 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj -o - \ +# RUN: | extract-section .text \ +# RUN: | FileCheck %s -check-prefixes=ADD16di,ADD32ri + +#------------------------------------------------------------------------------ +# MxBiArOp_RFRI_xEA class used for binary arithmetic operations and operates on +# register and immediate data. It uses MxArithEncoding(yes for immediates) class +# and either MxOpModeNdEA or MxOpmodeNrEA opmode classes. +#------------------------------------------------------------------------------ + + +# ---------------+-----------+-----------+-----------+----------- +# F E D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# ---------------+-----------+-----------+-----------+----------- +# x x x x | REG | OPMODE | MODE | REG +# ADD16di: 1 1 0 1 0 0 0 0 . 0 1 1 1 1 1 0 0 +# ADD16di: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# ---------------+-----------+-----------+-----------+----------- +# ADD16di-SAME: 1 1 0 1 0 1 1 0 . 0 1 1 1 1 1 0 0 +# ADD16di-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# ---------------+-----------+-----------+-----------+----------- +# ADD32ri-SAME: 1 1 0 1 0 0 0 0 . 1 0 1 1 1 1 0 0 +# ADD32ri-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# ADD32ri-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# ---------------+-----------+-----------+-----------+----------- +# ADD32ri-SAME: 1 1 0 1 0 0 0 0 . 1 0 1 1 1 1 0 0 +# ADD32ri-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 1 +# ADD32ri-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# ---------------+-----------+-----------+-----------+----------- +# ADD32ri-SAME: 1 1 0 1 1 1 1 0 . 1 0 1 1 1 1 0 0 +# ADD32ri-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 1 1 1 +# ADD32ri-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# ---------------+-----------+-----------+-----------+----------- +# ADD32ri-SAME: 1 1 0 1 0 0 0 1 . 1 1 1 1 1 1 0 0 +# ADD32ri-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# ADD32ri-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +name: MxBiArOp_RFRI_xEA +body: | + bb.0: + $wd0 = ADD16di $wd0, 0, implicit-def $ccr + $wd3 = ADD16di $wd3, -1, implicit-def $ccr + $d0 = ADD32ri $d0, -1, implicit-def $ccr + $d0 = ADD32ri $d0, 131071, implicit-def $ccr + $d7 = ADD32ri $d7, 458752, implicit-def $ccr + $a0 = ADD32ri $a0, 0, implicit-def $ccr Index: test/CodeGen/M680x0/OBJ/Arith/Classes/MxBiArOp_RFRM.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Arith/Classes/MxBiArOp_RFRM.mir @@ -0,0 +1,121 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj -o - \ +# RUN: | extract-section .text \ +# RUN: | FileCheck %s -check-prefixes=ADD8dk,ADD32rk,ADD8dq,ADD32rq,\ +# RUN:ADD8df,ADD32rf,ADD8dp,ADD32rp,ADD8dj,ADD32rj + +#------------------------------------------------------------------------------ +# MxBiArOp_RFRM class used for binary arithmetic operations and operates on +# register and memory. It uses MxArithEncoding encoding class and either +# MxOpModeNdEA or MxOpModeNrEA opmode classes. +#------------------------------------------------------------------------------ + +--- # PCI +# ---------------+-----------+-----------+-----------+----------- +# F E D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# ---------------+-----------+-----------+-----------+----------- +# OPWORD x x x x | REG | OPMODE | MODE | REG +# ---------------+-----------+-----------+-----------+----------- +# ADD8dk: 1 1 0 1 0 0 0 0 . 0 0 1 1 1 0 1 1 +# ADD8dk-SAME: 0 0 0 1 1 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# ADD8dk-SAME: 1 1 0 1 0 0 0 0 . 0 0 1 1 1 0 1 1 +# ADD8dk-SAME: 0 0 0 1 1 0 0 0 . 1 1 1 1 1 1 1 1 +# --------------------------------------------------------------- +# ADD32rk-SAME: 1 1 0 1 0 0 0 0 . 1 0 1 1 1 0 1 1 +# ADD32rk-SAME: 0 0 0 1 1 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# ADD32rk-SAME: 1 1 0 1 0 0 1 1 . 1 1 1 1 1 0 1 1 +# ADD32rk-SAME: 1 0 1 0 1 0 0 0 . 0 0 0 0 0 0 0 0 +# ---+-----------+---+-------+---+------------------------------- +# BRIEF DA | REG | L | SCALE | 0 | DISPLACEMENT +# ---+-----------+---+-------+---+------------------------------- +name: MxBiArOp_RFRM_PCI +body: | + bb.0: + $bd0 = ADD8dk $bd0, 0, $d1, implicit-def $ccr + $bd0 = ADD8dk $bd0, -1, $d1, implicit-def $ccr + $d0 = ADD32rk $d0, 0, $d1, implicit-def $ccr + $a1 = ADD32rk $a1, 0, $a2, implicit-def $ccr + +... +--- # PCD +# ---------------+-----------+-----------+-----------+----------- +# F E D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# ---------------+-----------+-----------+-----------+----------- +# OPWORD x x x x | REG | OPMODE | MODE | REG +# ---------------+-----------+-----------+-----------+----------- +# ADD8dq-SAME: 1 1 0 1 0 0 0 0 . 0 0 1 1 1 0 1 0 +# ADD8dq-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# ADD32rq-SAME: 1 1 0 1 0 0 0 0 . 1 0 1 1 1 0 1 0 +# ADD32rq-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +name: MxBiArOp_RFRM_PCD +body: | + bb.0: + $bd0 = ADD8dq $bd0, 0, implicit-def $ccr + $d0 = ADD32rq $d0, -1, implicit-def $ccr + +... +--- # ARII +# ---------------+-----------+-----------+-----------+----------- +# F E D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# ---------------+-----------+-----------+-----------+----------- +# OPWORD x x x x | REG | OPMODE | MODE | REG +# ---------------+-----------+-----------+-----------+----------- +# ADD8df-SAME: 1 1 0 1 0 0 0 0 . 0 0 1 1 0 0 0 0 +# ADD8df-SAME: 0 0 0 1 1 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# ADD8df-SAME: 1 1 0 1 0 0 0 0 . 0 0 1 1 0 0 0 0 +# ADD8df-SAME: 0 0 0 1 1 0 0 0 . 1 1 1 1 1 1 1 1 +# --------------------------------------------------------------- +# ADD32rf-SAME: 1 1 0 1 0 0 0 0 . 1 0 1 1 0 0 0 1 +# ADD32rf-SAME: 0 0 0 1 1 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# ADD32rf-SAME: 1 1 0 1 0 0 1 1 . 1 1 1 1 0 0 1 0 +# ADD32rf-SAME: 1 0 1 0 1 0 0 0 . 0 0 0 0 0 0 0 0 +# ---+-----------+---+-------+---+------------------------------- +# BRIEF DA | REG | L | SCALE | 0 | DISPLACEMENT +# ---+-----------+---+-------+---+------------------------------- +name: MxBiArOp_RFRM_ARII +body: | + bb.0: + $bd0 = ADD8df $bd0, 0, $a0, $d1, implicit-def $ccr + $bd0 = ADD8df $bd0, -1, $a0, $d1, implicit-def $ccr + $d0 = ADD32rf $d0, 0, $a1, $d1, implicit-def $ccr + $a1 = ADD32rf $a1, 0, $a2, $a2, implicit-def $ccr + +... +--- # ARID +# ---------------+-----------+-----------+-----------+----------- +# F E D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# ---------------+-----------+-----------+-----------+----------- +# OPWORD x x x x | REG | OPMODE | MODE | REG +# ---------------+-----------+-----------+-----------+----------- +# ADD8dp-SAME: 1 1 0 1 0 0 0 0 . 0 0 1 0 1 0 0 0 +# ADD8dp-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# ADD32rp-SAME: 1 1 0 1 0 0 0 0 . 1 0 1 0 1 0 0 1 +# ADD32rp-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +name: MxBiArOp_RFRM_ARID +body: | + bb.0: + $bd0 = ADD8dp $bd0, 0, $a0, implicit-def $ccr + $d0 = ADD32rp $d0, -1, $a1, implicit-def $ccr + +... +--- # ARI +# ---------------+-----------+-----------+-----------+----------- +# F E D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# ---------------+-----------+-----------+-----------+----------- +# OPWORD x x x x | REG | OPMODE | MODE | REG +# ---------------+-----------+-----------+-----------+----------- +# ADD8dj-SAME: 1 1 0 1 0 0 0 0 . 0 0 0 1 0 0 0 0 +# --------------------------------------------------------------- +# ADD32rj-SAME: 1 1 0 1 0 1 1 1 . 1 1 0 1 0 0 0 1 +name: MxBiArOp_RFRM_ARI +body: | + bb.0: + $bd0 = ADD8dj $bd0, $a0, implicit-def $ccr + $a3 = ADD32rj $a3, $a1, implicit-def $ccr + +... Index: test/CodeGen/M680x0/OBJ/Arith/Classes/MxBiArOp_RFRRF.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Arith/Classes/MxBiArOp_RFRRF.mir @@ -0,0 +1,27 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj -o - \ +# RUN: | extract-section .text \ +# RUN: | FileCheck %s -check-prefixes=ADDX16dd,ADDX32dd + +#------------------------------------------------------------------------------ +# MxBiArOp_RFRRF class used for carry-aware binary arithmetic operations and +# operates on both data and address registers only. It uses MxArithXEncoding +# encoding class and either MxOpModeNdEA or MxOpModeNrEA opmode classes. +#------------------------------------------------------------------------------ + + +# --------------------------------------------------------------- +# F E D C | B A 9 | 8 | 7 6 | 5 4 | 3 | 2 1 0 +# ---------------+-----------+---+-------+-------+---+----------- +# x x x x | REG Rx | 1 | SIZE | 0 0 | M | REG Ry +# ---------------+-----------+---+-------+-------+---+----------- +# ADDX16dd: 1 1 0 1 0 0 0 1 . 0 1 0 0 0 0 0 1 +# ADDX16dd-SAME: 1 1 0 1 0 1 1 1 . 0 1 0 0 0 0 1 0 +# ADDX32dd-SAME: 1 1 0 1 0 0 0 1 . 1 0 0 0 0 0 0 1 +# ADDX32dd-SAME: 1 1 0 1 1 1 1 1 . 1 0 0 0 0 0 0 1 +name: MxBiArOp_RFRRF +body: | + bb.0: + $wd0 = ADDX16dd $wd0, $wd1, implicit $ccr, implicit-def $ccr + $wd3 = ADDX16dd $wd3, $wd2, implicit $ccr, implicit-def $ccr + $d0 = ADDX32dd $d0, $d1, implicit $ccr, implicit-def $ccr + $d7 = ADDX32dd $d7, $d1, implicit $ccr, implicit-def $ccr Index: test/CodeGen/M680x0/OBJ/Arith/Classes/MxBiArOp_RFRR_EAd.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Arith/Classes/MxBiArOp_RFRR_EAd.mir @@ -0,0 +1,26 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj -o - \ +# RUN: | extract-section .text \ +# RUN: | FileCheck %s -check-prefixes=XOR16dd,XOR32dd + +#------------------------------------------------------------------------------ +# MxBiArOp_RFRR_EAd class used for binary arithmetic operations and operates on +# both data and address registers only. It uses MxArithEncoding encoding class +# and MxOpModeEAd opmode class. This is a special case for XOR(EOR). +#------------------------------------------------------------------------------ + + +# ---------------+-----------+-----------+-----------+----------- +# F E D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# ---------------+-----------+-----------+-----------+----------- +# x x x x | REG | OPMODE | MODE | REG +# XOR16dd: 1 0 1 1 0 0 1 1 . 0 1 0 0 0 0 0 0 +# XOR16dd-SAME: 1 0 1 1 0 1 0 1 . 0 1 0 0 0 0 1 1 +# XOR32dd-SAME: 1 0 1 1 0 0 1 1 . 1 0 0 0 0 0 0 0 +# XOR32dd-SAME: 1 0 1 1 0 0 1 1 . 1 0 0 0 0 1 1 1 +name: MxBiArOp_RFRR_EAd +body: | + bb.0: + $wd0 = XOR16dd $wd0, $wd1, implicit-def $ccr + $wd3 = XOR16dd $wd3, $wd2, implicit-def $ccr + $d0 = XOR32dd $d0, $d1, implicit-def $ccr + $d7 = XOR32dd $d7, $d1, implicit-def $ccr Index: test/CodeGen/M680x0/OBJ/Arith/Classes/MxBiArOp_RFRR_xEA.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Arith/Classes/MxBiArOp_RFRR_xEA.mir @@ -0,0 +1,30 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj -o - \ +# RUN: | extract-section .text \ +# RUN: | FileCheck %s -check-prefixes=ADD16dd,ADD32rr + +#------------------------------------------------------------------------------ +# MxBiArOp_RFRR_xEA class used for binary arithmetic operations and operates on +# both data and address registers only. It uses MxArithEncoding encoding class +# and either MxOpModeNdEA or MxOpmodeNrEA opmode classes. +#------------------------------------------------------------------------------ + + +# ---------------+-----------+-----------+-----------+----------- +# F E D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# ---------------+-----------+-----------+-----------+----------- +# x x x x | REG | OPMODE | MODE | REG +# ADD16dd: 1 1 0 1 0 0 0 0 . 0 1 0 0 0 0 0 1 +# ADD16dd-SAME: 1 1 0 1 0 1 1 0 . 0 1 0 0 0 0 1 0 +# ADD32rr-SAME: 1 1 0 1 0 0 0 0 . 1 0 0 0 0 0 0 1 +# ADD32rr-SAME: 1 1 0 1 0 0 0 0 . 1 0 0 0 1 0 0 1 +# ADD32rr-SAME: 1 1 0 1 1 1 1 0 . 1 0 0 0 1 0 0 1 +# ADD32rr-SAME: 1 1 0 1 0 0 0 1 . 1 1 0 0 0 0 0 1 +name: MxBiArOp_RFRR_xEA +body: | + bb.0: + $wd0 = ADD16dd $wd0, $wd1, implicit-def $ccr + $wd3 = ADD16dd $wd3, $wd2, implicit-def $ccr + $d0 = ADD32rr $d0, $d1, implicit-def $ccr + $d0 = ADD32rr $d0, $a1, implicit-def $ccr + $d7 = ADD32rr $d7, $a1, implicit-def $ccr + $a0 = ADD32rr $a0, $d1, implicit-def $ccr Index: test/CodeGen/M680x0/OBJ/Arith/Classes/MxCMP_BI.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Arith/Classes/MxCMP_BI.mir @@ -0,0 +1,44 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj -o - \ +# RUN: | extract-section .text \ +# RUN: | FileCheck %s -check-prefixes=CMP8bi,CMP32bi + +#------------------------------------------------------------------------------ +# MxCMP_BI class used for compare operations and operates on absolute memory +# locations and immediate data. It uses MxArithImmEncoding encoding class. +# NOTE: CMP is calculated by subtracting LHS(Imm) from RHS(ABS) +#------------------------------------------------------------------------------ + + +# -------------------------------+-------+-----------+----------- +# F E D C B A 9 8 | 7 6 | 5 4 3 | 2 1 0 +# -------------------------------+-------+-----------+----------- +# OPWORD 0 0 0 0 1 1 0 0 | SIZE | MODE | REG +# -------------------------------+-------+-----------+----------- +# CMP8bi: 0 0 0 0 1 1 0 0 . 0 0 1 1 1 0 0 1 +# CMP8bi-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# CMP8bi-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# CMP8bi-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# -------------------------------+-------+-----------+----------- +# CMP8bi-SAME: 0 0 0 0 1 1 0 0 . 0 0 1 1 1 0 0 1 +# CMP8bi-SAME: 0 0 0 0 0 0 0 0 . 1 1 1 1 1 1 1 1 +# CMP8bi-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# CMP8bi-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# -------------------------------+-------+-----------+----------- +# CMP32bi-SAME: 0 0 0 0 1 1 0 0 . 1 0 1 1 1 0 0 1 +# CMP32bi-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# CMP32bi-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# CMP32bi-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# CMP32bi-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# -------------------------------+-------+-----------+----------- +# CMP32bi-SAME: 0 0 0 0 1 1 0 0 . 1 0 1 1 1 0 0 1 +# CMP32bi-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# CMP32bi-SAME: 0 0 0 0 0 0 0 0 . 0 0 1 0 1 0 1 0 +# CMP32bi-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# CMP32bi-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +name: MxCMP_BI +body: | + bb.0: + CMP8bi 0, -1, implicit-def $ccr + CMP8bi -1, 0, implicit-def $ccr + CMP32bi -1, 0, implicit-def $ccr + CMP32bi 42, -1, implicit-def $ccr Index: test/CodeGen/M680x0/OBJ/Arith/Classes/MxCMP_MI.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Arith/Classes/MxCMP_MI.mir @@ -0,0 +1,172 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj -o - \ +# RUN: | extract-section .text \ +# RUN: | FileCheck %s -check-prefixes=CMP8ki,CMP32ki,CMP8qi,CMP32qi,CMP8fi,\ +# RUN:CMP32fi,CMP8pi,CMP32pi,CMP8ji,CMP32ji + +#------------------------------------------------------------------------------ +# MxCMP_MI class used for compare operations and operates on memory data and +# immediate data. It uses MxArithImmEncoding encoding class. +# NOTE: CMP is calculated by subtracting LHS(Imm) from RHS(Mem) +#------------------------------------------------------------------------------ + + +--- # PCI +# -------------------------------+-------+-----------+----------- +# F E D C B A 9 8 | 7 6 | 5 4 3 | 2 1 0 +# -------------------------------+-------+-----------+----------- +# OPWORD 0 0 0 0 1 1 0 0 | SIZE | MODE | REG +# -------------------------------+-------+-----------+----------- +# CMP8ki: 0 0 0 0 1 1 0 0 . 0 0 1 1 1 0 1 1 +# CMP8ki-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# CMP8ki-SAME: 0 0 0 1 1 0 0 0 . 1 1 1 1 1 1 1 1 +# -------------------------------+-------+-----------+----------- +# CMP8ki-SAME: 0 0 0 0 1 1 0 0 . 0 0 1 1 1 0 1 1 +# CMP8ki-SAME: 0 0 0 0 0 0 0 0 . 1 1 1 1 1 1 1 1 +# CMP8ki-SAME: 0 0 0 0 1 0 0 0 . 0 0 0 0 0 0 0 0 +# -------------------------------+-------+-----------+----------- +# CMP32ki-SAME: 0 0 0 0 1 1 0 0 . 1 0 1 1 1 0 1 1 +# CMP32ki-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# CMP32ki-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# CMP32ki-SAME: 0 1 1 1 1 0 0 0 . 0 0 0 0 0 0 0 0 +# -------------------------------+-------+-----------+----------- +# CMP32ki-SAME: 0 0 0 0 1 1 0 0 . 1 0 1 1 1 0 1 1 +# CMP32ki-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# CMP32ki-SAME: 0 0 0 0 0 0 0 0 . 0 0 1 0 1 0 1 0 +# CMP32ki-SAME: 0 0 0 1 1 0 0 0 . 1 1 1 1 1 1 1 1 +# ---+-----------+---+-------+---+------------------------------- +# BRIEF DA | REG | L | SCALE | 0 | DISPLACEMENT +# ---+-----------+---+-------+---+------------------------------- +name: MxCMP_MI_PCI +body: | + bb.0: + CMP8ki 0, -1, $d1, implicit-def $ccr + CMP8ki -1, 0, $d0, implicit-def $ccr + CMP32ki -1, 0, $d7, implicit-def $ccr + CMP32ki 42, -1, $d1, implicit-def $ccr + +... +--- # PCD +# -------------------------------+-------+-----------+----------- +# F E D C B A 9 8 | 7 6 | 5 4 3 | 2 1 0 +# -------------------------------+-------+-----------+----------- +# OPWORD 0 0 0 0 1 1 0 0 | SIZE | MODE | REG +# -------------------------------+-------+-----------+----------- +# CMP8qi-SAME: 0 0 0 0 1 1 0 0 . 0 0 1 1 1 0 1 0 +# CMP8qi-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# CMP8qi-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# -------------------------------+-------+-----------+----------- +# CMP8qi-SAME: 0 0 0 0 1 1 0 0 . 0 0 1 1 1 0 1 0 +# CMP8qi-SAME: 0 0 0 0 0 0 0 0 . 1 1 1 1 1 1 1 1 +# CMP8qi-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# -------------------------------+-------+-----------+----------- +# CMP32qi-SAME: 0 0 0 0 1 1 0 0 . 1 0 1 1 1 0 1 0 +# CMP32qi-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# CMP32qi-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# CMP32qi-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# -------------------------------+-------+-----------+----------- +# CMP32qi-SAME: 0 0 0 0 1 1 0 0 . 1 0 1 1 1 0 1 0 +# CMP32qi-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# CMP32qi-SAME: 0 0 0 0 0 0 0 0 . 0 0 1 0 1 0 1 0 +# CMP32qi-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +name: MxCMP_MI_PCD +body: | + bb.0: + CMP8qi 0, 0, implicit-def $ccr + CMP8qi -1, -1, implicit-def $ccr + CMP32qi -1, 0, implicit-def $ccr + CMP32qi 42, 0, implicit-def $ccr + +... +--- # ARII +# -------------------------------+-------+-----------+----------- +# F E D C B A 9 8 | 7 6 | 5 4 3 | 2 1 0 +# -------------------------------+-------+-----------+----------- +# OPWORD 0 0 0 0 1 1 0 0 | SIZE | MODE | REG +# -------------------------------+-------+-----------+----------- +# CMP8fi-SAME: 0 0 0 0 1 1 0 0 . 0 0 1 1 0 0 0 1 +# CMP8fi-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# CMP8fi-SAME: 1 0 0 0 1 0 0 0 . 1 1 1 1 1 1 1 1 +# -------------------------------+-------+-----------+----------- +# CMP8fi-SAME: 0 0 0 0 1 1 0 0 . 0 0 1 1 0 0 0 0 +# CMP8fi-SAME: 0 0 0 0 0 0 0 0 . 1 1 1 1 1 1 1 1 +# CMP8fi-SAME: 1 0 0 0 1 0 0 0 . 0 0 0 0 0 0 0 0 +# -------------------------------+-------+-----------+----------- +# CMP32fi-SAME: 0 0 0 0 1 1 0 0 . 1 0 1 1 0 1 1 0 +# CMP32fi-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# CMP32fi-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# CMP32fi-SAME: 1 0 0 0 1 0 0 0 . 0 0 0 0 0 0 0 0 +# -------------------------------+-------+-----------+----------- +# CMP32fi-SAME: 0 0 0 0 1 1 0 0 . 1 0 1 1 0 0 0 1 +# CMP32fi-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# CMP32fi-SAME: 0 0 0 0 0 0 0 0 . 0 0 1 0 1 0 1 0 +# CMP32fi-SAME: 1 0 0 0 1 0 0 0 . 1 1 1 1 1 1 1 1 +# ---+-----------+---+-------+---+------------------------------- +# BRIEF DA | REG | L | SCALE | 0 | DISPLACEMENT +# ---+-----------+---+-------+---+------------------------------- +name: MxCMP_MI_ARII +body: | + bb.0: + CMP8fi 0, -1, $a1, $a0, implicit-def $ccr + CMP8fi -1, 0, $a0, $a0, implicit-def $ccr + CMP32fi -1, 0, $a6, $a0, implicit-def $ccr + CMP32fi 42, -1, $a1, $a0, implicit-def $ccr + +... +--- # ARID +# -------------------------------+-------+-----------+----------- +# F E D C B A 9 8 | 7 6 | 5 4 3 | 2 1 0 +# -------------------------------+-------+-----------+----------- +# OPWORD 0 0 0 0 1 1 0 0 | SIZE | MODE | REG +# -------------------------------+-------+-----------+----------- +# CMP8pi-SAME: 0 0 0 0 1 1 0 0 . 0 0 1 0 1 0 0 1 +# CMP8pi-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# CMP8pi-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# -------------------------------+-------+-----------+----------- +# CMP8pi-SAME: 0 0 0 0 1 1 0 0 . 0 0 1 0 1 0 0 0 +# CMP8pi-SAME: 0 0 0 0 0 0 0 0 . 1 1 1 1 1 1 1 1 +# CMP8pi-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# -------------------------------+-------+-----------+----------- +# CMP32pi-SAME: 0 0 0 0 1 1 0 0 . 1 0 1 0 1 1 1 0 +# CMP32pi-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# CMP32pi-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# CMP32pi-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# -------------------------------+-------+-----------+----------- +# CMP32pi-SAME: 0 0 0 0 1 1 0 0 . 1 0 1 0 1 0 0 1 +# CMP32pi-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# CMP32pi-SAME: 0 0 0 0 0 0 0 0 . 0 0 1 0 1 0 1 0 +# CMP32pi-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +name: MxCMP_MI_ARID +body: | + bb.0: + CMP8pi 0, -1, $a1, implicit-def $ccr + CMP8pi -1, 0, $a0, implicit-def $ccr + CMP32pi -1, 0, $a6, implicit-def $ccr + CMP32pi 42, -1, $a1, implicit-def $ccr + +... +--- # ARI +# -------------------------------+-------+-----------+----------- +# F E D C B A 9 8 | 7 6 | 5 4 3 | 2 1 0 +# -------------------------------+-------+-----------+----------- +# OPWORD 0 0 0 0 1 1 0 0 | SIZE | MODE | REG +# -------------------------------+-------+-----------+----------- +# CMP8ji-SAME: 0 0 0 0 1 1 0 0 . 0 0 0 1 0 0 0 1 +# CMP8ji-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# -------------------------------+-------+-----------+----------- +# CMP8ji-SAME: 0 0 0 0 1 1 0 0 . 0 0 0 1 0 0 0 0 +# CMP8ji-SAME: 0 0 0 0 0 0 0 0 . 1 1 1 1 1 1 1 1 +# -------------------------------+-------+-----------+----------- +# CMP32ji-SAME: 0 0 0 0 1 1 0 0 . 1 0 0 1 0 1 1 0 +# CMP32ji-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# -------------------------------+-------+-----------+----------- +# CMP32ji-SAME: 0 0 0 0 1 1 0 0 . 1 0 0 1 0 0 0 1 +# CMP32ji-SAME: 0 0 0 0 0 0 0 0 . 0 0 1 0 1 0 1 0 +name: MxCMP_MI_ARI +body: | + bb.0: + CMP8ji 0, $a1, implicit-def $ccr + CMP8ji -1, $a0, implicit-def $ccr + CMP32ji -1, $a6, implicit-def $ccr + CMP32ji 42, $a1, implicit-def $ccr + +... Index: test/CodeGen/M680x0/OBJ/Arith/Classes/MxCMP_RI.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Arith/Classes/MxCMP_RI.mir @@ -0,0 +1,36 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj -o - \ +# RUN: | extract-section .text \ +# RUN: | FileCheck %s -check-prefixes=CMP8di,CMP32di + +#------------------------------------------------------------------------------ +# MxCMP_RI class used for compare operations and operates on data registers and +# immediate data. It uses MxArithImmEncoding encoding class. +# NOTE: CMP is calculated by subtracting LHS(Imm) from RHS(Dn) +#------------------------------------------------------------------------------ + + +# -------------------------------+-------+-----------+----------- +# F E D C B A 9 8 | 7 6 | 5 4 3 | 2 1 0 +# -------------------------------+-------+-----------+----------- +# 0 0 0 0 1 1 0 0 | SIZE | MODE | REG +# -------------------------------+-------+-----------+----------- +# CMP8di: 0 0 0 0 1 1 0 0 . 0 0 0 0 0 0 0 1 +# CMP8di-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# -------------------------------+-------+-----------+----------- +# CMP8di-SAME: 0 0 0 0 1 1 0 0 . 0 0 0 0 0 0 0 0 +# CMP8di-SAME: 0 0 0 0 0 0 0 0 . 1 1 1 1 1 1 1 1 +# -------------------------------+-------+-----------+----------- +# CMP32di-SAME: 0 0 0 0 1 1 0 0 . 1 0 0 0 0 1 1 1 +# CMP32di-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# CMP32di-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 1 1 0 1 +# -------------------------------+-------+-----------+----------- +# CMP32di-SAME: 0 0 0 0 1 1 0 0 . 1 0 0 0 0 0 0 1 +# CMP32di-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# CMP32di-SAME: 0 0 0 0 0 0 0 0 . 0 0 1 0 1 0 1 0 +name: MxCMP_RI +body: | + bb.0: + CMP8di 0, $bd1, implicit-def $ccr + CMP8di -1, $bd0, implicit-def $ccr + CMP32di 13, $d7, implicit-def $ccr + CMP32di 42, $d1, implicit-def $ccr Index: test/CodeGen/M680x0/OBJ/Arith/Classes/MxCMP_RM.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Arith/Classes/MxCMP_RM.mir @@ -0,0 +1,123 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj -o - \ +# RUN: | extract-section .text \ +# RUN: | FileCheck %s -check-prefixes=CMP8kf,CMP32kf,CMP8qp,CMP32qp,CMP8df,CMP32df,\ +# RUN:CMP8dp,CMP32dp,CMP8dj,CMP32dj + +#------------------------------------------------------------------------------ +# MxCMP_RM class used for compare operations and operates on memory data and +# register. It uses MxArithEncoding encoding class. +# NOTE: CMP is calculated by subtracting LHS(Mem) from RHS(Reg) +#------------------------------------------------------------------------------ + +--- # PCI +# ---------------+-----------+-----------+-----------+----------- +# F E D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# ---------------+-----------+-----------+-----------+----------- +# OPWORD x x x x | REG | OPMODE | MODE | REG +# ---------------+-----------+-----------+-----------+----------- +# CMP8dk: 1 0 1 1 0 0 0 0 . 0 0 1 1 1 0 1 1 +# CMP8dk-SAME: 0 0 0 1 1 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# CMP8dk-SAME: 1 0 1 1 0 0 0 0 . 0 0 1 1 1 0 1 1 +# CMP8dk-SAME: 0 0 0 1 1 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# CMP32dk-SAME: 1 0 1 1 0 0 0 0 . 1 0 1 1 1 0 1 1 +# CMP32dk-SAME: 0 0 0 1 1 0 0 0 . 0 0 0 0 0 0 0 0 +# ---+-----------+---+-------+---+------------------------------- +# BRIEF DA | REG | L | SCALE | 0 | DISPLACEMENT +# ---+-----------+---+-------+---+------------------------------- +# +# NOTE Immediates for pc-rel instructions use relocations and addendum is encoded +# inside the relocation record thus there are 0s instead of the value. +name: MxBiArOp_RFRM_PCI +body: | + bb.0: + CMP8dk $bd0, 0, $d1, implicit-def $ccr + CMP8dk $bd0, -1, $d1, implicit-def $ccr + CMP32dk $d0, 0, $d1, implicit-def $ccr + +... +--- # PCD +# ---------------+-----------+-----------+-----------+----------- +# F E D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# ---------------+-----------+-----------+-----------+----------- +# OPWORD x x x x | REG | OPMODE | MODE | REG +# ---------------+-----------+-----------+-----------+----------- +# CMP8dq-SAME: 1 0 1 1 0 0 0 0 . 0 0 1 1 1 0 1 0 +# CMP8dq-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# CMP32dq-SAME: 1 0 1 1 0 0 0 0 . 1 0 1 1 1 0 1 0 +# CMP32dq-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# +# NOTE Immediates for pc-rel instructions use relocations and addendum is encoded +# inside the relocation record thus there are 0s instead of the value. +name: MxBiArOp_RFRM_PCD +body: | + bb.0: + CMP8dq $bd0, 0, implicit-def $ccr + CMP32dq $d0, -1, implicit-def $ccr + +... +--- # ARII +# ---------------+-----------+-----------+-----------+----------- +# F E D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# ---------------+-----------+-----------+-----------+----------- +# OPWORD x x x x | REG | OPMODE | MODE | REG +# ---------------+-----------+-----------+-----------+----------- +# CMP8df: 1 0 1 1 0 0 0 0 . 0 0 1 1 0 0 0 0 +# CMP8df-SAME: 0 0 0 1 1 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# CMP8df-SAME: 1 0 1 1 0 0 0 0 . 0 0 1 1 0 0 0 0 +# CMP8df-SAME: 0 0 0 1 1 0 0 0 . 1 1 1 1 1 1 1 1 +# --------------------------------------------------------------- +# CMP32df-SAME: 1 0 1 1 0 0 0 0 . 1 0 1 1 0 0 0 1 +# CMP32df-SAME: 0 0 0 1 1 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# CMP32df-SAME: 1 0 1 1 0 0 1 0 . 1 0 1 1 0 0 1 0 +# CMP32df-SAME: 1 0 1 0 1 0 0 0 . 0 0 0 0 0 0 0 0 +# ---+-----------+---+-------+---+------------------------------- +# BRIEF DA | REG | L | SCALE | 0 | DISPLACEMENT +# ---+-----------+---+-------+---+------------------------------- +name: MxCMP_RM_ARII +body: | + bb.0: + CMP8df $bd0, 0, $a0, $d1, implicit-def $ccr + CMP8df $bd0, -1, $a0, $d1, implicit-def $ccr + CMP32df $d0, 0, $a1, $d1, implicit-def $ccr + CMP32df $d1, 0, $a2, $a2, implicit-def $ccr + +... +--- # ARID +# ---------------+-----------+-----------+-----------+----------- +# F E D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# ---------------+-----------+-----------+-----------+----------- +# OPWORD x x x x | REG | OPMODE | MODE | REG +# ---------------+-----------+-----------+-----------+----------- +# CMP8dp-SAME: 1 0 1 1 0 0 0 0 . 0 0 1 0 1 0 0 0 +# CMP8dp-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# CMP32dp-SAME: 1 0 1 1 0 0 0 0 . 1 0 1 0 1 0 0 1 +# CMP32dp-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +name: MxCMP_RM_ARID +body: | + bb.0: + CMP8dp $bd0, 0, $a0, implicit-def $ccr + CMP32dp $d0, -1, $a1, implicit-def $ccr + +... +--- # ARI +# ---------------+-----------+-----------+-----------+----------- +# F E D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# ---------------+-----------+-----------+-----------+----------- +# OPWORD x x x x | REG | OPMODE | MODE | REG +# ---------------+-----------+-----------+-----------+----------- +# CMP8dj-SAME: 1 0 1 1 0 0 0 0 . 0 0 0 1 0 0 0 0 +# --------------------------------------------------------------- +# CMP32dj-SAME: 1 0 1 1 0 1 1 0 . 1 0 0 1 0 0 0 1 +name: MxCMP_RM_ARI +body: | + bb.0: + CMP8dj $bd0, $a0, implicit-def $ccr + CMP32dj $d3, $a1, implicit-def $ccr + +... Index: test/CodeGen/M680x0/OBJ/Arith/Classes/MxCMP_RR.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Arith/Classes/MxCMP_RR.mir @@ -0,0 +1,27 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj -o - \ +# RUN: | extract-section .text \ +# RUN: | FileCheck %s -check-prefixes=CMP8dd,CMP32dd + +#------------------------------------------------------------------------------ +# MxCMP_RR class used for compare operations and operates on data registers only. +# It uses MxArithEncoding encoding class and MxOpModeNdEA opmode class. +# NOTE: CMP is calculated by subtracting LHS(EA) from RHS(Dn) +#------------------------------------------------------------------------------ + + +# ---------------+-----------+-----------+-----------+----------- +# F E D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# ---------------+-----------+-----------+-----------+----------- +# x x x x | REG | OPMODE | MODE | REG +# ---------------+-----------+-----------+-----------+----------- +# CMP8dd: 1 0 1 1 0 0 1 0 . 0 0 0 0 0 0 0 0 +# CMP8dd-SAME: 1 0 1 1 0 1 0 0 . 0 0 0 0 0 0 1 1 +# CMP32dd-SAME: 1 0 1 1 0 0 1 0 . 1 0 0 0 0 0 0 0 +# CMP32dd-SAME: 1 0 1 1 0 0 1 0 . 1 0 0 0 0 1 1 1 +name: MxCMP_RR +body: | + bb.0: + CMP8dd $bd0, $bd1, implicit-def $ccr + CMP8dd $bd3, $bd2, implicit-def $ccr + CMP32dd $d0, $d1, implicit-def $ccr + CMP32dd $d7, $d1, implicit-def $ccr Index: test/CodeGen/M680x0/OBJ/Arith/Classes/MxDiMu.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Arith/Classes/MxDiMu.mir @@ -0,0 +1,46 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj -o - \ +# RUN: | extract-section .text \ +# RUN: | FileCheck %s -check-prefixes=SDIVd32d16,UDIVd32d16,SMULd32d16,UMULd32d16,\ +# RUN:SDIVd32i16,UDIVd32i16,SMULd32i16,UMULd32i16 + +#------------------------------------------------------------------------------ +# MxDiMu is used for sign/unsigned division/multiply of word size data +#------------------------------------------------------------------------------ + + +# --------------------------------------------------------------- +# F E D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# --------------------------------------------------------------- +# x x x x | REG | OPMODE | MODE | REG +# --------------------------------------------------------------- +# SDIVd32d16: 1 0 0 0 0 0 0 1 . 1 1 0 0 0 0 0 1 +# --------------------------------------------------------------- +# UDIVd32d16-SAME: 1 0 0 0 0 0 0 0 . 1 1 0 0 0 0 0 1 +# --------------------------------------------------------------- +# SDIVd32i16-SAME: 1 0 0 0 0 0 0 1 . 1 1 1 1 1 1 0 0 +# SDIVd32i16-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# UDIVd32i16-SAME: 1 0 0 0 0 0 0 0 . 1 1 1 1 1 1 0 0 +# UDIVd32i16-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# --------------------------------------------------------------- +# SMULd32d16-SAME: 1 1 0 0 0 0 0 1 . 1 1 0 0 0 0 0 1 +# --------------------------------------------------------------- +# UMULd32d16-SAME: 1 1 0 0 0 0 0 0 . 1 1 0 0 0 0 0 1 +# --------------------------------------------------------------- +# SMULd32i16-SAME: 1 1 0 0 0 0 0 1 . 1 1 1 1 1 1 0 0 +# SMULd32i16-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# UMULd32i16-SAME: 1 1 0 0 0 0 0 0 . 1 1 1 1 1 1 0 0 +# UMULd32i16-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# --------------------------------------------------------------- +name: MxDiMu +body: | + bb.0: + $d0 = SDIVd32d16 $d0, $wd1, implicit-def $ccr + $d0 = UDIVd32d16 $d0, $wd1, implicit-def $ccr + $d0 = SDIVd32i16 $d0, 0, implicit-def $ccr + $d0 = UDIVd32i16 $d0, -1, implicit-def $ccr + $d0 = SMULd32d16 $d0, $wd1, implicit-def $ccr + $d0 = UMULd32d16 $d0, $wd1, implicit-def $ccr + $d0 = SMULd32i16 $d0, 0, implicit-def $ccr + $d0 = UMULd32i16 $d0, -1, implicit-def $ccr Index: test/CodeGen/M680x0/OBJ/Arith/Classes/MxExt.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Arith/Classes/MxExt.mir @@ -0,0 +1,25 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj -o - \ +# RUN: | extract-section .text \ +# RUN: | FileCheck %s -check-prefixes=EXT16,EXT32 + +#------------------------------------------------------------------------------ +# MxExt sign extends data inside a register +#------------------------------------------------------------------------------ + + +# --------------------------------------------------------------- +# F E D C B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# --------------------------------------------------------------- +# 0 1 0 0 1 0 0 | OPMODE | 0 0 0 | REG +# --------------------------------------------------------------- +# EXT16: 0 1 0 0 1 0 0 0 . 1 0 0 0 0 0 0 0 +# EXT16-SAME: 0 1 0 0 1 0 0 0 . 1 0 0 0 0 0 1 1 +# EXT32-SAME: 0 1 0 0 1 0 0 0 . 1 1 0 0 0 0 0 0 +# EXT32-SAME: 0 1 0 0 1 0 0 0 . 1 1 0 0 0 1 1 1 +name: MxEXT +body: | + bb.0: + $wd0 = EXT16 $wd0, implicit-def $ccr + $wd3 = EXT16 $wd3, implicit-def $ccr + $d0 = EXT32 $d0, implicit-def $ccr + $d7 = EXT32 $d7, implicit-def $ccr Index: test/CodeGen/M680x0/OBJ/Arith/Classes/MxNEG.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Arith/Classes/MxNEG.mir @@ -0,0 +1,39 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj -o - \ +# RUN: | extract-section .text \ +# RUN: | FileCheck %s -check-prefixes=NEG8d,NEG32d,NEGX8d,NEGX32d + +#------------------------------------------------------------------------------ +# MxNEG is used to negate a number +#------------------------------------------------------------------------------ + + +--- # NEG +# ---------------+---------------+-------+-----------+----------- +# F E D C | B A 9 8 | 7 6 | 5 4 3 | 2 1 0 +# ---------------+---------------+-------+----------------------- +# 0 1 0 0 | x x x x | SIZE | MODE | REG +# ---------------+---------------+-------+-----------+----------- +# NEG8d: 0 1 0 0 0 1 0 0 . 0 0 0 0 0 0 0 0 +# NEG32d-SAME: 0 1 0 0 0 1 0 0 . 1 0 0 0 0 0 0 0 +name: MxNEG +body: | + bb.0: + $bd0 = NEG8d $bd0, implicit-def $ccr + $d0 = NEG32d $d0, implicit-def $ccr + +... +--- # NEGX +# ---------------+---------------+-------+-----------+----------- +# F E D C | B A 9 8 | 7 6 | 5 4 3 | 2 1 0 +# ---------------+---------------+-------+----------------------- +# 0 1 0 0 | x x x x | SIZE | MODE | REG +# ---------------+---------------+-------+-----------+----------- +# NEGX8d-SAME: 0 1 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# NEGX32d-SAME: 0 1 0 0 0 0 0 0 . 1 0 0 0 0 0 0 0 +name: MxNEGX +body: | + bb.0: + $bd0 = NEGX8d $bd0, implicit $ccr, implicit-def $ccr + $d0 = NEGX32d $d0, implicit $ccr, implicit-def $ccr + +... Index: test/CodeGen/M680x0/OBJ/Bits/Classes/MxBTST_MI.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Bits/Classes/MxBTST_MI.mir @@ -0,0 +1,115 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj -o - \ +# RUN: | extract-section .text \ +# RUN: | FileCheck %s -check-prefixes=BTST8ki,BTST8qi,BTST8fi,BTST8pi,BTST8ji + +#------------------------------------------------------------------------------ +# MxBTST_MI class used for BTST operations, the source is locate in memory and +# the bit number is immediate value. This instruciton can only operate on 8 bits. +#------------------------------------------------------------------------------ + + +--- # PCI +# ---------------------------------------+-----------+----------- +# F E D C B A 9 8 7 6 | 5 4 3 | 2 1 0 +# ---------------------------------------+-----------+----------- +# 0 0 0 0 1 0 0 0 0 0 | MODE | REG +# ---------------------------------------+-----------+----------- +# BTST8ki: 0 0 0 0 1 0 0 0 . 0 0 1 1 1 0 1 1 +# BTST8ki-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# BTST8ki-SAME: 0 0 0 1 1 0 0 0 . 1 1 1 1 1 1 1 1 +# -------------------------------+-------+-----------+----------- +# BTST8ki-SAME: 0 0 0 0 1 0 0 0 . 0 0 1 1 1 0 1 1 +# BTST8ki-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 1 +# BTST8ki-SAME: 0 0 0 0 1 0 0 0 . 0 0 0 0 0 0 0 0 +# ---+-----------+---+-------+---+------------------------------- +# BRIEF DA | REG | L | SCALE | 0 | DISPLACEMENT +# ---+-----------+---+-------+---+------------------------------- +name: MxBTST_MI_PCI +body: | + bb.0: + BTST8ki -1, $d1, 0, implicit-def $ccr + BTST8ki 0, $d0, 1, implicit-def $ccr + +... +--- # PCD +# ---------------------------------------+-----------+----------- +# F E D C B A 9 8 7 6 | 5 4 3 | 2 1 0 +# ---------------------------------------+-----------+----------- +# 0 0 0 0 1 0 0 0 0 0 | MODE | REG +# ---------------------------------------+-----------+----------- +# BTST8qi-SAME: 0 0 0 0 1 0 0 0 . 0 0 1 1 1 0 1 0 +# BTST8qi-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# BTST8qi-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# -------------------------------+-------+-----------+----------- +# BTST8qi-SAME: 0 0 0 0 1 0 0 0 . 0 0 1 1 1 0 1 0 +# BTST8qi-SAME: 0 0 0 0 0 0 0 0 . 1 1 1 1 1 1 1 1 +# BTST8qi-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# -------------------------------+-------+-----------+----------- +name: MxBTST_MI_PCD +body: | + bb.0: + BTST8qi 0, 0, implicit-def $ccr + BTST8qi -1, -1, implicit-def $ccr + +... +--- # ARII +# ---------------------------------------+-----------+----------- +# F E D C B A 9 8 7 6 | 5 4 3 | 2 1 0 +# ---------------------------------------+-----------+----------- +# 0 0 0 0 1 0 0 0 0 0 | MODE | REG +# ---------------------------------------+-----------+----------- +# BTST8fi-SAME: 0 0 0 0 1 0 0 0 . 0 0 1 1 0 0 0 1 +# BTST8fi-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# BTST8fi-SAME: 1 0 0 0 1 0 0 0 . 1 1 1 1 1 1 1 1 +# -------------------------------+-------+-----------+----------- +# BTST8fi-SAME: 0 0 0 0 1 0 0 0 . 0 0 1 1 0 0 0 0 +# BTST8fi-SAME: 0 0 0 0 0 0 0 0 . 1 1 1 1 1 1 1 1 +# BTST8fi-SAME: 1 0 0 0 1 0 0 0 . 0 0 0 0 0 0 0 0 +# ---+-----------+---+-------+---+------------------------------- +# BRIEF DA | REG | L | SCALE | 0 | DISPLACEMENT +# ---+-----------+---+-------+---+------------------------------- +name: MxBTST_MI_ARII +body: | + bb.0: + BTST8fi -1, $a1, $a0, 0, implicit-def $ccr + BTST8fi 0, $a0, $a0, -1, implicit-def $ccr + +... +--- # ARID +# ---------------------------------------+-----------+----------- +# F E D C B A 9 8 7 6 | 5 4 3 | 2 1 0 +# ---------------------------------------+-----------+----------- +# 0 0 0 0 1 0 0 0 0 0 | MODE | REG +# ---------------------------------------+-----------+----------- +# BTST8pi-SAME: 0 0 0 0 1 0 0 0 . 0 0 1 0 1 0 0 1 +# BTST8pi-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# BTST8pi-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# -------------------------------+-------+-----------+----------- +# BTST8pi-SAME: 0 0 0 0 1 0 0 0 . 0 0 1 0 1 0 0 0 +# BTST8pi-SAME: 0 0 0 0 0 0 0 0 . 1 1 1 1 1 1 1 1 +# BTST8pi-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +name: MxBTST_MI_ARID +body: | + bb.0: + BTST8pi -1, $a1, 0, implicit-def $ccr + BTST8pi 0, $a0, -1, implicit-def $ccr + +... +--- # ARI +# ---------------------------------------+-----------+----------- +# F E D C B A 9 8 7 6 | 5 4 3 | 2 1 0 +# ---------------------------------------+-----------+----------- +# 0 0 0 0 1 0 0 0 0 0 | MODE | REG +# ---------------------------------------+-----------+----------- +# BTST8ji-SAME: 0 0 0 0 1 0 0 0 . 0 0 0 1 0 0 0 1 +# BTST8ji-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# -------------------------------+-------+-----------+----------- +# BTST8ji-SAME: 0 0 0 0 1 0 0 0 . 0 0 0 1 0 0 0 0 +# BTST8ji-SAME: 0 0 0 0 0 0 0 0 . 1 1 1 1 1 1 1 1 +name: MxBTST_MI_ARI +body: | + bb.0: + BTST8ji $a1, 0, implicit-def $ccr + BTST8ji $a0, -1, implicit-def $ccr + +... Index: test/CodeGen/M680x0/OBJ/Bits/Classes/MxBTST_MR.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Bits/Classes/MxBTST_MR.mir @@ -0,0 +1,104 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj -o - \ +# RUN: | extract-section .text \ +# RUN: | FileCheck %s -check-prefixes=BTST8kd,BTST8qd,BTST8fd,BTST8pd,BTST8jd + +#------------------------------------------------------------------------------ +# MxBTST_MR class used for BTST operations, the source is locate in memory and +# the bit number is in register. This instruciton can only operate on 8 bits. +#------------------------------------------------------------------------------ + + +--- # PCI +# ---------------+-----------+-----------+-----------+----------- +# F E D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# ---------------+-----------+-----------+-----------+----------- +# 0 0 0 0 | REG | 1 0 0 | MODE | REG +# ---------------+-----------+-----------+-----------+----------- +# BTST8kd: 0 0 0 0 0 0 0 1 . 0 0 1 1 1 0 1 1 +# BTST8kd-SAME: 0 0 0 1 1 0 0 0 . 1 1 1 1 1 1 1 1 +# -------------------------------+-------+-----------+----------- +# BTST8kd-SAME: 0 0 0 0 0 0 1 1 . 0 0 1 1 1 0 1 1 +# BTST8kd-SAME: 0 0 0 0 1 0 0 0 . 0 0 0 0 0 0 0 0 +# ---+-----------+---+-------+---+------------------------------- +# BRIEF DA | REG | L | SCALE | 0 | DISPLACEMENT +# ---+-----------+---+-------+---+------------------------------- +name: MxBTST_MR_PCI +body: | + bb.0: + BTST8kd -1, $d1, $bd0, implicit-def $ccr + BTST8kd 0, $d0, $bd1, implicit-def $ccr + +... +--- # PCD +# ---------------+-----------+-----------+-----------+----------- +# F E D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# ---------------+-----------+-----------+-----------+----------- +# 0 0 0 0 | REG | 1 0 0 | MODE | REG +# ---------------+-----------+-----------+-----------+----------- +# BTST8qd-SAME: 0 0 0 0 0 0 0 1 . 0 0 1 1 1 0 1 0 +# BTST8qd-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# -------------------------------+-------+-----------+----------- +# BTST8qd-SAME: 0 0 0 0 0 0 1 1 . 0 0 1 1 1 0 1 0 +# BTST8qd-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# -------------------------------+-------+-----------+----------- +name: MxBTST_MR_PCD +body: | + bb.0: + BTST8qd 0, $bd0, implicit-def $ccr + BTST8qd -1, $bd1, implicit-def $ccr + +... +--- # ARII +# ---------------+-----------+-----------+-----------+----------- +# F E D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# ---------------+-----------+-----------+-----------+----------- +# 0 0 0 0 | REG | 1 0 0 | MODE | REG +# ---------------+-----------+-----------+-----------+----------- +# BTST8fd-SAME: 0 0 0 0 0 0 0 1 . 0 0 1 1 0 0 0 1 +# BTST8fd-SAME: 1 0 0 0 1 0 0 0 . 1 1 1 1 1 1 1 1 +# -------------------------------+-------+-----------+----------- +# BTST8fd-SAME: 0 0 0 0 0 0 1 1 . 0 0 1 1 0 0 0 0 +# BTST8fd-SAME: 1 0 0 0 1 0 0 0 . 0 0 0 0 0 0 0 0 +# ---+-----------+---+-------+---+------------------------------- +# BRIEF DA | REG | L | SCALE | 0 | DISPLACEMENT +# ---+-----------+---+-------+---+------------------------------- +name: MxBTST_MR_ARII +body: | + bb.0: + BTST8fd -1, $a1, $a0, $bd0, implicit-def $ccr + BTST8fd 0, $a0, $a0, $bd1, implicit-def $ccr + +... +--- # ARID +# ---------------+-----------+-----------+-----------+----------- +# F E D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# ---------------+-----------+-----------+-----------+----------- +# 0 0 0 0 | REG | 1 0 0 | MODE | REG +# ---------------+-----------+-----------+-----------+----------- +# BTST8pd-SAME: 0 0 0 0 0 0 0 1 . 0 0 1 0 1 0 0 1 +# BTST8pd-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# -------------------------------+-------+-----------+----------- +# BTST8pd-SAME: 0 0 0 0 0 0 1 1 . 0 0 1 0 1 0 0 0 +# BTST8pd-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +name: MxBTST_MR_ARID +body: | + bb.0: + BTST8pd -1, $a1, $bd0, implicit-def $ccr + BTST8pd 0, $a0, $bd1, implicit-def $ccr + +... +--- # ARI +# ---------------+-----------+-----------+-----------+----------- +# F E D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# ---------------+-----------+-----------+-----------+----------- +# 0 0 0 0 | REG | 1 0 0 | MODE | REG +# ---------------+-----------+-----------+-----------+----------- +# BTST8jd-SAME: 0 0 0 0 0 0 0 1 . 0 0 0 1 0 0 0 1 +# BTST8jd-SAME: 0 0 0 0 0 0 1 1 . 0 0 0 1 0 0 0 0 +name: MxBTST_MR_ARI +body: | + bb.0: + BTST8jd $a1, $bd0, implicit-def $ccr + BTST8jd $a0, $bd1, implicit-def $ccr + +... Index: test/CodeGen/M680x0/OBJ/Bits/Classes/MxBTST_RI.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Bits/Classes/MxBTST_RI.mir @@ -0,0 +1,25 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj -o - \ +# RUN: | extract-section .text \ +# RUN: | FileCheck %s -check-prefixes=BTST32di + +#------------------------------------------------------------------------------ +# MxBTST_RI class used for BTST operations, the source is in a data register and +# the bit number is a immediate. This instruction can only operate on 32 bits +#------------------------------------------------------------------------------ + + +# ---------------------------------------+-----------+----------- +# F E D C B A 9 8 7 6 | 5 4 3 | 2 1 0 +# ---------------------------------------------------+----------- +# 0 0 0 0 1 0 0 0 0 0 | MODE | REG +# ---------------------------------------+-----------+----------- +# BTST32di: 0 0 0 0 1 0 0 0 . 0 0 0 0 0 0 0 0 +# BTST32di: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 1 +# --------------------------------------------------------------- +# BTST32di: 0 0 0 0 1 0 0 0 . 0 0 0 0 0 0 1 1 +# BTST32di: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +name: MxBTST_RI +body: | + bb.0: + BTST32di $d0, 1, implicit-def $ccr + BTST32di $d3, 0, implicit-def $ccr Index: test/CodeGen/M680x0/OBJ/Bits/Classes/MxBTST_RR.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Bits/Classes/MxBTST_RR.mir @@ -0,0 +1,22 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj -o - \ +# RUN: | extract-section .text \ +# RUN: | FileCheck %s -check-prefixes=BTST32dd + +#------------------------------------------------------------------------------ +# MxBTST_RR class used for BTST operations, where both the source and bit number +# are in registers. This instruction can only operate on 32 bits +#------------------------------------------------------------------------------ + + +# ---------------+-----------+-----------+-----------+----------- +# F E D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# ---------------+-----------+-----------+-----------+----------- +# 0 0 0 0 | REG | 1 0 0 | MODE | REG +# ---------------+-----------+-----------+-----------+----------- +# BTST32dd: 0 0 0 0 0 0 1 1 . 0 0 0 0 0 0 0 0 +# BTST32dd: 0 0 0 0 0 0 0 1 . 0 0 0 0 0 0 1 1 +name: MxBTST_RR +body: | + bb.0: + BTST32dd $d0, $d1, implicit-def $ccr + BTST32dd $d3, $d0, implicit-def $ccr Index: test/CodeGen/M680x0/OBJ/Control/Classes/MxBRA.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Control/Classes/MxBRA.mir @@ -0,0 +1,49 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj -o - \ +# RUN: | extract-section .text \ +# RUN: | FileCheck %s -check-prefixes=BRA8,BRA16 + +#------------------------------------------------------------------------------ +# MxBRA unconditionally branches somewhere +#------------------------------------------------------------------------------ + +--- # MxBRA8 +# -------------------------------+------------------------------- +# F E D C B A 9 8 | 7 6 5 4 3 2 1 0 +# -------------------------------+------------------------------- +# 0 1 1 0 0 0 0 0 | 8-BIT DISPLACEMENT +# -------------------------------+------------------------------- +# BRA8: 0 1 1 0 0 0 0 0 . 0 0 0 0 0 0 0 1 +# BRA8-SAME: 0 1 1 0 0 0 0 0 . 0 0 1 0 1 0 1 0 +# +# NOTE MxBRA branches cannot encode 0 displacement, 0 in displacement instructs +# to use additional word. Also it cannot encode -1 since all 1s instruct to use +# two additional words to encode 32bit offset(since M68020). +name: MxBRA8 +body: | + bb.0: + BRA8 1, implicit $ccr + BRA8 42, implicit $ccr + +... +--- # MxBRA16 +# -------------------------------+------------------------------- +# F E D C B A 9 8 | 7 6 5 4 3 2 1 0 +# -------------------------------+------------------------------- +# 0 1 1 0 0 0 0 0 | 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# BRA16-SAME: 0 1 1 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# BRA16-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# -------------------------------+------------------------------- +# BRA16-SAME: 0 1 1 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# BRA16-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# --------------------------------------------------------------- +# BRA16-SAME: 0 1 1 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# BRA16-SAME: 0 0 0 0 0 0 0 0 . 0 0 1 0 1 0 1 0 +name: MxBRA16 +body: | + bb.0: + BRA16 0, implicit $ccr + BRA16 -1, implicit $ccr + BRA16 42, implicit $ccr + +... Index: test/CodeGen/M680x0/OBJ/Control/Classes/MxBcc.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Control/Classes/MxBcc.mir @@ -0,0 +1,119 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj -o - \ +# RUN: | extract-section .text \ +# RUN: | FileCheck %s -check-prefixes=Bcc8,Bls8,Blt8,Beq8,Bmi8,Bne8,Bge8,Bcs8,\ +# RUN:Bpl8,Bgt8,Bhi8,Bvc8,Ble8,Bvs8,Bcc16,Bls16,Blt16,Beq16,Bmi16,Bne16,Bge16,\ +# RUN:Bcs16,Bpl16,Bgt16,Bhi16,Bvc16,Ble16,Bvs16 + +#------------------------------------------------------------------------------ +# MxScc branches if the condition is True +#------------------------------------------------------------------------------ + +--- # MxBcc8 +# ---------------+---------------+------------------------------- +# F E D C | B A 9 8 | 7 6 5 4 3 2 1 0 +# ---------------+---------------+------------------------------- +# 0 1 1 0 | CONDITION | 8-BIT DISPLACEMENT +# ---------------+---------------+------------------------------- +# Bhi8: 0 1 1 0 0 0 1 0 . 0 0 0 0 0 0 0 1 +# Bls8-SAME: 0 1 1 0 0 0 1 1 . 0 0 1 0 1 0 1 0 +# Bcc8-SAME: 0 1 1 0 0 1 0 0 . 0 0 0 0 0 0 0 1 +# Bcs8-SAME: 0 1 1 0 0 1 0 1 . 0 0 0 0 0 0 0 1 +# Bne8-SAME: 0 1 1 0 0 1 1 0 . 0 0 0 0 0 0 0 1 +# Beq8-SAME: 0 1 1 0 0 1 1 1 . 0 0 0 0 0 0 0 1 +# Bvc8-SAME: 0 1 1 0 1 0 0 0 . 0 0 0 0 0 0 0 1 +# Bvs8-SAME: 0 1 1 0 1 0 0 1 . 0 0 0 0 0 0 0 1 +# Bpl8-SAME: 0 1 1 0 1 0 1 0 . 0 0 0 0 0 0 0 1 +# Bmi8-SAME: 0 1 1 0 1 0 1 1 . 0 0 0 0 0 0 0 1 +# Bge8-SAME: 0 1 1 0 1 1 0 0 . 0 0 0 0 0 0 0 1 +# Blt8-SAME: 0 1 1 0 1 1 0 1 . 0 0 0 0 0 0 0 1 +# Bgt8-SAME: 0 1 1 0 1 1 1 0 . 0 0 0 0 0 0 0 1 +# Ble8-SAME: 0 1 1 0 1 1 1 1 . 0 0 0 0 0 0 0 1 +# +# NOTE MxBcc8 branches cannot encode 0 displacement, 0 in displacement instructs +# to use additional word. Also it cannot encode -1 since all 1s instruct to use +# two additional words to encode 32bit offset(since M68020). +name: MxBcc8 +body: | + bb.0: + Bhi8 1, implicit $ccr + Bls8 42, implicit $ccr + Bcc8 1, implicit $ccr + Bcs8 1, implicit $ccr + Bne8 1, implicit $ccr + Beq8 1, implicit $ccr + Bvc8 1, implicit $ccr + Bvs8 1, implicit $ccr + Bpl8 1, implicit $ccr + Bmi8 1, implicit $ccr + Bge8 1, implicit $ccr + Blt8 1, implicit $ccr + Bgt8 1, implicit $ccr + Ble8 1, implicit $ccr + +... +--- # MxBcc16 +# ---------------+---------------+------------------------------- +# F E D C | B A 9 8 | 7 6 5 4 3 2 1 0 +# ---------------+---------------+------------------------------- +# 0 1 1 0 | CONDITION | 0 0 0 0 0 0 0 0 +# ---------------+---------------+------------------------------- +# Bhi16-SAME: 0 1 1 0 0 0 1 0 . 0 0 0 0 0 0 0 0 +# Bhi16-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# --------------------------------------------------------------- +# Bls16-SAME: 0 1 1 0 0 0 1 1 . 0 0 0 0 0 0 0 0 +# Bls16-SAME: 0 0 0 0 0 0 0 0 . 0 0 1 0 1 0 1 0 +# --------------------------------------------------------------- +# Bcc16-SAME: 0 1 1 0 0 1 0 0 . 0 0 0 0 0 0 0 0 +# Bcc16-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# Bcs16-SAME: 0 1 1 0 0 1 0 1 . 0 0 0 0 0 0 0 0 +# Bcs16-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# Bne16-SAME: 0 1 1 0 0 1 1 0 . 0 0 0 0 0 0 0 0 +# Bne16-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# Beq16-SAME: 0 1 1 0 0 1 1 1 . 0 0 0 0 0 0 0 0 +# Bge16-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# Bvc16-SAME: 0 1 1 0 1 0 0 0 . 0 0 0 0 0 0 0 0 +# Bvc16-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# Bvs16-SAME: 0 1 1 0 1 0 0 1 . 0 0 0 0 0 0 0 0 +# Bvs16-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# Bpl16-SAME: 0 1 1 0 1 0 1 0 . 0 0 0 0 0 0 0 0 +# Bpl16-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# Bmi16-SAME: 0 1 1 0 1 0 1 1 . 0 0 0 0 0 0 0 0 +# Bmi16-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# Bge16-SAME: 0 1 1 0 1 1 0 0 . 0 0 0 0 0 0 0 0 +# Ble16-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# Blt16-SAME: 0 1 1 0 1 1 0 1 . 0 0 0 0 0 0 0 0 +# Blt16-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# Bgt16-SAME: 0 1 1 0 1 1 1 0 . 0 0 0 0 0 0 0 0 +# Bgt16-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# Ble16-SAME: 0 1 1 0 1 1 1 1 . 0 0 0 0 0 0 0 0 +# Ble16-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +name: MxBcc16 +body: | + bb.0: + Bhi16 -1, implicit $ccr + Bls16 42, implicit $ccr + Bcc16 0, implicit $ccr + Bcs16 0, implicit $ccr + Bne16 0, implicit $ccr + Beq16 0, implicit $ccr + Bvc16 0, implicit $ccr + Bvs16 0, implicit $ccr + Bpl16 0, implicit $ccr + Bmi16 0, implicit $ccr + Bge16 0, implicit $ccr + Blt16 0, implicit $ccr + Bgt16 0, implicit $ccr + Ble16 0, implicit $ccr + +... Index: test/CodeGen/M680x0/OBJ/Control/Classes/MxCALL.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Control/Classes/MxCALL.mir @@ -0,0 +1,88 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj -o - \ +# RUN: | extract-section .text \ +# RUN: | FileCheck %s -check-prefixes=CALLk,CALLq,CALLb,CALLj + +#------------------------------------------------------------------------------ +# MxCALL pushes address of the next instruction and jumps to the location +#------------------------------------------------------------------------------ + +--- # MxCALL_PCI +# ---------------------------------------+-----------+----------- +# F E D C B A 9 8 7 6 | 5 4 3 | 2 1 0 +# ---------------------------------------+-----------+----------- +# 0 1 0 0 1 1 1 0 1 0 | MODE | REG +# ---------------------------------------+-----------+----------- +# CALLk: 0 1 0 0 1 1 1 0 . 1 0 1 1 1 0 1 1 +# CALLk-SAME: 1 0 0 0 1 0 0 0 . 0 0 0 0 0 0 0 0 +# ---------------------------------------+-----------+----------- +# CALLk-SAME: 0 1 0 0 1 1 1 0 . 1 0 1 1 1 0 1 1 +# CALLk-SAME: 1 0 0 0 1 0 0 0 . 1 1 1 1 1 1 1 1 +# ---------------------------------------+-----------+----------- +# CALLk-SAME: 0 1 0 0 1 1 1 0 . 1 0 1 1 1 0 1 1 +# CALLk-SAME: 1 0 0 0 1 0 0 0 . 0 0 1 0 1 0 1 0 +# ---+-----------+---+-------+---+------------------------------- +# BRIEF DA | REG | L | SCALE | 0 | DISPLACEMENT +# ---+-----------+---+-------+---+------------------------------- +name: MxCALL_PCI +body: | + bb.0: + CALLk 0, $a0 + CALLk -1, $a0 + CALLk 42, $a0 + +... +--- # MxCALL_PCD +# ---------------------------------------+-----------+----------- +# F E D C B A 9 8 7 6 | 5 4 3 | 2 1 0 +# ---------------------------------------+-----------+----------- +# 0 1 0 0 1 1 1 0 1 0 | MODE | REG +# ---------------------------------------+-----------+----------- +# CALLq-SAME: 0 1 0 0 1 1 1 0 . 1 0 1 1 1 0 1 0 +# CALLq-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# ---------------------------------------+-----------+----------- +# CALLq-SAME: 0 1 0 0 1 1 1 0 . 1 0 1 1 1 0 1 0 +# CALLq-SAME: 0 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +name: MxCALL_PCD +body: | + bb.0: + CALLq 0 + CALLq 32767 + +... +--- # MxCALL_ABS +# ---------------------------------------+-----------+----------- +# F E D C B A 9 8 7 6 | 5 4 3 | 2 1 0 +# ---------------------------------------+-----------+----------- +# 0 1 0 0 1 1 1 0 1 0 | MODE | REG +# ---------------------------------------+-----------+----------- +# CALLb-SAME: 0 1 0 0 1 1 1 0 . 1 0 1 1 1 0 0 1 +# CALLb-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# CALLb-SAME: 0 0 0 0 0 0 0 0 . 0 0 1 0 1 0 1 0 +# ---------------------------------------+-----------+----------- +# CALLb-SAME: 0 1 0 0 1 1 1 0 . 1 0 1 1 1 0 0 1 +# CALLb-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# CALLb-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +name: MxCALL_ABS +body: | + bb.0: + CALLb 42 + CALLb -1 + +... +--- # MxCALL_ARI +# ---------------------------------------+-----------+----------- +# F E D C B A 9 8 7 6 | 5 4 3 | 2 1 0 +# ---------------------------------------+-----------+----------- +# 0 1 0 0 1 1 1 0 1 0 | MODE | REG +# ---------------------------------------+-----------+----------- +# CALLj-SAME: 0 1 0 0 1 1 1 0 . 1 0 0 1 0 0 0 0 +# CALLj-SAME: 0 1 0 0 1 1 1 0 . 1 0 0 1 0 0 0 1 +# CALLj-SAME: 0 1 0 0 1 1 1 0 . 1 0 0 1 0 0 1 0 +name: MxCALL_ARI +body: | + bb.0: + CALLj $a0 + CALLj $a1 + CALLj $a2 + +... Index: test/CodeGen/M680x0/OBJ/Control/Classes/MxJMP.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Control/Classes/MxJMP.mir @@ -0,0 +1,21 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj -o - \ +# RUN: | extract-section .text \ +# RUN: | FileCheck %s -check-prefixes=JMP32j + +#------------------------------------------------------------------------------ +# MxJMP encodes unconditional jump +#------------------------------------------------------------------------------ + +--- # MxJMP_ARI +# ---------------------------------------+-----------+----------- +# F E D C B A 9 8 7 6 | 5 4 3 | 2 1 0 +# ---------------------------------------+-----------+----------- +# 0 1 0 0 1 1 1 0 1 1 | MODE | REG +# ---------------------------------------+-----------+----------- +# JMP32j: 0 1 0 0 1 1 1 0 . 1 1 0 1 0 0 0 0 +name: MxJMP_ARI +body: | + bb.0: + JMP32j $a0 + +... Index: test/CodeGen/M680x0/OBJ/Control/Classes/MxNOP.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Control/Classes/MxNOP.mir @@ -0,0 +1,16 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj -o - \ +# RUN: | extract-section .text \ +# RUN: | FileCheck %s -check-prefixes=NOP + +#------------------------------------------------------------------------------ +# MxNOP +#------------------------------------------------------------------------------ + +# --------------------------------------------------------------- +# F E D C B A 9 8 7 6 5 4 3 2 1 0 +# --------------------------------------------------------------- +# NOP: 0 1 0 0 1 1 1 0 . 0 1 1 1 0 0 0 1 +name: MxNOP +body: | + bb.0: + NOP Index: test/CodeGen/M680x0/OBJ/Control/Classes/MxRTS.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Control/Classes/MxRTS.mir @@ -0,0 +1,16 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj -o - \ +# RUN: | extract-section .text \ +# RUN: | FileCheck %s -check-prefixes=RTS + +#------------------------------------------------------------------------------ +# MxRTS pops return address from the stack and jumps there +#------------------------------------------------------------------------------ + +# --------------------------------------------------------------- +# F E D C B A 9 8 7 6 5 4 3 2 1 0 +# --------------------------------------------------------------- +# RTS: 0 1 0 0 1 1 1 0 . 0 1 1 1 0 1 0 1 +name: MxRTS +body: | + bb.0: + RTS Index: test/CodeGen/M680x0/OBJ/Control/Classes/MxScc.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Control/Classes/MxScc.mir @@ -0,0 +1,128 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj -o - \ +# RUN: | extract-section .text \ +# RUN: | FileCheck %s -check-prefixes=SETd8cc,SETd8ls,SETd8lt,SETd8eq,SETd8mi,\ +# RUN:SETd8f,SETd8ne,SETd8ge,SETd8cs,SETd8pl,SETd8gt,SETd8t,SETd8hi,SETd8vc,\ +# RUN:SETd8le,SETd8vs,SETp8cc,SETp8ls,SETp8lt,SETp8eq,SETp8mi,SETp8f,SETp8ne,\ +# RUN:SETp8ge,SETp8cs,SETp8pl,SETp8gt,SETp8t,SETp8hi,SETp8vc,SETp8le,SETp8vs + +#------------------------------------------------------------------------------ +# MxScc sets byte filled with 1s or 0s based on cc condition +#------------------------------------------------------------------------------ + +--- # MxScc_D +# ---------------+---------------+-------+-----------+----------- +# F E D C | B A 9 8 | 7 6 | 5 4 3 | 2 1 0 +# ---------------+---------------+-------+-----------+----------- +# 0 1 0 1 | CONDITION | 1 1 | MODE | REG +# ---------------+---------------+-------+-----------+----------- +# SETd8t: 0 1 0 1 0 0 0 0 . 1 1 0 0 0 0 0 0 +# SETd8f-SAME: 0 1 0 1 0 0 0 1 . 1 1 0 0 0 0 0 1 +# SETd8hi-SAME: 0 1 0 1 0 0 1 0 . 1 1 0 0 0 0 1 0 +# SETd8ls-SAME: 0 1 0 1 0 0 1 1 . 1 1 0 0 0 0 1 1 +# SETd8cc-SAME: 0 1 0 1 0 1 0 0 . 1 1 0 0 0 1 0 0 +# SETd8cs-SAME: 0 1 0 1 0 1 0 1 . 1 1 0 0 0 1 0 1 +# SETd8ne-SAME: 0 1 0 1 0 1 1 0 . 1 1 0 0 0 1 1 0 +# SETd8eq-SAME: 0 1 0 1 0 1 1 1 . 1 1 0 0 0 1 1 1 +# SETd8vc-SAME: 0 1 0 1 1 0 0 0 . 1 1 0 0 0 0 0 0 +# SETd8vs-SAME: 0 1 0 1 1 0 0 1 . 1 1 0 0 0 0 0 0 +# SETd8pl-SAME: 0 1 0 1 1 0 1 0 . 1 1 0 0 0 0 0 0 +# SETd8mi-SAME: 0 1 0 1 1 0 1 1 . 1 1 0 0 0 0 0 0 +# SETd8ge-SAME: 0 1 0 1 1 1 0 0 . 1 1 0 0 0 0 0 0 +# SETd8lt-SAME: 0 1 0 1 1 1 0 1 . 1 1 0 0 0 0 0 0 +# SETd8gt-SAME: 0 1 0 1 1 1 1 0 . 1 1 0 0 0 0 0 0 +# SETd8le-SAME: 0 1 0 1 1 1 1 1 . 1 1 0 0 0 0 0 0 +name: MxScc_D +body: | + bb.0: + $bd0 = SETd8t implicit $ccr + $bd1 = SETd8f implicit $ccr + $bd2 = SETd8hi implicit $ccr + $bd3 = SETd8ls implicit $ccr + $bd4 = SETd8cc implicit $ccr + $bd5 = SETd8cs implicit $ccr + $bd6 = SETd8ne implicit $ccr + $bd7 = SETd8eq implicit $ccr + $bd0 = SETd8vc implicit $ccr + $bd0 = SETd8vs implicit $ccr + $bd0 = SETd8pl implicit $ccr + $bd0 = SETd8mi implicit $ccr + $bd0 = SETd8ge implicit $ccr + $bd0 = SETd8lt implicit $ccr + $bd0 = SETd8gt implicit $ccr + $bd0 = SETd8le implicit $ccr + +... +--- # MxScc_ARID +# ---------------+---------------+-------+-----------+----------- +# F E D C | B A 9 8 | 7 6 | 5 4 3 | 2 1 0 +# ---------------+---------------+-------+-----------+----------- +# 0 1 0 1 | CONDITION | 1 1 | MODE | REG +# ---------------+---------------+-------+-----------+----------- +# SETp8t-SAME: 0 1 0 1 0 0 0 0 . 1 1 1 0 1 0 0 0 +# SETp8t-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# --------------------------------------------------------------- +# SETp8f-SAME: 0 1 0 1 0 0 0 1 . 1 1 1 0 1 0 0 1 +# SETp8f-SAME: 0 0 0 0 0 0 0 0 . 0 0 1 0 1 0 1 0 +# --------------------------------------------------------------- +# SETp8hi-SAME: 0 1 0 1 0 0 1 0 . 1 1 1 0 1 0 1 0 +# SETp8hi-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# SETp8ls-SAME: 0 1 0 1 0 0 1 1 . 1 1 1 0 1 0 1 1 +# SETp8ls-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# SETp8cc-SAME: 0 1 0 1 0 1 0 0 . 1 1 1 0 1 1 0 0 +# SETp8cc-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# SETp8cs-SAME: 0 1 0 1 0 1 0 1 . 1 1 1 0 1 1 0 1 +# SETp8cs-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# SETp8ne-SAME: 0 1 0 1 0 1 1 0 . 1 1 1 0 1 1 1 0 +# SETp8ne-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# SETp8eq-SAME: 0 1 0 1 0 1 1 1 . 1 1 1 0 1 0 0 0 +# SETp8eq-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# SETp8vc-SAME: 0 1 0 1 1 0 0 0 . 1 1 1 0 1 0 0 0 +# SETp8vc-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# SETp8vs-SAME: 0 1 0 1 1 0 0 1 . 1 1 1 0 1 0 0 0 +# SETp8vs-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# SETp8pl-SAME: 0 1 0 1 1 0 1 0 . 1 1 1 0 1 0 0 0 +# SETp8pl-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# SETp8mi-SAME: 0 1 0 1 1 0 1 1 . 1 1 1 0 1 0 0 0 +# SETp8mi-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# SETp8ge-SAME: 0 1 0 1 1 1 0 0 . 1 1 1 0 1 0 0 0 +# SETp8ge-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# SETp8lt-SAME: 0 1 0 1 1 1 0 1 . 1 1 1 0 1 0 0 0 +# SETp8lt-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# SETp8gt-SAME: 0 1 0 1 1 1 1 0 . 1 1 1 0 1 0 0 0 +# SETp8gt-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# SETp8le-SAME: 0 1 0 1 1 1 1 1 . 1 1 1 0 1 0 0 0 +# SETp8le-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +name: MxScc_ARID +body: | + bb.0: + SETp8t -1, $a0, implicit $ccr + SETp8f 42, $a1, implicit $ccr + SETp8hi 0, $a2, implicit $ccr + SETp8ls 0, $a3, implicit $ccr + SETp8cc 0, $a4, implicit $ccr + SETp8cs 0, $a5, implicit $ccr + SETp8ne 0, $a6, implicit $ccr + SETp8eq 0, $a0, implicit $ccr + SETp8vc 0, $a0, implicit $ccr + SETp8vs 0, $a0, implicit $ccr + SETp8pl 0, $a0, implicit $ccr + SETp8mi 0, $a0, implicit $ccr + SETp8ge 0, $a0, implicit $ccr + SETp8lt 0, $a0, implicit $ccr + SETp8gt 0, $a0, implicit $ccr + SETp8le 0, $a0, implicit $ccr + +... Index: test/CodeGen/M680x0/OBJ/Control/branch-pc-rel.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Control/branch-pc-rel.mir @@ -0,0 +1,31 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj \ +# RUN: -code-model=small -relocation-model=pic -o - \ +# RUN: | extract-section .text -h \ +# RUN: | FileCheck %s -check-prefixes=x00 + +#------------------------------------------------------------------------------ +# This test checks whether branches have correct offset +#------------------------------------------------------------------------------ + +--- # TEXT +# 0 x00: 6702 +# 2 x00-SAME: 6008 +# 4 x00-SAME: d0bc 0000 0000 +# A x00-SAME: 4e75 +# C x00-SAME: d0bc 0000 0001 +# 12 x00-SAME: 4e75 +name: TEXT +body: | + bb.0: + successors: %bb.2,%bb.1 + + Beq8 %bb.1, implicit $ccr + BRA8 %bb.2 + bb.1: + $d0 = ADD32ri $d0, 0, implicit-def $ccr + RET 0, $d0 + bb.2: + $d0 = ADD32ri $d0, 1, implicit-def $ccr + RET 0, $d0 + +... Index: test/CodeGen/M680x0/OBJ/Control/call-pc-rel.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Control/call-pc-rel.mir @@ -0,0 +1,66 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj \ +# RUN: -code-model=small -relocation-model=pic -o - \ +# RUN: | extract-section .text -h \ +# RUN: | FileCheck %s -check-prefixes=x00 + +#------------------------------------------------------------------------------ +# Tests PC-Relative Calls' offsets. The rest requires relocation and tested +# appropriately elsewhere. +#------------------------------------------------------------------------------ + +# +# +# 00 x00: 4e71 +# 02 x00-SAME: 4e75 +# +# +# 04 x00-SAME: 4ebb 08fa +# 08 x00-SAME: 4ebb 080a +# +# +# 0c x00-SAME: 4eba fff2 +# 10 x00-SAME: 4eba 0002 +# +# +# 14 x00-SAME: 4e71 +# 16 x00-SAME: 4e75 +--- | + + define void @BACKWARD() { entry: ret void } + define void @PCI() { entry: ret void } + define void @PCD() { entry: ret void } + define void @FORWARD() { entry: ret void } + +... +--- # BACKWARD +name: BACKWARD +body: | + bb.0: + NOP + RTS + +... +--- # PCI +name: PCI +body: | + bb.0: + CALLk @BACKWARD, $d0 + CALLk @FORWARD, $d0 + +... +--- # PCD +name: PCD +body: | + bb.0: + CALLq @BACKWARD + CALLq @FORWARD + +... +--- # FORWARD +name: FORWARD +body: | + bb.0: + NOP + RTS + +... Index: test/CodeGen/M680x0/OBJ/Data/Classes/MxLEA.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Data/Classes/MxLEA.mir @@ -0,0 +1,65 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj -o - \ +# RUN: | extract-section .text \ +# RUN: | FileCheck %s -check-prefixes=LEA32q,LEA32f,LEA32p + +#------------------------------------------------------------------------------ +# MxLEA is used to calculate effective address and load it into a address reg +#------------------------------------------------------------------------------ + +--- # PCD +# --------------------------------------------------------------- +# F E D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# --------------------------------------------------------------- +# 0 1 0 0 | DST REG | 1 1 1 | MODE | REG +# --------------------------------------------------------------- +# LEA32q: 0 1 0 0 0 0 0 1 . 1 1 1 1 1 0 1 0 +# LEA32q-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# LEA32q-SAME: 0 1 0 0 0 0 0 1 . 1 1 1 1 1 0 1 0 +# LEA32q-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +name: MxLEA_PCD +body: | + bb.0: + $a0 = LEA32q 0, implicit-def $ccr + $a0 = LEA32q -1, implicit-def $ccr + +... +--- # ARII +# --------------------------------------------------------------- +# F E D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# --------------------------------------------------------------- +# 0 1 0 0 | DST REG | 1 1 1 | MODE | REG +# --------------------------------------------------------------- +# LEA32f-SAME: 0 1 0 0 0 0 0 1 . 1 1 1 1 0 0 0 1 +# LEA32f-SAME: 0 0 0 1 1 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# LEA32f-SAME: 0 1 0 0 0 0 1 1 . 1 1 1 1 0 0 1 0 +# LEA32f-SAME: 1 0 1 0 1 0 0 0 . 0 0 0 0 0 0 0 0 +# ---+-----------+---+-------+---+------------------------------- +# BRIEF DA | REG | L | SCALE | 0 | DISPLACEMENT +# ---+-----------+---+-------+---+------------------------------- +name: MxLEA_ARII +body: | + bb.0: + $a0 = LEA32f 0, $a1, $d1, implicit-def $ccr + $a1 = LEA32f 0, $a2, $a2, implicit-def $ccr + +... +--- # ARID +# --------------------------------------------------------------- +# F E D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# --------------------------------------------------------------- +# 0 1 0 0 | DST REG | 1 1 1 | MODE | REG +# --------------------------------------------------------------- +# LEA32p-SAME: 0 1 0 0 0 0 0 1 . 1 1 1 0 1 0 0 1 +# LEA32p-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# --------------------------------------------------------------- +# LEA32p-SAME: 0 1 0 0 0 0 0 1 . 1 1 1 0 1 0 0 1 +# LEA32p-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +name: MxLEA_ARID +body: | + bb.0: + $a0 = LEA32p -1, $a1, implicit-def $ccr + $a0 = LEA32p -1, $a1, implicit-def $ccr + +... Index: test/CodeGen/M680x0/OBJ/Data/Classes/MxMOVEM_MR.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Data/Classes/MxMOVEM_MR.mir @@ -0,0 +1,52 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj -o - \ +# RUN: | extract-section .text \ +# RUN: | FileCheck %s -check-prefixes=MOVM32pm,MOVM32jm + +#------------------------------------------------------------------------------ +# MxMOVEM_MR is used to store a list of register sequentially into a memory +# location +#------------------------------------------------------------------------------ + +--- # ARID +# -------------------+---+-----------+---+-----------+----------- +# F E D C B | A | 9 8 7 | 6 | 5 4 3 | 2 1 0 +# -------------------+---+-----------+---+-----------+----------- +# 0 1 0 0 1 | D | 0 0 1 | S | MODE | REG +# -------------------+---+-----------+---+-----------+----------- +# REGISTER LIST MASK +# --------------------------------------------------------------- +# MOVM32pm: 0 1 0 0 1 0 0 0 . 1 1 1 0 1 0 0 1 +# MOVM32pm-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 1 +# MOVM32pm-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# MOVM32pm-SAME: 0 1 0 0 1 0 0 0 . 1 1 1 0 1 0 0 1 +# MOVM32pm-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 1 1 +# MOVM32pm-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +name: MxMOVEM_MR_ARID +body: | + bb.0: + MOVM32pm 0, $a1, 1 + MOVM32pm -1, $a1, 3 + +... +--- # ARI +# -------------------+---+-----------+---+-----------+----------- +# F E D C B | A | 9 8 7 | 6 | 5 4 3 | 2 1 0 +# -------------------+---+-----------+---+-----------+----------- +# 0 1 0 0 1 | D | 0 0 1 | S | MODE | REG +# -------------------+---+-----------+---+-----------+----------- +# MOVM32jm-SAME: 0 1 0 0 1 0 0 0 . 1 1 0 1 0 0 0 1 +# MOVM32jm-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 1 +# --------------------------------------------------------------- +# MOVM32jm-SAME: 0 1 0 0 1 0 0 0 . 1 1 0 1 0 0 0 1 +# MOVM32jm-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 1 1 +# --------------------------------------------------------------- +# REGISTER LIST MASK +# --------------------------------------------------------------- +name: MxMOVEM_MR_ARI +body: | + bb.0: + MOVM32jm $a1, 1 + MOVM32jm $a1, 3 + +... Index: test/CodeGen/M680x0/OBJ/Data/Classes/MxMOVEM_RM.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Data/Classes/MxMOVEM_RM.mir @@ -0,0 +1,52 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj -o - \ +# RUN: | extract-section .text \ +# RUN: | FileCheck %s -check-prefixes=MOVM32mp,MOVM32mj + +#------------------------------------------------------------------------------ +# MxMOVEM_RM is used to load a list of registers sequentially from a memory +# location +#------------------------------------------------------------------------------ + +--- # ARID +# -------------------+---+-----------+---+-----------+----------- +# F E D C B | A | 9 8 7 | 6 | 5 4 3 | 2 1 0 +# -------------------+---+-----------+---+-----------+----------- +# 0 1 0 0 1 | D | 0 0 1 | S | MODE | REG +# -------------------+---+-----------+---+-----------+----------- +# REGISTER LIST MASK +# --------------------------------------------------------------- +# MOVM32mp: 0 1 0 0 1 1 0 0 . 1 1 1 0 1 0 0 1 +# MOVM32mp-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 1 +# MOVM32mp-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# MOVM32mp-SAME: 0 1 0 0 1 1 0 0 . 1 1 1 0 1 0 0 1 +# MOVM32mp-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 1 1 +# MOVM32mp-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +name: MxMOVEM_RM_ARID +body: | + bb.0: + MOVM32mp 1, 0, $a1 + MOVM32mp 3, -1, $a1 + +... +--- # ARI +# -------------------+---+-----------+---+-----------+----------- +# F E D C B | A | 9 8 7 | 6 | 5 4 3 | 2 1 0 +# -------------------+---+-----------+---+-----------+----------- +# 0 1 0 0 1 | D | 0 0 1 | S | MODE | REG +# -------------------+---+-----------+---+-----------+----------- +# MOVM32mj-SAME: 0 1 0 0 1 1 0 0 . 1 1 0 1 0 0 0 1 +# MOVM32mj-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 1 +# --------------------------------------------------------------- +# MOVM32mj-SAME: 0 1 0 0 1 1 0 0 . 1 1 0 1 0 0 0 1 +# MOVM32mj-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 1 1 +# --------------------------------------------------------------- +# REGISTER LIST MASK +# --------------------------------------------------------------- +name: MxMOVEM_RM_ARI +body: | + bb.0: + MOVM32mj 1, $a1 + MOVM32mj 3, $a1 + +... Index: test/CodeGen/M680x0/OBJ/Data/Classes/MxMoveCCR.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Data/Classes/MxMoveCCR.mir @@ -0,0 +1,34 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj -o - \ +# RUN: | extract-section .text \ +# RUN: | FileCheck %s -check-prefixes=MOV16cd,MOV16dc + +#------------------------------------------------------------------------------ +# MxMoveToCCR and MxMoveFromCCR load/store condition flag register +#------------------------------------------------------------------------------ + +--- # To CCR +# ---------------------------------------+-----------+----------- +# F E D C B A 9 8 7 6 | 5 4 3 | 2 1 0 +# ---------------------------------------+-----------+----------- +# 0 1 0 0 0 1 0 0 1 1 | MODE | REG +# ---------------------------------------+-----------+----------- +# MOV16cd: 0 1 0 0 0 1 0 0 . 1 1 0 0 0 0 0 1 +name: MxMoveToCCR +body: | + bb.0: + $ccr = MOV16cd $wd1, implicit-def $ccr + +... +--- # From CCR +# ---------------------------------------+-----------+----------- +# F E D C B A 9 8 7 6 | 5 4 3 | 2 1 0 +# ---------------------------------------+-----------+----------- +# 0 1 0 0 0 0 1 0 1 1 | MODE | REG +# ---------------------------------------+-----------+----------- +# MOV16cd-SAME: 0 1 0 0 0 0 1 0 . 1 1 0 0 0 0 0 1 +name: MxMoveFromCCR +body: | + bb.0: + $wd1 = MOV16dc $ccr, implicit $ccr + +... Index: test/CodeGen/M680x0/OBJ/Data/Classes/MxMove_MI.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Data/Classes/MxMove_MI.mir @@ -0,0 +1,97 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj -o - \ +# RUN: | extract-section .text \ +# RUN: | FileCheck %s -check-prefixes=MOV8fi,MOV32fi,MOV8pi,MOV32pi,MOV8ji,MOV32ji + +#------------------------------------------------------------------------------ +# MxMove_MI is used for moving immediate to memory +#------------------------------------------------------------------------------ + +--- # ARII +# ---------------------------+-----------+-----------+----------- +# F E | D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# -------+-------+-----------+-----------+-----------+----------- +# | | DESTINATION | SOURCE +# 0 0 | SIZE | REG | MODE | MODE | REG +# -------+-------+-----------+-----------+-----------+----------- +# MOV8fi: 0 0 0 1 0 0 0 1 . 1 0 1 1 1 1 0 0 +# MOV8fi-SAME: 0 0 0 0 0 0 0 0 . 1 1 1 1 1 1 1 1 +# MOV8fi-SAME: 0 0 0 1 1 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# MOV8fi-SAME: 0 0 0 1 0 0 0 1 . 1 0 1 1 1 1 0 0 +# MOV8fi-SAME: 0 0 0 0 0 0 0 0 . 0 0 1 0 1 0 1 0 +# MOV8fi-SAME: 0 0 0 1 1 0 0 0 . 1 1 1 1 1 1 1 1 +# --------------------------------------------------------------- +# MOV32fi-SAME: 0 0 1 0 0 0 1 1 . 1 0 1 1 1 1 0 0 +# MOV32fi-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# MOV32fi-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# MOV32fi-SAME: 0 0 0 1 1 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# MOV32fi-SAME: 0 0 1 0 0 1 0 1 . 1 0 1 1 1 1 0 0 +# MOV32fi-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# MOV32fi-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# MOV32fi-SAME: 1 0 1 0 1 0 0 0 . 0 0 0 0 0 0 0 0 +# ---+-----------+---+-------+---+------------------------------- +# BRIEF DA | REG | L | SCALE | 0 | DISPLACEMENT +# ---+-----------+---+-------+---+------------------------------- +name: MxMove_MI_ARII +body: | + bb.0: + MOV8fi 0, $a0, $d1, -1, implicit-def $ccr + MOV8fi -1, $a0, $d1, 42, implicit-def $ccr + MOV32fi 0, $a1, $d1, -1, implicit-def $ccr + MOV32fi 0, $a2, $a2, 0, implicit-def $ccr + +... +--- # ARID +# ---------------------------+-----------+-----------+----------- +# F E | D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# -------+-------+-----------+-----------+-----------+----------- +# | | DESTINATION | SOURCE +# 0 0 | SIZE | REG | MODE | MODE | REG +# -------+-------+-----------+-----------+-----------+----------- +# MOV8pi-SAME: 0 0 0 1 0 0 0 1 . 0 1 1 1 1 1 0 0 +# MOV8pi-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# MOV8pi-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# MOV32pi-SAME: 0 0 1 0 0 0 1 1 . 0 1 1 1 1 1 0 0 +# MOV32pi-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# MOV32pi-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# MOV32pi-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# --------------------------------------------------------------- +# MOV32pi-SAME: 0 0 1 0 0 0 1 1 . 0 1 1 1 1 1 0 0 +# MOV32pi-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# MOV32pi-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# MOV32pi-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +name: MxMove_MI_ARID +body: | + bb.0: + MOV8pi 0, $a0, 0, implicit-def $ccr + MOV32pi -1, $a1, 0, implicit-def $ccr + MOV32pi -1, $a1, 0, implicit-def $ccr + +... +--- # ARI +# ---------------------------+-----------+-----------+----------- +# F E | D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# -------+-------+-----------+-----------+-----------+----------- +# | | DESTINATION | SOURCE +# 0 0 | SIZE | REG | MODE | MODE | REG +# -------+-------+-----------+-----------+-----------+----------- +# MOV8ji-SAME: 0 0 0 1 0 0 0 0 . 1 0 1 1 1 1 0 0 +# MOV8ji-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# MOV32ji-SAME: 0 0 1 0 0 0 1 0 . 1 0 1 1 1 1 0 0 +# MOV32ji-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# MOV32ji-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# MOV32ji-SAME: 0 0 1 0 0 0 1 0 . 1 0 1 1 1 1 0 0 +# MOV32ji-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# MOV32ji-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +name: MxMove_MI_ARI +body: | + bb.0: + MOV8ji $a0, 0, implicit-def $ccr + MOV32ji $a1, 0, implicit-def $ccr + MOV32ji $a1, 0, implicit-def $ccr + +... Index: test/CodeGen/M680x0/OBJ/Data/Classes/MxMove_MM.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Data/Classes/MxMove_MM.mir @@ -0,0 +1,212 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj -o - \ +# RUN: | extract-section .text \ +# RUN: | FileCheck %s -check-prefixes=MOV8jk,MOV32jk,MOV8jq,MOV32jq,MOV8ff,MOV32ff,\ +# RUN:MOV8pp,MOV32pp,MOV8jj,MOV32jj,MOV8oo,MOV32oo,MOV8ee,MOV32ee,MOV8bb,MOV32bb + +#------------------------------------------------------------------------------ +# MxMove_MM is used for moving data from memory to memory +#------------------------------------------------------------------------------ + +--- # ARI <- PCI +# ---------------------------+-----------+-----------+----------- +# F E | D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# -------+-------+-----------+-----------+-----------+----------- +# | | DESTINATION | SOURCE +# 0 0 | SIZE | REG | MODE | MODE | REG +# -------+-------+-----------+-----------+-----------+----------- +# MOV8jk: 0 0 0 1 0 0 0 0 . 1 0 1 1 1 0 1 1 +# MOV8jk-SAME: 0 0 0 1 1 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# MOV8jk-SAME: 0 0 0 1 0 0 0 0 . 1 0 1 1 1 0 1 1 +# MOV8jk-SAME: 0 0 0 1 1 0 0 0 . 1 1 1 1 1 1 1 1 +# --------------------------------------------------------------- +# MOV32jk-SAME: 0 0 1 0 0 0 0 0 . 1 0 1 1 1 0 1 1 +# MOV32jk-SAME: 0 0 0 1 1 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# MOV32jk-SAME: 0 0 1 0 0 0 1 0 . 1 0 1 1 1 0 1 1 +# MOV32jk-SAME: 1 0 1 0 1 0 0 0 . 0 0 0 0 0 0 0 0 +# ---+-----------+---+-------+---+------------------------------- +# BRIEF DA | REG | L | SCALE | 0 | DISPLACEMENT +# ---+-----------+---+-------+---+------------------------------- +name: MxMove_RM_ARI_PCI +body: | + bb.0: + MOV8jk $a0, 0, $d1, implicit-def $ccr + MOV8jk $a0, -1, $d1, implicit-def $ccr + MOV32jk $a0, 0, $d1, implicit-def $ccr + MOV32jk $a1, 0, $a2, implicit-def $ccr + +... +--- # ARI <- PCD +# ---------------------------+-----------+-----------+----------- +# F E | D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# -------+-------+-----------+-----------+-----------+----------- +# | | DESTINATION | SOURCE +# 0 0 | SIZE | REG | MODE | MODE | REG +# -------+-------+-----------+-----------+-----------+----------- +# MOV8jq-SAME: 0 0 0 1 0 0 0 0 . 1 0 1 1 1 0 1 0 +# MOV8jq-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# MOV32jq-SAME: 0 0 1 0 0 0 0 0 . 1 0 1 1 1 0 1 0 +# MOV32jq-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# --------------------------------------------------------------- +# MOV32jq-SAME: 0 0 1 0 0 0 0 0 . 1 0 1 1 1 0 1 0 +# MOV32jq-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +name: MxMove_RM_PCD +body: | + bb.0: + MOV8jq $a0, 0, implicit-def $ccr + MOV32jq $a0, -1, implicit-def $ccr + MOV32jq $a0, -1, implicit-def $ccr + +... +--- # ARII <- ARII +# ---------------------------+-----------+-----------+----------- +# F E | D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# -------+-------+-----------+-----------+-----------+----------- +# | | DESTINATION | SOURCE +# 0 0 | SIZE | REG | MODE | MODE | REG +# -------+-------+-----------+-----------+-----------+----------- +# MOV8ff: 0 0 0 1 0 0 0 1 . 1 0 1 1 0 0 0 0 +# MOV8ff-SAME: 0 0 0 1 1 0 0 0 . 0 0 0 0 0 0 0 0 +# MOV8ff-SAME: 0 0 0 1 1 0 0 0 . 1 1 1 1 1 1 1 1 +# --------------------------------------------------------------- +# MOV8ff-SAME: 0 0 0 1 0 0 0 1 . 1 0 1 1 0 0 0 0 +# MOV8ff-SAME: 0 0 0 1 1 0 0 0 . 1 1 1 1 1 1 1 1 +# MOV8ff-SAME: 0 0 0 1 1 0 0 0 . 1 1 1 1 1 1 1 1 +# --------------------------------------------------------------- +# MOV32ff-SAME: 0 0 1 0 0 0 1 1 . 1 0 1 1 0 0 0 1 +# MOV32ff-SAME: 0 0 0 1 1 0 0 0 . 0 0 0 0 0 0 0 0 +# MOV32ff-SAME: 0 0 0 1 1 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# MOV32ff-SAME: 0 0 1 0 0 1 0 1 . 1 0 1 1 0 0 1 0 +# MOV32ff-SAME: 1 0 1 0 1 0 0 0 . 0 0 1 0 1 0 1 0 +# MOV32ff-SAME: 1 0 1 0 1 0 0 0 . 0 0 0 0 0 0 0 0 +# ---+-----------+---+-------+---+------------------------------- +# BRIEF DA | REG | L | SCALE | 0 | DISPLACEMENT +# ---+-----------+---+-------+---+------------------------------- +name: MxMove_RM_ARII_ARII +body: | + bb.0: + MOV8ff -1, $a0, $d1, 0, $a0, $d1, implicit-def $ccr + MOV8ff -1, $a0, $d1, -1, $a0, $d1, implicit-def $ccr + MOV32ff 0, $a1, $d1, 0, $a1, $d1, implicit-def $ccr + MOV32ff 0, $a2, $a2, 42, $a2, $a2, implicit-def $ccr + +... +--- # ARID <- ARID +# ---------------------------+-----------+-----------+----------- +# F E | D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# -------+-------+-----------+-----------+-----------+----------- +# | | DESTINATION | SOURCE +# 0 0 | SIZE | REG | MODE | MODE | REG +# -------+-------+-----------+-----------+-----------+----------- +# MOV8pp-SAME: 0 0 0 1 0 0 0 1 . 0 1 1 0 1 0 0 0 +# MOV8pp-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# MOV8pp-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# MOV32pp-SAME: 0 0 1 0 0 0 1 1 . 0 1 1 0 1 0 0 1 +# MOV32pp-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# MOV32pp-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# MOV32pp-SAME: 0 0 1 0 0 0 1 1 . 0 1 1 0 1 0 0 1 +# MOV32pp-SAME: 0 0 0 0 0 0 0 0 . 0 0 1 0 1 0 1 0 +# MOV32pp-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +name: MxMove_RM_ARID +body: | + bb.0: + MOV8pp 0, $a0, 0, $a0, implicit-def $ccr + MOV32pp 0, $a1, -1, $a1, implicit-def $ccr + MOV32pp -1, $a1, 42, $a1, implicit-def $ccr + +... +--- # ARIPD <- ARIPD +# ---------------------------+-----------+-----------+----------- +# F E | D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# -------+-------+-----------+-----------+-----------+----------- +# | | DESTINATION | SOURCE +# 0 0 | SIZE | REG | MODE | MODE | REG +# -------+-------+-----------+-----------+-----------+----------- +# MOV8ee-SAME: 0 0 0 1 0 0 0 1 . 0 0 1 0 0 0 0 0 +# --------------------------------------------------------------- +# MOV32ee-SAME: 0 0 1 0 0 0 1 1 . 0 0 1 0 0 0 0 1 +# --------------------------------------------------------------- +# MOV32ee-SAME: 0 0 1 0 0 0 1 1 . 0 0 1 0 0 0 0 1 +name: MxMove_RM_ARIPD +body: | + bb.0: + MOV8ee $a0, $a0, implicit-def $ccr + MOV32ee $a1, $a1, implicit-def $ccr + MOV32ee $a1, $a1, implicit-def $ccr + +... +--- # ARIPI <- ARIPI +# ---------------------------+-----------+-----------+----------- +# F E | D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# -------+-------+-----------+-----------+-----------+----------- +# | | DESTINATION | SOURCE +# 0 0 | SIZE | REG | MODE | MODE | REG +# -------+-------+-----------+-----------+-----------+----------- +# MOV8oo-SAME: 0 0 0 1 0 0 0 0 . 1 1 0 1 1 0 0 0 +# --------------------------------------------------------------- +# MOV32oo-SAME: 0 0 1 0 0 0 1 0 . 1 1 0 1 1 0 0 1 +# --------------------------------------------------------------- +# MOV32oo-SAME: 0 0 1 0 0 0 1 0 . 1 1 0 1 1 0 0 1 +name: MxMove_RM_ARIPI +body: | + bb.0: + MOV8oo $a0, $a0, implicit-def $ccr + MOV32oo $a1, $a1, implicit-def $ccr + MOV32oo $a1, $a1, implicit-def $ccr + +... +--- # ARI <- ARI +# ---------------------------+-----------+-----------+----------- +# F E | D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# -------+-------+-----------+-----------+-----------+----------- +# | | DESTINATION | SOURCE +# 0 0 | SIZE | REG | MODE | MODE | REG +# -------+-------+-----------+-----------+-----------+----------- +# MOV8jj-SAME: 0 0 0 1 0 0 0 0 . 1 0 0 1 0 0 0 0 +# --------------------------------------------------------------- +# MOV32jj-SAME: 0 0 1 0 0 0 1 0 . 1 0 0 1 0 0 0 1 +# --------------------------------------------------------------- +# MOV32jj-SAME: 0 0 1 0 0 0 1 0 . 1 0 0 1 0 0 0 1 +name: MxMove_RM_ARI +body: | + bb.0: + MOV8jj $a0, $a0, implicit-def $ccr + MOV32jj $a1, $a1, implicit-def $ccr + MOV32jj $a1, $a1, implicit-def $ccr + +... +--- # ABS <- ABS +# ---------------------------+-----------+-----------+----------- +# F E | D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# -------+-------+-----------+-----------+-----------+----------- +# | | DESTINATION | SOURCE +# 0 0 | SIZE | REG | MODE | MODE | REG +# -------+-------+-----------+-----------+-----------+----------- +# MOV8bb-SAME: 0 0 0 1 0 0 1 1 . 1 1 1 1 1 0 0 1 +# MOV8bb-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# MOV8bb-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# MOV32bb-SAME: 0 0 1 0 0 0 1 1 . 1 1 1 1 1 0 0 1 +# MOV32bb-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# MOV32bb-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# MOV32bb-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# MOV32bb-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# --------------------------------------------------------------- +# MOV32bb-SAME: 0 0 1 0 0 0 1 1 . 1 1 1 1 1 0 0 1 +# MOV32bb-SAME: 0 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# MOV32bb-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# MOV32bb-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# MOV32bb-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +name: MxMove_RM_ABS +body: | + bb.0: + MOV8bb 0, -1, implicit-def $ccr + MOV32bb -1, 0, implicit-def $ccr + MOV32bb 0, 2147483647, implicit-def $ccr + +... Index: test/CodeGen/M680x0/OBJ/Data/Classes/MxMove_MR.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Data/Classes/MxMove_MR.mir @@ -0,0 +1,81 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj -o - \ +# RUN: | extract-section .text \ +# RUN: | FileCheck %s -check-prefixes=MOV8fd,MOV32fr,MOV8pd,MOV32pr,MOV8jd,MOV32jr + +#------------------------------------------------------------------------------ +# MxMove_MR is used for moving data from register to memory +#------------------------------------------------------------------------------ + +--- # ARII +# ---------------------------+-----------+-----------+----------- +# F E | D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# -------+-------+-----------+-----------+-----------+----------- +# | | DESTINATION | SOURCE +# 0 0 | SIZE | REG | MODE | MODE | REG +# -------+-------+-----------+-----------+-----------+----------- +# MOV8fd: 0 0 0 1 0 0 0 1 . 1 0 0 0 0 0 0 0 +# MOV8fd-SAME: 0 0 0 1 1 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# MOV8fd-SAME: 0 0 0 1 0 0 0 1 . 1 0 0 0 0 0 0 0 +# MOV8fd-SAME: 0 0 0 1 1 0 0 0 . 1 1 1 1 1 1 1 1 +# --------------------------------------------------------------- +# MOV32fr-SAME: 0 0 1 0 0 0 1 1 . 1 0 0 0 0 0 0 0 +# MOV32fr-SAME: 0 0 0 1 1 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# MOV32fr-SAME: 0 0 1 0 0 1 0 1 . 1 0 0 0 0 0 0 1 +# MOV32fr-SAME: 1 0 1 0 1 0 0 0 . 0 0 0 0 0 0 0 0 +# ---+-----------+---+-------+---+------------------------------- +# BRIEF DA | REG | L | SCALE | 0 | DISPLACEMENT +# ---+-----------+---+-------+---+------------------------------- +name: MxMove_MR_ARII +body: | + bb.0: + MOV8fd 0, $a0, $d1, $bd0, implicit-def $ccr + MOV8fd -1, $a0, $d1, $bd0, implicit-def $ccr + MOV32fr 0, $a1, $d1, $d0, implicit-def $ccr + MOV32fr 0, $a2, $a2, $d1, implicit-def $ccr + +... +--- # ARID +# ---------------------------+-----------+-----------+----------- +# F E | D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# -------+-------+-----------+-----------+-----------+----------- +# | | DESTINATION | SOURCE +# 0 0 | SIZE | REG | MODE | MODE | REG +# -------+-------+-----------+-----------+-----------+----------- +# MOV8pd-SAME: 0 0 0 1 0 0 0 1 . 0 1 0 0 0 0 0 0 +# MOV8pd-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# MOV32pr-SAME: 0 0 1 0 0 0 1 1 . 0 1 0 0 0 0 0 0 +# MOV32pr-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# --------------------------------------------------------------- +# MOV32pr-SAME: 0 0 1 0 0 0 1 1 . 0 1 0 0 1 0 0 0 +# MOV32pr-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +name: MxMove_MR_ARID +body: | + bb.0: + MOV8pd 0, $a0, $bd0, implicit-def $ccr + MOV32pr -1, $a1, $d0, implicit-def $ccr + MOV32pr -1, $a1, $a0, implicit-def $ccr + +... +--- # ARI +# ---------------------------+-----------+-----------+----------- +# F E | D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# -------+-------+-----------+-----------+-----------+----------- +# | | DESTINATION | SOURCE +# 0 0 | SIZE | REG | MODE | MODE | REG +# -------+-------+-----------+-----------+-----------+----------- +# MOV8jd-SAME: 0 0 0 1 0 0 0 0 . 1 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# MOV32jr-SAME: 0 0 1 0 0 0 1 0 . 1 0 0 0 0 0 1 1 +# --------------------------------------------------------------- +# MOV32jr-SAME: 0 0 1 0 0 0 1 0 . 1 0 0 0 1 1 0 0 +name: MxMove_MR_ARI +body: | + bb.0: + MOV8jd $a0, $bd0, implicit-def $ccr + MOV32jr $a1, $d3, implicit-def $ccr + MOV32jr $a1, $a4, implicit-def $ccr + +... Index: test/CodeGen/M680x0/OBJ/Data/Classes/MxMove_RI.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Data/Classes/MxMove_RI.mir @@ -0,0 +1,31 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj -o - \ +# RUN: | extract-section .text \ +# RUN: | FileCheck %s -check-prefixes=MOV8di,MOV32ri + +#------------------------------------------------------------------------------ +# MxMove_RI is used for moving immediate to register +#------------------------------------------------------------------------------ + +# ---------------------------+-----------+-----------+----------- +# F E | D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# -------+-------+-----------+-----------+-----------+----------- +# | | DESTINATION | SOURCE +# 0 0 | SIZE | REG | MODE | MODE | REG +# -------+-------+-----------+-----------+-----------+----------- +# MOV8di: 0 0 0 1 0 0 0 0 . 0 0 1 1 1 1 0 0 +# MOV8di-SAME: 0 0 0 0 0 0 0 0 . 1 1 1 1 1 1 1 1 +# --------------------------------------------------------------- +# MOV32ri-SAME: 0 0 1 0 0 0 1 0 . 0 1 1 1 1 1 0 0 +# MOV32ri-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# MOV32ri-SAME: 0 0 0 0 0 0 0 0 . 0 0 1 0 1 0 1 0 +# --------------------------------------------------------------- +# MOV32ri-SAME: 0 0 1 0 0 0 1 0 . 0 1 1 1 1 1 0 0 +# MOV32ri-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# MOV32ri-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +name: MxMove_RI +body: | + bb.0: + $bd0 = MOV8di -1, implicit-def $ccr + $a1 = MOV32ri 42, implicit-def $ccr + $a1 = MOV32ri -1, implicit-def $ccr + Index: test/CodeGen/M680x0/OBJ/Data/Classes/MxMove_RM.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Data/Classes/MxMove_RM.mir @@ -0,0 +1,200 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj -o - \ +# RUN: | extract-section .text \ +# RUN: | FileCheck %s -check-prefixes=MOV8dk,MOV32rk,MOV8dq,MOV32rq,MOV8df,MOV32rf,\ +# RUN:MOV8dp,MOV32rp,MOV8dj,MOV32rj,MOV8do,MOV32ro,MOV8de,MOV32re,MOV8db,MOV32rb + +#------------------------------------------------------------------------------ +# MxMove_RM is used for moving data from memory to register +#------------------------------------------------------------------------------ + +--- # PCI +# ---------------------------+-----------+-----------+----------- +# F E | D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# -------+-------+-----------+-----------+-----------+----------- +# | | DESTINATION | SOURCE +# 0 0 | SIZE | REG | MODE | MODE | REG +# -------+-------+-----------+-----------+-----------+----------- +# MOV8dk: 0 0 0 1 0 0 0 0 . 0 0 1 1 1 0 1 1 +# MOV8dk-SAME: 0 0 0 1 1 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# MOV8dk-SAME: 0 0 0 1 0 0 0 0 . 0 0 1 1 1 0 1 1 +# MOV8dk-SAME: 0 0 0 1 1 0 0 0 . 1 1 1 1 1 1 1 1 +# --------------------------------------------------------------- +# MOV32rk-SAME: 0 0 1 0 0 0 0 0 . 0 0 1 1 1 0 1 1 +# MOV32rk-SAME: 0 0 0 1 1 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# MOV32rk-SAME: 0 0 1 0 0 0 1 0 . 0 0 1 1 1 0 1 1 +# MOV32rk-SAME: 1 0 1 0 1 0 0 0 . 0 0 0 0 0 0 0 0 +# ---+-----------+---+-------+---+------------------------------- +# BRIEF DA | REG | L | SCALE | 0 | DISPLACEMENT +# ---+-----------+---+-------+---+------------------------------- +name: MxMove_RM_PCI +body: | + bb.0: + $bd0 = MOV8dk 0, $d1, implicit-def $ccr + $bd0 = MOV8dk -1, $d1, implicit-def $ccr + $d0 = MOV32rk 0, $d1, implicit-def $ccr + $d1 = MOV32rk 0, $a2, implicit-def $ccr + +... +--- # PCD +# ---------------------------+-----------+-----------+----------- +# F E | D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# -------+-------+-----------+-----------+-----------+----------- +# | | DESTINATION | SOURCE +# 0 0 | SIZE | REG | MODE | MODE | REG +# -------+-------+-----------+-----------+-----------+----------- +# MOV8dq-SAME: 0 0 0 1 0 0 0 0 . 0 0 1 1 1 0 1 0 +# MOV8dq-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# MOV32rq-SAME: 0 0 1 0 0 0 0 0 . 0 0 1 1 1 0 1 0 +# MOV32rq-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# --------------------------------------------------------------- +# MOV32rq-SAME: 0 0 1 0 0 0 0 0 . 0 1 1 1 1 0 1 0 +# MOV32rq-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +name: MxMove_RM_PCD +body: | + bb.0: + $bd0 = MOV8dq 0, implicit-def $ccr + $d0 = MOV32rq -1, implicit-def $ccr + $a0 = MOV32rq -1, implicit-def $ccr + +... +--- # ARII +# ---------------------------+-----------+-----------+----------- +# F E | D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# -------+-------+-----------+-----------+-----------+----------- +# | | DESTINATION | SOURCE +# 0 0 | SIZE | REG | MODE | MODE | REG +# -------+-------+-----------+-----------+-----------+----------- +# MOV8df: 0 0 0 1 0 0 0 0 . 0 0 1 1 0 0 0 0 +# MOV8df-SAME: 0 0 0 1 1 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# MOV8df-SAME: 0 0 0 1 0 0 0 0 . 0 0 1 1 0 0 0 0 +# MOV8df-SAME: 0 0 0 1 1 0 0 0 . 1 1 1 1 1 1 1 1 +# --------------------------------------------------------------- +# MOV32rf-SAME: 0 0 1 0 0 0 0 0 . 0 0 1 1 0 0 0 1 +# MOV32rf-SAME: 0 0 0 1 1 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# MOV32rf-SAME: 0 0 1 0 0 0 1 0 . 0 0 1 1 0 0 1 0 +# MOV32rf-SAME: 1 0 1 0 1 0 0 0 . 0 0 0 0 0 0 0 0 +# ---+-----------+---+-------+---+------------------------------- +# BRIEF DA | REG | L | SCALE | 0 | DISPLACEMENT +# ---+-----------+---+-------+---+------------------------------- +name: MxMove_RM_ARII +body: | + bb.0: + $bd0 = MOV8df 0, $a0, $d1, implicit-def $ccr + $bd0 = MOV8df -1, $a0, $d1, implicit-def $ccr + $d0 = MOV32rf 0, $a1, $d1, implicit-def $ccr + $d1 = MOV32rf 0, $a2, $a2, implicit-def $ccr + +... +--- # ARID +# ---------------------------+-----------+-----------+----------- +# F E | D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# -------+-------+-----------+-----------+-----------+----------- +# | | DESTINATION | SOURCE +# 0 0 | SIZE | REG | MODE | MODE | REG +# -------+-------+-----------+-----------+-----------+----------- +# MOV8dp-SAME: 0 0 0 1 0 0 0 0 . 0 0 1 0 1 0 0 0 +# MOV8dp-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# MOV32rp-SAME: 0 0 1 0 0 0 0 0 . 0 0 1 0 1 0 0 1 +# MOV32rp-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +# --------------------------------------------------------------- +# MOV32rp-SAME: 0 0 1 0 0 0 0 0 . 0 1 1 0 1 0 0 1 +# MOV32rp-SAME: 1 1 1 1 1 1 1 1 . 1 1 1 1 1 1 1 1 +name: MxMove_RM_ARID +body: | + bb.0: + $bd0 = MOV8dp 0, $a0, implicit-def $ccr + $d0 = MOV32rp -1, $a1, implicit-def $ccr + $a0 = MOV32rp -1, $a1, implicit-def $ccr + +... +--- # ARIPD +# ---------------------------+-----------+-----------+----------- +# F E | D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# -------+-------+-----------+-----------+-----------+----------- +# | | DESTINATION | SOURCE +# 0 0 | SIZE | REG | MODE | MODE | REG +# -------+-------+-----------+-----------+-----------+----------- +# MOV8de-SAME: 0 0 0 1 0 0 0 0 . 0 0 1 0 0 0 0 0 +# --------------------------------------------------------------- +# MOV32re-SAME: 0 0 1 0 0 1 1 0 . 0 0 1 0 0 0 0 1 +# --------------------------------------------------------------- +# MOV32re-SAME: 0 0 1 0 1 0 0 0 . 0 1 1 0 0 0 0 1 +name: MxMove_RM_ARIPD +body: | + bb.0: + $bd0 = MOV8de $a0, implicit-def $ccr + $d3 = MOV32re $a1, implicit-def $ccr + $a4 = MOV32re $a1, implicit-def $ccr + +... +--- # ARIPI +# ---------------------------+-----------+-----------+----------- +# F E | D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# -------+-------+-----------+-----------+-----------+----------- +# | | DESTINATION | SOURCE +# 0 0 | SIZE | REG | MODE | MODE | REG +# -------+-------+-----------+-----------+-----------+----------- +# MOV8do-SAME: 0 0 0 1 0 0 0 0 . 0 0 0 1 1 0 0 0 +# --------------------------------------------------------------- +# MOV32ro-SAME: 0 0 1 0 0 1 1 0 . 0 0 0 1 1 0 0 1 +# --------------------------------------------------------------- +# MOV32ro-SAME: 0 0 1 0 1 0 0 0 . 0 1 0 1 1 0 0 1 +name: MxMove_RM_ARIPI +body: | + bb.0: + $bd0 = MOV8do $a0, implicit-def $ccr + $d3 = MOV32ro $a1, implicit-def $ccr + $a4 = MOV32ro $a1, implicit-def $ccr + +... +--- # ARI +# ---------------------------+-----------+-----------+----------- +# F E | D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# -------+-------+-----------+-----------+-----------+----------- +# | | DESTINATION | SOURCE +# 0 0 | SIZE | REG | MODE | MODE | REG +# -------+-------+-----------+-----------+-----------+----------- +# MOV8dj-SAME: 0 0 0 1 0 0 0 0 . 0 0 0 1 0 0 0 0 +# --------------------------------------------------------------- +# MOV32rj-SAME: 0 0 1 0 0 1 1 0 . 0 0 0 1 0 0 0 1 +# --------------------------------------------------------------- +# MOV32rj-SAME: 0 0 1 0 1 0 0 0 . 0 1 0 1 0 0 0 1 +name: MxMove_RM_ARI +body: | + bb.0: + $bd0 = MOV8dj $a0, implicit-def $ccr + $d3 = MOV32rj $a1, implicit-def $ccr + $a4 = MOV32rj $a1, implicit-def $ccr + +... +--- # ABS +# ---------------------------+-----------+-----------+----------- +# F E | D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# -------+-------+-----------+-----------+-----------+----------- +# | | DESTINATION | SOURCE +# 0 0 | SIZE | REG | MODE | MODE | REG +# -------+-------+-----------+-----------+-----------+----------- +# MOV8db-SAME: 0 0 0 1 0 0 0 0 . 0 0 1 1 1 0 0 1 +# MOV8db-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# MOV32rb-SAME: 0 0 1 0 0 1 1 0 . 0 0 1 1 1 0 0 1 +# MOV32rb-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# MOV32rb-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# --------------------------------------------------------------- +# MOV32rb-SAME: 0 0 1 0 1 0 0 0 . 0 1 1 1 1 0 0 1 +# MOV32rb-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +# MOV32rb-SAME: 0 0 0 0 0 0 0 0 . 0 0 0 0 0 0 0 0 +name: MxMove_RM_ABS +body: | + bb.0: + $bd0 = MOV8db 0, implicit-def $ccr + $d3 = MOV32rb 0, implicit-def $ccr + $a4 = MOV32rb 0, implicit-def $ccr + +... Index: test/CodeGen/M680x0/OBJ/Data/Classes/MxMove_RR.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Data/Classes/MxMove_RR.mir @@ -0,0 +1,30 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj -o - \ +# RUN: | extract-section .text \ +# RUN: | FileCheck %s -check-prefixes=MOV8dd,MOV16ra,MOV32rr + +#------------------------------------------------------------------------------ +# MxMove_RR moves data from register to register +#------------------------------------------------------------------------------ + + +# ---------------------------+-----------+-----------+----------- +# F E | D C | B A 9 | 8 7 6 | 5 4 3 | 2 1 0 +# -------+-------+-----------+-----------+-----------+----------- +# | | DESTINATION | SOURCE +# 0 0 | SIZE | REG | MODE | MODE | REG +# -------+-------+-----------+-----------+-----------+----------- +# MOV8dd: 0 0 0 1 0 0 1 0 . 0 0 0 0 0 0 0 0 +# MOV16rr-SAME: 0 0 1 1 0 1 1 0 . 0 0 0 0 1 0 1 0 +# MOV16rr-SAME: 0 0 1 1 1 1 0 0 . 0 1 0 0 1 0 1 0 +# MOV32ra-SAME: 0 0 1 0 1 1 0 0 . 0 0 0 0 1 0 1 0 +# MOV32rr-SAME: 0 0 1 0 0 0 1 0 . 0 0 0 0 0 0 1 0 +# MOV32rr-SAME: 0 0 1 0 0 0 1 0 . 0 1 0 0 1 0 1 0 +name: MxMove_RR +body: | + bb.0: + $bd1 = MOV8dd $bd0, implicit-def $ccr + $wd3 = MOV16ra $wa2, implicit-def $ccr + $wa6 = MOV16ra $wa2, implicit-def $ccr + $wd1 = MOV16ra $wa2, implicit-def $ccr + $d1 = MOV32rr $d2, implicit-def $ccr + $a1 = MOV32rr $a2, implicit-def $ccr Index: test/CodeGen/M680x0/OBJ/README.md =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/README.md @@ -0,0 +1,14 @@ +# OBJECT CODE TESTS + +## Purpose +These test snippets are to test object code generation features, specifically +lowering of MxBean encoding, Relocations and specific commands encoding classes. + +### MxBean +TODO + +### Relocations +TODO + +### Encoding Classes +TODO Index: test/CodeGen/M680x0/OBJ/Relaxations/branch.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Relaxations/branch.mir @@ -0,0 +1,116 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj \ +# RUN: -code-model=small -relocation-model=pic -o - \ +# RUN: | extract-section .text -h \ +# RUN: | FileCheck %s -check-prefixes=x00 + +#------------------------------------------------------------------------------ +# Test branch relaxation. By default codegen choses smallest branch instruction, +# during object code generation it might get clear that offset does not fit and +# bigger instruction is required. +#------------------------------------------------------------------------------ + + +--- # TIGHT +# The offset for the small branch is from the PC value, which points to the +# next instruction, because there is no extension word here. +# 000 x00: 6078 +# 002 x00-SAME: 23f9 0000 0000 0000 0000 +# 00C x00-SAME: 23f9 0000 0000 0000 0000 +# 016 x00-SAME: 23f9 0000 0000 0000 0000 +# 020 x00-SAME: 23f9 0000 0000 0000 0000 +# 02A x00-SAME: 23f9 0000 0000 0000 0000 +# 034 x00-SAME: 23f9 0000 0000 0000 0000 +# 03E x00-SAME: 23f9 0000 0000 0000 0000 +# 048 x00-SAME: 23f9 0000 0000 0000 0000 +# 052 x00-SAME: 23f9 0000 0000 0000 0000 +# 05C x00-SAME: 23f9 0000 0000 0000 0000 +# 066 x00-SAME: 23f9 0000 0000 0000 0000 +# 070 x00-SAME: 23f9 0000 0000 0000 0000 +# 07A x00-SAME: d0bc 0000 0000 +# 080 x00-SAME: 4e75 +# 082 x00-SAME: 4e71 +name: TIGHT +body: | + bb.0: + successors: %bb.1,%bb.2 + + BRA8 %bb.2 + bb.1: + MOV32bb 0, 0, implicit-def $ccr + MOV32bb 0, 0, implicit-def $ccr + MOV32bb 0, 0, implicit-def $ccr + MOV32bb 0, 0, implicit-def $ccr + MOV32bb 0, 0, implicit-def $ccr + MOV32bb 0, 0, implicit-def $ccr + MOV32bb 0, 0, implicit-def $ccr + MOV32bb 0, 0, implicit-def $ccr + MOV32bb 0, 0, implicit-def $ccr + MOV32bb 0, 0, implicit-def $ccr + MOV32bb 0, 0, implicit-def $ccr + MOV32bb 0, 0, implicit-def $ccr + bb.2: + $d0 = ADD32ri $d0, 0, implicit-def $ccr + RET 0, $d0 + +... +--- # RELAXED +# 084 x00-SAME 6000 0084 +# 088 x00-SAME: 23f9 0000 0000 0000 0000 +# 092 x00-SAME: 23f9 0000 0000 0000 0000 +# 09C x00-SAME: 23f9 0000 0000 0000 0000 +# 0A6 x00-SAME: 23f9 0000 0000 0000 0000 +# 0B0 x00-SAME: 23f9 0000 0000 0000 0000 +# 0BA x00-SAME: 23f9 0000 0000 0000 0000 +# 0C4 x00-SAME: 23f9 0000 0000 0000 0000 +# 0CE x00-SAME: 23f9 0000 0000 0000 0000 +# 0D8 x00-SAME: 23f9 0000 0000 0000 0000 +# 0E2 x00-SAME: 23f9 0000 0000 0000 0000 +# 0EC x00-SAME: 23f9 0000 0000 0000 0000 +# 0F6 x00-SAME: 23f9 0000 0000 0000 0000 +# 100 x00-SAME: 23f9 0000 0000 0000 0000 +# 10A x00-SAME: d0bc 0000 0000 +# 110 x00-SAME: 4e75 +# 112 x00-SAME: 4e71 +name: RELAXED +body: | + bb.0: + successors: %bb.1,%bb.2 + + BRA8 %bb.2 + bb.1: + MOV32bb 0, 0, implicit-def $ccr + MOV32bb 0, 0, implicit-def $ccr + MOV32bb 0, 0, implicit-def $ccr + MOV32bb 0, 0, implicit-def $ccr + MOV32bb 0, 0, implicit-def $ccr + MOV32bb 0, 0, implicit-def $ccr + MOV32bb 0, 0, implicit-def $ccr + MOV32bb 0, 0, implicit-def $ccr + MOV32bb 0, 0, implicit-def $ccr + MOV32bb 0, 0, implicit-def $ccr + MOV32bb 0, 0, implicit-def $ccr + MOV32bb 0, 0, implicit-def $ccr + MOV32bb 0, 0, implicit-def $ccr + bb.2: + $d0 = ADD32ri $d0, 0, implicit-def $ccr + RET 0, $d0 + +... +--- # ZERO +# Because of the way M680x0 encodes branches it is not possible to encode 0 +# offset with the smallest insruction(0 in the offset field means exension word +# is used) thus we switch to the wider instruction. +# 114 x00-SAME: 6000 0002 +# 118 x00-SAME: d0bc 0000 0000 +# 11E x00-SAME: 4e75 +name: ZERO +body: | + bb.0: + successors: %bb.1 + + BRA8 %bb.1 + bb.1: + $d0 = ADD32ri $d0, 0, implicit-def $ccr + RET 0, $d0 + +... Index: test/CodeGen/M680x0/OBJ/Relocations/data-abs.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Relocations/data-abs.mir @@ -0,0 +1,26 @@ + +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj \ +# RUN: -code-model=small -relocation-model=pic -o - \ +# RUN: | llvm-readobj -relocations -elf-output-style=GNU \ +# RUN: | FileCheck %s -check-prefixes=x00 + +#------------------------------------------------------------------------------ +# Test ABS relocation +#------------------------------------------------------------------------------ + +--- | + + @dst = external global i32 + + define void @DATA() { entry: ret void } + +... +--- # DATA +# Offset Info Type Sym. Value Sym S Addend +# x00: 00000002 {{[0-9a-f]+}} R_M680x0_32 {{[0-9]*}} dst + 0 +name: DATA +body: | + bb.0: + $d0 = MOV32rb @dst, implicit-def $ccr + +... Index: test/CodeGen/M680x0/OBJ/Relocations/data-gotoff.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Relocations/data-gotoff.mir @@ -0,0 +1,29 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj \ +# RUN: -code-model=small -relocation-model=pic -o - \ +# RUN: | llvm-readobj -relocations -elf-output-style=GNU \ +# RUN: | FileCheck %s -check-prefixes=x00 + +#------------------------------------------------------------------------------ +# Test GOTOFF relocation +#------------------------------------------------------------------------------ + +--- | + + @dst = external global i32 + + define void @DATA() { entry: ret void } + +... +--- # DATA +# Offset Info Type Sym. Value Sym S Addend +# x00: 00000002 {{[0-9a-f]+}} R_M680x0_GOTPCREL16 {{[0-9]*}} _GLOBAL_OFFSET_TABLE_ + 0 +# x00: 00000007 {{[0-9a-f]+}} R_M680x0_GOTOFF8 {{[0-9]*}} dst + 0 +# x00: 0000000a {{[0-9a-f]+}} R_M680x0_GOTOFF16 {{[0-9]*}} dst + 0 +name: DATA +body: | + bb.0: + $a5 = LEA32q target-flags(M680x0-gotpcrel) &_GLOBAL_OFFSET_TABLE_ + $d0 = MOV32rf target-flags(M680x0-gotoff) @dst, $a5, $d0, implicit-def $ccr + $d0 = MOV32rp target-flags(M680x0-gotoff) @dst, $a5, implicit-def $ccr + +... Index: test/CodeGen/M680x0/OBJ/Relocations/data-gotpcrel.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Relocations/data-gotpcrel.mir @@ -0,0 +1,27 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj \ +# RUN: -code-model=small -relocation-model=pic -o - \ +# RUN: | llvm-readobj -relocations -elf-output-style=GNU \ +# RUN: | FileCheck %s -check-prefixes=x00 + +#------------------------------------------------------------------------------ +# Test GOTPCREL relocation +#------------------------------------------------------------------------------ + +--- | + + @dst = external global i32 + + define void @DATA() { entry: ret void } + +... +--- # DATA +# Offset Info Type Sym. Value Sym S Addend +# x00: 00000003 {{[0-9]+}} R_M680x0_GOTPCREL8 {{[0-9]*}} dst + 1 +# x00: 00000006 {{[0-9]+}} R_M680x0_GOTPCREL16 {{[0-9]*}} dst + 0 +name: DATA +body: | + bb.0: + $a0 = MOV32rk target-flags(M680x0-gotpcrel) @dst, $d0, implicit-def $ccr + $a0 = MOV32rq target-flags(M680x0-gotpcrel) @dst, implicit-def $ccr + +... Index: test/CodeGen/M680x0/OBJ/Relocations/data-pc-rel.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Relocations/data-pc-rel.mir @@ -0,0 +1,30 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj \ +# RUN: -code-model=small -relocation-model=pic -o - \ +# RUN: | llvm-readobj -relocations -elf-output-style=GNU \ +# RUN: | FileCheck %s -check-prefixes=x00 + +#------------------------------------------------------------------------------ +# Tests PC-Relative data relocations +#------------------------------------------------------------------------------ + +--- | + + @dst = external global i32 + + define void @DATA() { entry: ret void } + +... +--- # DATA +# Offset Info Type Sym. Value Sym S Addend +# x00: 00000003 {{[0-9]+}} R_M680x0_PC8 {{[0-9]*}} dst + 1 +# x00: 00000006 {{[0-9]+}} R_M680x0_PC16 {{[0-9]*}} dst + 0 +# No need for relocation here +# x00-NOT: 0000000a {{[0-9]+}} R_M680x0_PC16 0 +name: DATA +body: | + bb.0: + $a0 = MOV32rk @dst, $a0, implicit-def $ccr + $a0 = MOV32rq @dst, implicit-def $ccr + $a0 = MOV32rq 0, implicit-def $ccr + +... Index: test/CodeGen/M680x0/OBJ/Relocations/text-plt.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/Relocations/text-plt.mir @@ -0,0 +1,24 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj \ +# RUN: -code-model=small -relocation-model=pic -o - \ +# RUN: | llvm-readobj -relocations -elf-output-style=GNU \ +# RUN: | FileCheck %s -check-prefixes=x00 + +#------------------------------------------------------------------------------ +# Test PLT relocation +#------------------------------------------------------------------------------ + +--- | + + declare void @TARGET() + define void @TEXT() { entry: ret void } + +... +--- # TEXT +# Offset Info Type Sym. Value Sym S Addend +# x00: 00000002 {{[0-9a-f]+}} R_M680x0_PLT16 {{[0-9]*}} TARGET + 0 +name: TEXT +body: | + bb.0: + CALLq target-flags(M680x0-plt) @TARGET + +... Index: test/CodeGen/M680x0/OBJ/ShiftRotate/Classes/MxSR_DD.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/ShiftRotate/Classes/MxSR_DD.mir @@ -0,0 +1,41 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj -o - \ +# RUN: | extract-section .text \ +# RUN: | FileCheck %s -check-prefixes=SHL8dd,SHL32dd,LSR8dd,LSR32dd,ASR8dd,ASR32dd,\ +# RUN:ROL8dd,ROL32dd,ROR8dd,ROR32dd + +#------------------------------------------------------------------------------ +# MxSR_DD encodes shift or rotate instructions; shift count is in register +#------------------------------------------------------------------------------ + +# ---------------+-----------+---+-------+---+-------+----------- +# F E D C | B A 9 | 8 | 7 6 | 5 | 4 3 | 2 1 0 +# ---------------+-----------+---+-------+---+-------+----------- +# 1 1 1 0 | Dx | D | SIZE | 1 | OP | Dy +# ---------------+-----------+---+-------+---+-------+----------- +# SHL8dd: 1 1 1 0 0 0 0 1 . 0 0 1 0 1 0 0 1 +# SHL32dd-SAME: 1 1 1 0 0 0 1 1 . 1 0 1 0 1 0 1 0 +# ---------------+-----------+---+-------+---+-------+----------- +# LSR8dd-SAME: 1 1 1 0 0 1 0 0 . 0 0 1 0 1 0 1 1 +# LSR32dd-SAME: 1 1 1 0 0 1 1 0 . 1 0 1 0 1 1 0 0 +# ---------------+-----------+---+-------+---+-------+----------- +# ASR8dd-SAME: 1 1 1 0 1 0 0 0 . 0 0 1 0 0 1 0 1 +# ASR32dd-SAME: 1 1 1 0 1 0 1 0 . 1 0 1 0 0 1 1 0 +# ---------------+-----------+---+-------+---+-------+----------- +# ROL8dd-SAME: 1 1 1 0 1 1 0 1 . 0 0 1 1 1 1 1 1 +# ROL32dd-SAME: 1 1 1 0 1 1 1 1 . 1 0 1 1 1 0 0 1 +# ---------------+-----------+---+-------+---+-------+----------- +# ROR8dd-SAME: 1 1 1 0 0 0 0 0 . 0 0 1 1 1 0 0 1 +# ROR32dd-SAME: 1 1 1 0 0 0 0 0 . 1 0 1 1 1 0 0 1 +name: MxSR_DD +body: | + bb.0: + $bd1 = SHL8dd $bd1, $bd0, implicit-def $ccr + $d2 = SHL32dd $d2, $d1, implicit-def $ccr + $bd3 = LSR8dd $bd3, $bd2, implicit-def $ccr + $d4 = LSR32dd $d4, $d3, implicit-def $ccr + $bd5 = ASR8dd $bd5, $bd4, implicit-def $ccr + $d6 = ASR32dd $d6, $d5, implicit-def $ccr + $bd7 = ROL8dd $bd7, $bd6, implicit-def $ccr + $d1 = ROL32dd $d1, $d7, implicit-def $ccr + $bd1 = ROR8dd $bd1, $bd0, implicit-def $ccr + $d1 = ROR32dd $d1, $d0, implicit-def $ccr Index: test/CodeGen/M680x0/OBJ/ShiftRotate/Classes/MxSR_DI.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/OBJ/ShiftRotate/Classes/MxSR_DI.mir @@ -0,0 +1,56 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=obj -o - \ +# RUN: | extract-section .text \ +# RUN: | FileCheck %s -check-prefixes=SHL8di,SHL32di,LSR8di,LSR32di,ASR8di,ASR32di,\ +# RUN:ROL8di,ROL32di,ROR8di,ROR32di + +#------------------------------------------------------------------------------ +# MxSR_DI encodes shift or rotate instructions; shift count is encoded into +# the instruction +#------------------------------------------------------------------------------ + +# ---------------+-----------+---+-------+---+-------+----------- +# F E D C | B A 9 | 8 | 7 6 | 5 | 4 3 | 2 1 0 +# ---------------+-----------+---+-------+---+-------+----------- +# 1 1 1 0 | Dx | D | SIZE | 0 | OP | Dy +# ---------------+-----------+---+-------+---+-------+----------- +# SHL8di: 1 1 1 0 0 0 1 1 . 0 0 0 0 1 0 0 1 +# SHL32di-SAME: 1 1 1 0 0 0 1 1 . 1 0 0 0 1 0 0 1 +# ---------------+-----------+---+-------+---+-------+----------- +# LSR8di-SAME: 1 1 1 0 0 0 1 0 . 0 0 0 0 1 0 0 1 +# LSR32di-SAME: 1 1 1 0 0 0 1 0 . 1 0 0 0 1 0 0 1 +# ---------------+-----------+---+-------+---+-------+----------- +# ASR8di-SAME: 1 1 1 0 0 0 1 0 . 0 0 0 0 0 0 0 1 +# ASR32di-SAME: 1 1 1 0 0 0 1 0 . 1 0 0 0 0 0 0 1 +# ---------------+-----------+---+-------+---+-------+----------- +# ROL8di-SAME: 1 1 1 0 0 0 1 1 . 0 0 0 1 1 0 0 1 +# ROL32di-SAME: 1 1 1 0 0 0 1 1 . 1 0 0 1 1 0 0 1 +# ---------------+-----------+---+-------+---+-------+----------- +# ROR8di-SAME: 1 1 1 0 0 0 1 0 . 0 0 0 1 1 0 0 1 +# ROR32di-SAME: 1 1 1 0 0 0 1 0 . 1 0 0 1 1 0 0 1 +# ROR32di-SAME: 1 1 1 0 0 1 0 0 . 1 0 0 1 1 0 0 1 +# ROR32di-SAME: 1 1 1 0 0 1 1 0 . 1 0 0 1 1 0 0 1 +# ROR32di-SAME: 1 1 1 0 1 0 0 0 . 1 0 0 1 1 0 0 1 +# ROR32di-SAME: 1 1 1 0 1 0 1 0 . 1 0 0 1 1 0 0 1 +# ROR32di-SAME: 1 1 1 0 1 1 0 0 . 1 0 0 1 1 0 0 1 +# ROR32di-SAME: 1 1 1 0 1 1 1 0 . 1 0 0 1 1 0 0 1 +# ROR32di-SAME: 1 1 1 0 0 0 0 0 . 1 0 0 1 1 0 0 1 +name: MxSR_DI +body: | + bb.0: + $bd1 = SHL8di $bd1, 1, implicit-def $ccr + $d1 = SHL32di $d1, 1, implicit-def $ccr + $bd1 = LSR8di $bd1, 1, implicit-def $ccr + $d1 = LSR32di $d1, 1, implicit-def $ccr + $bd1 = ASR8di $bd1, 1, implicit-def $ccr + $d1 = ASR32di $d1, 1, implicit-def $ccr + $bd1 = ROL8di $bd1, 1, implicit-def $ccr + $d1 = ROL32di $d1, 1, implicit-def $ccr + $bd1 = ROR8di $bd1, 1, implicit-def $ccr + $d1 = ROR32di $d1, 1, implicit-def $ccr + $d1 = ROR32di $d1, 2, implicit-def $ccr + $d1 = ROR32di $d1, 3, implicit-def $ccr + $d1 = ROR32di $d1, 4, implicit-def $ccr + $d1 = ROR32di $d1, 5, implicit-def $ccr + $d1 = ROR32di $d1, 6, implicit-def $ccr + $d1 = ROR32di $d1, 7, implicit-def $ccr + $d1 = ROR32di $d1, 8, implicit-def $ccr Index: test/CodeGen/M680x0/PAS/CollapseMOVEM.mir =================================================================== --- /dev/null +++ test/CodeGen/M680x0/PAS/CollapseMOVEM.mir @@ -0,0 +1,162 @@ +# RUN: llc %s -mtriple=m680x0 -start-after=prologepilog -O0 -filetype=asm -o - \ +# RUN: | FileCheck %s -check-prefixes=x00 + +#------------------------------------------------------------------------------ +# CollapseMOVEM pass finds sequences of MOVEM instructions and collapse them +# into a single instruciton with merged masks. This only works with stack data +#------------------------------------------------------------------------------ + +--- # CollapseMOVEM_RM +# +# x00-LABEL: CollapseMOVEM_RM +# x00: movem.l (0,%sp), %d0-%d2,%d7,%a1-%a3,%a5 +name: CollapseMOVEM_RM +body: | + bb.0: + MOVM32mp 1, 0, $sp + MOVM32mp 2, 4, $sp + MOVM32mp 4, 8, $sp + MOVM32mp 128, 12, $sp + MOVM32mp 512, 16, $sp + MOVM32mp 1024, 20, $sp + MOVM32mp 2048, 24, $sp + MOVM32mp 8192, 28, $sp + +... +# +# x00-LABEL: CollapseMOVEM_RM_Reversed +# x00: movem.l (0,%sp), %d0-%d2,%d7,%a1-%a3,%a5 +name: CollapseMOVEM_RM_Reversed +body: | + bb.0: + MOVM32mp 8192, 28, $sp + MOVM32mp 2048, 24, $sp + MOVM32mp 1024, 20, $sp + MOVM32mp 512, 16, $sp + MOVM32mp 128, 12, $sp + MOVM32mp 4, 8, $sp + MOVM32mp 2, 4, $sp + MOVM32mp 1, 0, $sp + +... +# This async reg/mem order is impossible to store with MOVEM +# x00-LABEL: CollapseMOVEM_RM_ReversedStoreOrder +# x00: movem.l (0,%sp), %a5 +# x00: movem.l (4,%sp), %a3 +# x00: movem.l (8,%sp), %a2 +# x00: movem.l (12,%sp), %a1 +# x00: movem.l (16,%sp), %d7 +# x00: movem.l (20,%sp), %d2 +# x00: movem.l (24,%sp), %d1 +# x00: movem.l (28,%sp), %d0 +name: CollapseMOVEM_RM_ReversedStoreOrder +body: | + bb.0: + MOVM32mp 8192, 0, $sp + MOVM32mp 2048, 4, $sp + MOVM32mp 1024, 8, $sp + MOVM32mp 512, 12, $sp + MOVM32mp 128, 16, $sp + MOVM32mp 4, 20, $sp + MOVM32mp 2, 24, $sp + MOVM32mp 1, 28, $sp + +... +--- # CollapseMOVEM_MR +# +# x00-LABEL: CollapseMOVEM_MR +# x00: movem.l %d0-%d2,%d7,%a1-%a3,%a5, (0,%sp) +name: CollapseMOVEM_MR +body: | + bb.0: + MOVM32pm 0, $sp, 1 + MOVM32pm 4, $sp, 2 + MOVM32pm 8, $sp, 4 + MOVM32pm 12, $sp, 128 + MOVM32pm 16, $sp, 512 + MOVM32pm 20, $sp, 1024 + MOVM32pm 24, $sp, 2048 + MOVM32pm 28, $sp, 8192 + +... +--- # CollapseMOVEM_Mixed +# +# x00-LABEL: CollapseMOVEM_Mixed +# x00: movem.l %d0-%d1, (0,%sp) +# x00: movem.l (8,%sp), %d2,%d7 +# x00: movem.l %a1-%a2, (16,%sp) +# x00: movem.l (24,%sp), %a3 +# x00: movem.l %a5, (28,%sp) +name: CollapseMOVEM_Mixed +body: | + bb.0: + MOVM32pm 0, $sp, 1 + MOVM32pm 4, $sp, 2 + MOVM32mp 4, 8, $sp + MOVM32mp 128, 12, $sp + MOVM32pm 16, $sp, 512 + MOVM32pm 20, $sp, 1024 + MOVM32mp 2048, 24, $sp + MOVM32pm 28, $sp, 8192 + +... +--- # CollapseMOVEM_Zero +# +# x00-LABEL: CollapseMOVEM_Zero +# x00: movem.l %d0-%d4, (-8,%sp) +name: CollapseMOVEM_Zero +body: | + bb.0: + MOVM32pm -8, $sp, 1 + MOVM32pm -4, $sp, 2 + MOVM32pm 0, $sp, 4 + MOVM32pm 4, $sp, 8 + MOVM32pm 8, $sp, 16 + +... +# +# x00-LABEL: CollapseMOVEM_Zero_Mixed +# x00: movem.l %d3, (4,%sp) +# x00: movem.l %d0-%d2, (-8,%sp) +# x00: movem.l %d4, (8,%sp) +name: CollapseMOVEM_Zero_Mixed +body: | + bb.0: + MOVM32pm 4, $sp, 8 + MOVM32pm -4, $sp, 2 + MOVM32pm -8, $sp, 1 + MOVM32pm 0, $sp, 4 + MOVM32pm 8, $sp, 16 + +... +--- # CollapseMOVEM_Zero_Reversed +# +# x00-LABEL: CollapseMOVEM_Zero_Reversed +# x00: movem.l %d0-%d4, (-8,%sp) +name: CollapseMOVEM_Zero_Reversed +body: | + bb.0: + MOVM32pm 8, $sp, 16 + MOVM32pm 4, $sp, 8 + MOVM32pm 0, $sp, 4 + MOVM32pm -4, $sp, 2 + MOVM32pm -8, $sp, 1 + +... +# +# x00-LABEL: CollapseMOVEM_Zero_ReversedStoreOrder +# x00: movem.l %d0, (8,%sp) +# x00: movem.l %d1, (4,%sp) +# x00: movem.l %d2, (0,%sp) +# x00: movem.l %d3, (-4,%sp) +# x00: movem.l %d4, (-8,%sp) +name: CollapseMOVEM_Zero_ReversedStoreOrder +body: | + bb.0: + MOVM32pm 8, $sp, 1 + MOVM32pm 4, $sp, 2 + MOVM32pm 0, $sp, 4 + MOVM32pm -4, $sp, 8 + MOVM32pm -8, $sp, 16 + +... Index: test/CodeGen/M680x0/lit.local.cfg =================================================================== --- /dev/null +++ test/CodeGen/M680x0/lit.local.cfg @@ -0,0 +1,13 @@ +import os + +config.suffixes = ['.ll', '.mir', '.test', '.txt'] + +config.environment['PATH'] = os.pathsep.join( + [os.path.join(config.llvm_src_root, 'utils')] + + [os.environ.get('PATH','')]) + +print(config.environment.get('PATH')) + +if not 'M680x0' in config.root.targets: + config.unsupported = True + Index: utils/TableGen/CMakeLists.txt =================================================================== --- utils/TableGen/CMakeLists.txt +++ utils/TableGen/CMakeLists.txt @@ -7,6 +7,7 @@ Attributes.cpp CallingConvEmitter.cpp CodeEmitterGen.cpp + CodeBeadsGen.cpp CodeGenDAGPatterns.cpp CodeGenHwModes.cpp CodeGenInstruction.cpp Index: utils/TableGen/CodeBeadsGen.cpp =================================================================== --- /dev/null +++ utils/TableGen/CodeBeadsGen.cpp @@ -0,0 +1,132 @@ +//===-- CodeBeadsGen.cpp - Code Beads Generator -*- 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 tablegen implementation of "code beads", binary strings +/// defined for an instruction. They are used to pass very target specific +/// binary information directly from a tablegen file, e.g. instruction +/// encoding. +/// +//===----------------------------------------------------------------------===// + +#include "CodeGenTarget.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Debug.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" +#include "llvm/TableGen/TableGenBackend.h" + +#include +#include +#include + +using namespace llvm; + +namespace { + +class CodeBeadsGen { + RecordKeeper &Records; + +public: + CodeBeadsGen(RecordKeeper &R) : Records(R) {} + void run(raw_ostream &o); +}; + +void CodeBeadsGen::run(raw_ostream &o) { + CodeGenTarget Target(Records); + std::vector Insts = Records.getAllDerivedDefinitions("Instruction"); + + // For little-endian instruction bit encodings, reverse the bit order + Target.reverseBitsForLittleEndianEncoding(); + + ArrayRef NumberedInstructions = + Target.getInstructionsByEnumValue(); + + // Emit function declaration + o << "const uint8_t * " << Target.getName(); + o << "MCCodeEmitter::getGenInstrBeads(const MCInst &MI) const {\n"; + + unsigned Length = 192; + unsigned Size = 8; + unsigned Parts = Length / Size; + + // Emit instruction base values + // TODO Make it auto-detect size + o << " static const uint" << Size << "_t InstBits[][" << Parts << "] = {\n"; + for (const CodeGenInstruction *CGI : NumberedInstructions) { + Record *R = CGI->TheDef; + + if (R->getValueAsString("Namespace") == "TargetOpcode" || + R->getValueAsBit("isPseudo")) { + o << "\t{ 0x0 },"; + o << '\t' << "// (Pseudo) " << R->getName() << "\n"; + continue; + } + + BitsInit *BI = R->getValueAsBitsInit("Beads"); + + if (!BI->isComplete()) { + PrintFatalError(R->getLoc(), "Record `" + R->getName() + + "', bit field 'Beads' is not complete"); + } + + if (BI->getNumBits() > Length) { + PrintFatalError(R->getLoc(), + "Record `" + R->getName() + + "', bit field 'Beads' is too long(maximum: " + + std::to_string(Length) + ")"); + } + + // Convert to byte array: + // [dcba] -> [a][b][c][d] + o << "\t{"; + for (unsigned p = 0; p < Parts; ++p) { + // unsigned Num = BI->getNumBits(); + unsigned Right = Size * p; + unsigned Left = Right + Size; + + uint64_t Value = 0; + for (unsigned i = Right; i != Left; ++i) { + unsigned Shift = i % Size; + if (BitInit *B = dyn_cast(BI->getBit(i))) { + Value |= (uint64_t)B->getValue() << (Shift); + } else { + PrintFatalError(R->getLoc(), + "Record `" + R->getName() + "', bit 'Beads[" + + std::to_string(i) + "]' is not defined"); + } + } + + if (p) + o << ','; + o << " 0x"; + o.write_hex(Value); + o << ""; + } + o << " }," << '\t' << "// " << R->getName() << "\n"; + } + o << "\t{ 0x0 }\n };\n"; + + // Emit initial function code + o << " const unsigned opcode = MI.getOpcode();\n" + << " return (uint8_t *)InstBits[opcode];\n" + << "}\n\n"; +} + +} // End anonymous namespace + +namespace llvm { + +void EmitCodeBeads(RecordKeeper &RK, raw_ostream &OS) { + emitSourceFileHeader("Machine Code Beads", OS); + CodeBeadsGen(RK).run(OS); +} + +} // namespace llvm Index: utils/TableGen/InstrInfoEmitter.cpp =================================================================== --- utils/TableGen/InstrInfoEmitter.cpp +++ utils/TableGen/InstrInfoEmitter.cpp @@ -43,8 +43,8 @@ const CodeGenSchedModels &SchedModels; public: - InstrInfoEmitter(RecordKeeper &R): - Records(R), CDP(R), SchedModels(CDP.getTargetInfo().getSchedModels()) {} + InstrInfoEmitter(RecordKeeper &R) + : Records(R), CDP(R), SchedModels(CDP.getTargetInfo().getSchedModels()) {} // run - Output the instruction set description. void run(raw_ostream &OS); @@ -57,8 +57,8 @@ /// The keys of this map are maps which have OpName enum values as their keys /// and instruction operand indices as their values. The values of this map /// are lists of instruction names. - typedef std::map, - std::vector> OpNameMapTy; + typedef std::map, std::vector> + OpNameMapTy; typedef std::map::iterator StrUintMapIter; /// Generate member functions in the target-specific GenInstrInfo class. @@ -73,27 +73,32 @@ void emitMCIIHelperMethods(raw_ostream &OS); void emitRecord(const CodeGenInstruction &Inst, unsigned Num, Record *InstrInfo, - std::map, unsigned> &EL, - const OperandInfoMapTy &OpInfo, - raw_ostream &OS); + std::map, unsigned> &EL, + const OperandInfoMapTy &MIOpInfo, + const OperandInfoMapTy &MCOpInfo, raw_ostream &OS); void emitOperandTypesEnum(raw_ostream &OS, const CodeGenTarget &Target); - void initOperandMapData( - ArrayRef NumberedInstructions, - StringRef Namespace, - std::map &Operands, - OpNameMapTy &OperandMap); - void emitOperandNameMappings(raw_ostream &OS, const CodeGenTarget &Target, - ArrayRef NumberedInstructions); + void + initOperandMapData(ArrayRef NumberedInstructions, + StringRef Namespace, + std::map &Operands, + OpNameMapTy &OperandMap); + void emitOperandNameMappings( + raw_ostream &OS, const CodeGenTarget &Target, + ArrayRef NumberedInstructions); // Operand information. - void EmitOperandInfo(raw_ostream &OS, OperandInfoMapTy &OperandInfoIDs); - std::vector GetOperandInfo(const CodeGenInstruction &Inst); + void EmitMIOperandInfo(raw_ostream &OS, OperandInfoMapTy &MCOperandInfoIDs); + std::vector GetMIOperandInfo(const CodeGenTarget &Target, + const CodeGenInstruction &Inst); + + void EmitMCOperandInfo(raw_ostream &OS, OperandInfoMapTy &MCOperandInfoIDs); + std::vector GetMCOperandInfo(const CodeGenInstruction &Inst); }; } // end anonymous namespace -static void PrintDefList(const std::vector &Uses, - unsigned Num, raw_ostream &OS) { +static void PrintDefList(const std::vector &Uses, unsigned Num, + raw_ostream &OS) { OS << "static const MCPhysReg ImplicitList" << Num << "[] = { "; for (Record *U : Uses) OS << getQualifiedName(U) << ", "; @@ -105,7 +110,62 @@ //===----------------------------------------------------------------------===// std::vector -InstrInfoEmitter::GetOperandInfo(const CodeGenInstruction &Inst) { +InstrInfoEmitter::GetMIOperandInfo(const CodeGenTarget &Target, + const CodeGenInstruction &Inst) { + + std::vector Result; + + const std::string &Namespace = Target.getInstNamespace(); + + for (auto &Op : Inst.Operands) { + + // One record + std::string Res; + + // This might be a multiple operand thing. Targets like X86 have + // registers in their multi-operand operands. It may also be an anonymous + // operand, which has a single operand, but no declared class for the + // operand. + DagInit *MIOI = Op.MIOperandInfo; + + if (!MIOI || MIOI->getNumArgs() == 0) { + Res = std::to_string(Op.MIOperandNo) + ", " + Op.OperandType + ", 1"; + } else { + Res = std::to_string(Op.MIOperandNo) + ", " + Namespace + + "::MIOpTypes::" + Op.Rec->getName().str() + ", " + + std::to_string(Op.MINumOperands); + } + + Result.push_back(Res); + } + + return Result; +} + +void InstrInfoEmitter::EmitMIOperandInfo(raw_ostream &OS, + OperandInfoMapTy &MIOperandInfoIDs) { + // ID #0 is for no operand info. + unsigned OperandListNum = 0; + MIOperandInfoIDs[std::vector()] = ++OperandListNum; + + OS << "\n"; + const CodeGenTarget &Target = CDP.getTargetInfo(); + for (const CodeGenInstruction *Inst : Target.getInstructionsByEnumValue()) { + std::vector OperandInfo = GetMIOperandInfo(Target, *Inst); + unsigned &N = MIOperandInfoIDs[OperandInfo]; + if (N != 0) + continue; + + N = ++OperandListNum; + OS << "static const MIOperandInfo MIOperandInfo" << N << "[] = { "; + for (const std::string &Info : OperandInfo) + OS << "{ " << Info << " }, "; + OS << "};\n"; + } +} + +std::vector +InstrInfoEmitter::GetMCOperandInfo(const CodeGenInstruction &Inst) { std::vector Result; for (auto &Op : Inst.Operands) { @@ -170,8 +230,7 @@ // Fill in constraint info. Res += ", "; - const CGIOperandList::ConstraintInfo &Constraint = - Op.Constraints[j]; + const CGIOperandList::ConstraintInfo &Constraint = Op.Constraints[j]; if (Constraint.isNone()) Res += "0"; else if (Constraint.isEarlyClobber()) @@ -179,7 +238,7 @@ else { assert(Constraint.isTied()); Res += "((" + utostr(Constraint.getTiedOperand()) + - " << 16) | (1 << MCOI::TIED_TO))"; + " << 16) | (1 << MCOI::TIED_TO))"; } Result.push_back(Res); @@ -189,21 +248,22 @@ return Result; } -void InstrInfoEmitter::EmitOperandInfo(raw_ostream &OS, - OperandInfoMapTy &OperandInfoIDs) { +void InstrInfoEmitter::EmitMCOperandInfo(raw_ostream &OS, + OperandInfoMapTy &MCOperandInfoIDs) { // ID #0 is for no operand info. unsigned OperandListNum = 0; - OperandInfoIDs[std::vector()] = ++OperandListNum; + MCOperandInfoIDs[std::vector()] = ++OperandListNum; OS << "\n"; const CodeGenTarget &Target = CDP.getTargetInfo(); for (const CodeGenInstruction *Inst : Target.getInstructionsByEnumValue()) { - std::vector OperandInfo = GetOperandInfo(*Inst); - unsigned &N = OperandInfoIDs[OperandInfo]; - if (N != 0) continue; + std::vector OperandInfo = GetMCOperandInfo(*Inst); + unsigned &N = MCOperandInfoIDs[OperandInfo]; + if (N != 0) + continue; N = ++OperandListNum; - OS << "static const MCOperandInfo OperandInfo" << N << "[] = { "; + OS << "static const MCOperandInfo MCOperandInfo" << N << "[] = { "; for (const std::string &Info : OperandInfo) OS << "{ " << Info << " }, "; OS << "};\n"; @@ -211,17 +271,16 @@ } /// Initialize data structures for generating operand name mappings. -/// +/// /// \param Operands [out] A map used to generate the OpName enum with operand /// names as its keys and operand enum values as its values. /// \param OperandMap [out] A map for representing the operand name mappings for /// each instructions. This is used to generate the OperandMap table as /// well as the getNamedOperandIdx() function. void InstrInfoEmitter::initOperandMapData( - ArrayRef NumberedInstructions, - StringRef Namespace, - std::map &Operands, - OpNameMapTy &OperandMap) { + ArrayRef NumberedInstructions, + StringRef Namespace, std::map &Operands, + OpNameMapTy &OperandMap) { unsigned NumOperands = 0; for (const CodeGenInstruction *Inst : NumberedInstructions) { if (!Inst->TheDef->getValueAsBit("UseNamedOperandTable")) @@ -231,13 +290,13 @@ StrUintMapIter I = Operands.find(Info.Name); if (I == Operands.end()) { - I = Operands.insert(Operands.begin(), - std::pair(Info.Name, NumOperands++)); + I = Operands.insert(Operands.begin(), std::pair( + Info.Name, NumOperands++)); } OpList[I->second] = Info.MIOperandNo; } - OperandMap[OpList].push_back(Namespace.str() + "::" + - Inst->TheDef->getName().str()); + OperandMap[OpList].push_back(Namespace.str() + + "::" + Inst->TheDef->getName().str()); } } @@ -252,9 +311,9 @@ /// - A function called getNamedOperandIdx(uint16_t Opcode, uint16_t NamedIdx) /// for looking up the operand index for an instruction, given a value from /// OpName enum -void InstrInfoEmitter::emitOperandNameMappings(raw_ostream &OS, - const CodeGenTarget &Target, - ArrayRef NumberedInstructions) { +void InstrInfoEmitter::emitOperandNameMappings( + raw_ostream &OS, const CodeGenTarget &Target, + ArrayRef NumberedInstructions) { StringRef Namespace = Target.getInstNamespace(); std::string OpNameNS = "OpName"; // Map of operand names to their enumeration value. This will be used to @@ -330,25 +389,24 @@ StringRef Namespace = Target.getInstNamespace(); std::vector Operands = Records.getAllDerivedDefinitions("Operand"); - OS << "#ifdef GET_INSTRINFO_OPERAND_TYPES_ENUM\n"; - OS << "#undef GET_INSTRINFO_OPERAND_TYPES_ENUM\n"; - OS << "namespace llvm {\n"; OS << "namespace " << Namespace << " {\n"; - OS << "namespace OpTypes {\n"; - OS << "enum OperandType {\n"; + OS << "namespace MIOpTypes {\n"; + OS << "enum MIOperandType {\n"; - unsigned EnumVal = 0; + bool First = true; for (const Record *Op : Operands) { - if (!Op->isAnonymous()) - OS << " " << Op->getName() << " = " << EnumVal << ",\n"; - ++EnumVal; + OS << " " << Op->getName(); + if (First) { + OS << " = llvm::MCOI::OPERAND_FIRST_TARGET"; + First = false; + } + OS << ",\n"; } - OS << " OPERAND_TYPE_LIST_END" << "\n};\n"; - OS << "} // end namespace OpTypes\n"; - OS << "} // end namespace " << Namespace << "\n"; - OS << "} // end namespace llvm\n"; - OS << "#endif // GET_INSTRINFO_OPERAND_TYPES_ENUM\n\n"; + OS << " OPERAND_TYPE_LIST_END" + << "\n};\n"; + OS << "} // end namespace MIOpTypes\n"; + OS << "} // end namespace " << Namespace << "\n\n"; } void InstrInfoEmitter::emitMCIIHelperMethods(raw_ostream &OS) { @@ -428,44 +486,58 @@ emitSourceFileHeader("Target Instruction Enum Values and Descriptors", OS); emitEnums(OS); + // Get the MI types separately from data + OS << "#ifdef GET_INSTRINFO_MI_OPS_INFO\n"; + OS << "#undef GET_INSTRINFO_MI_OPS_INFO\n"; + OS << "namespace llvm {\n\n"; + emitOperandTypesEnum(OS, CDP.getTargetInfo()); + OS << "} // end llvm namespace\n"; + OS << "#endif // GET_INSTRINFO_MI_OPS_INFO\n\n"; + OS << "#ifdef GET_INSTRINFO_MC_DESC\n"; OS << "#undef GET_INSTRINFO_MC_DESC\n"; OS << "namespace llvm {\n\n"; + emitOperandTypesEnum(OS, CDP.getTargetInfo()); + CodeGenTarget &Target = CDP.getTargetInfo(); const std::string &TargetName = Target.getName(); Record *InstrInfo = Target.getInstructionSet(); // Keep track of all of the def lists we have emitted already. - std::map, unsigned> EmittedLists; + std::map, unsigned> EmittedLists; unsigned ListNumber = 0; // Emit all of the instruction's implicit uses and defs. for (const CodeGenInstruction *II : Target.getInstructionsByEnumValue()) { Record *Inst = II->TheDef; - std::vector Uses = Inst->getValueAsListOfDefs("Uses"); + std::vector Uses = Inst->getValueAsListOfDefs("Uses"); if (!Uses.empty()) { unsigned &IL = EmittedLists[Uses]; - if (!IL) PrintDefList(Uses, IL = ++ListNumber, OS); + if (!IL) + PrintDefList(Uses, IL = ++ListNumber, OS); } - std::vector Defs = Inst->getValueAsListOfDefs("Defs"); + std::vector Defs = Inst->getValueAsListOfDefs("Defs"); if (!Defs.empty()) { unsigned &IL = EmittedLists[Defs]; - if (!IL) PrintDefList(Defs, IL = ++ListNumber, OS); + if (!IL) + PrintDefList(Defs, IL = ++ListNumber, OS); } } - OperandInfoMapTy OperandInfoIDs; + OperandInfoMapTy MIOperandInfoIDs; + OperandInfoMapTy MCOperandInfoIDs; // Emit all of the operand info records. - EmitOperandInfo(OS, OperandInfoIDs); + EmitMIOperandInfo(OS, MIOperandInfoIDs); + EmitMCOperandInfo(OS, MCOperandInfoIDs); // Emit all of the MCInstrDesc records in their ENUM ordering. // OS << "\nextern const MCInstrDesc " << TargetName << "Insts[] = {\n"; - ArrayRef NumberedInstructions = - Target.getInstructionsByEnumValue(); + ArrayRef NumberedInstructions = + Target.getInstructionsByEnumValue(); SequenceToOffsetTable InstrNames; unsigned Num = 0; @@ -473,7 +545,8 @@ // Keep a list of the instruction names. InstrNames.add(Inst->TheDef->getName()); // Emit the record into the table. - emitRecord(*Inst, Num++, InstrInfo, EmittedLists, OperandInfoIDs, OS); + emitRecord(*Inst, Num++, InstrInfo, EmittedLists, MIOperandInfoIDs, + MCOperandInfoIDs, OS); } OS << "};\n\n"; @@ -483,7 +556,7 @@ InstrNames.emit(OS, printChar); OS << "};\n\n"; - OS << "extern const unsigned " << TargetName <<"InstrNameIndices[] = {"; + OS << "extern const unsigned " << TargetName << "InstrNameIndices[] = {"; Num = 0; for (const CodeGenInstruction *Inst : NumberedInstructions) { // Newline every eight entries. @@ -498,8 +571,8 @@ // MCInstrInfo initialization routine. OS << "static inline void Init" << TargetName << "MCInstrInfo(MCInstrInfo *II) {\n"; - OS << " II->InitMCInstrInfo(" << TargetName << "Insts, " - << TargetName << "InstrNameIndices, " << TargetName << "InstrNameData, " + OS << " II->InitMCInstrInfo(" << TargetName << "Insts, " << TargetName + << "InstrNameIndices, " << TargetName << "InstrNameData, " << NumberedInstructions.size() << ");\n}\n\n"; OS << "} // end llvm namespace\n"; @@ -514,7 +587,8 @@ OS << "namespace llvm {\n"; OS << "struct " << ClassName << " : public TargetInstrInfo {\n" << " explicit " << ClassName - << "(int CFSetupOpcode = -1, int CFDestroyOpcode = -1, int CatchRetOpcode = -1, int ReturnOpcode = -1);\n" + << "(int CFSetupOpcode = -1, int CFDestroyOpcode = -1, int CatchRetOpcode " + "= -1, int ReturnOpcode = -1);\n" << " ~" << ClassName << "() override = default;\n"; emitTIIHelperMethods(OS); @@ -531,8 +605,10 @@ OS << "extern const unsigned " << TargetName << "InstrNameIndices[];\n"; OS << "extern const char " << TargetName << "InstrNameData[];\n"; OS << ClassName << "::" << ClassName - << "(int CFSetupOpcode, int CFDestroyOpcode, int CatchRetOpcode, int ReturnOpcode)\n" - << " : TargetInstrInfo(CFSetupOpcode, CFDestroyOpcode, CatchRetOpcode, ReturnOpcode) {\n" + << "(int CFSetupOpcode, int CFDestroyOpcode, int CatchRetOpcode, int " + "ReturnOpcode)\n" + << " : TargetInstrInfo(CFSetupOpcode, CFDestroyOpcode, CatchRetOpcode, " + "ReturnOpcode) {\n" << " InitMCInstrInfo(" << TargetName << "Insts, " << TargetName << "InstrNameIndices, " << TargetName << "InstrNameData, " << NumberedInstructions.size() << ");\n}\n"; @@ -542,64 +618,68 @@ emitOperandNameMappings(OS, Target, NumberedInstructions); - emitOperandTypesEnum(OS, Target); + // emitOperandTypesEnum(OS, Target); emitMCIIHelperMethods(OS); } -void InstrInfoEmitter::emitRecord(const CodeGenInstruction &Inst, unsigned Num, - Record *InstrInfo, - std::map, unsigned> &EmittedLists, - const OperandInfoMapTy &OpInfo, - raw_ostream &OS) { +void InstrInfoEmitter::emitRecord( + const CodeGenInstruction &Inst, unsigned Num, Record *InstrInfo, + std::map, unsigned> &EmittedLists, + const OperandInfoMapTy &MIOpInfo, const OperandInfoMapTy &MCOpInfo, + raw_ostream &OS) { + int MIOps = 0; int MinOperands = 0; - if (!Inst.Operands.empty()) + if (!Inst.Operands.empty()) { + // Number of logical operands + MIOps = Inst.Operands.back().MIOperandNo + 1; + // Each logical operand can be multiple MI operands. - MinOperands = Inst.Operands.back().MIOperandNo + - Inst.Operands.back().MINumOperands; + MinOperands = + Inst.Operands.back().MIOperandNo + Inst.Operands.back().MINumOperands; + } OS << " { "; - OS << Num << ",\t" << MinOperands << ",\t" - << Inst.Operands.NumDefs << ",\t" - << Inst.TheDef->getValueAsInt("Size") << ",\t" - << SchedModels.getSchedClassIdx(Inst) << ",\t0"; + OS << Num << ",\t" << MIOps << ",\t" << MinOperands << ",\t" + << Inst.Operands.NumDefs << ",\t" << Inst.TheDef->getValueAsInt("Size") + << ",\t" << SchedModels.getSchedClassIdx(Inst) << ",\t0"; CodeGenTarget &Target = CDP.getTargetInfo(); // Emit all of the target independent flags... - if (Inst.isPseudo) OS << "|(1ULL< UseList = Inst.TheDef->getValueAsListOfDefs("Uses"); + std::vector UseList = Inst.TheDef->getValueAsListOfDefs("Uses"); if (UseList.empty()) OS << "nullptr, "; else OS << "ImplicitList" << EmittedLists[UseList] << ", "; - std::vector DefList = Inst.TheDef->getValueAsListOfDefs("Defs"); + std::vector DefList = Inst.TheDef->getValueAsListOfDefs("Defs"); if (DefList.empty()) OS << "nullptr, "; else OS << "ImplicitList" << EmittedLists[DefList] << ", "; // Emit the operand info. - std::vector OperandInfo = GetOperandInfo(Inst); - if (OperandInfo.empty()) + std::vector MIOperandInfo = + GetMIOperandInfo(CDP.getTargetInfo(), Inst); + if (MIOperandInfo.empty()) + OS << "nullptr"; + else + OS << "MIOperandInfo" << MIOpInfo.find(MIOperandInfo)->second; + OS << ", "; + std::vector MCOperandInfo = GetMCOperandInfo(Inst); + if (MCOperandInfo.empty()) OS << "nullptr"; else - OS << "OperandInfo" << OpInfo.find(OperandInfo)->second; + OS << "MCOperandInfo" << MCOpInfo.find(MCOperandInfo)->second; if (Inst.HasComplexDeprecationPredicate) // Emit a function pointer to the complex predicate method. @@ -706,4 +793,4 @@ EmitMapTable(RK, OS); } -} // end llvm namespace +} // namespace llvm Index: utils/TableGen/TableGen.cpp =================================================================== --- utils/TableGen/TableGen.cpp +++ utils/TableGen/TableGen.cpp @@ -26,6 +26,7 @@ PrintRecords, DumpJSON, GenEmitter, + GenCodeBeads, GenRegisterInfo, GenInstrInfo, GenInstrDocs, @@ -64,6 +65,8 @@ "Dump all records as machine-readable JSON"), clEnumValN(GenEmitter, "gen-emitter", "Generate machine code emitter"), + clEnumValN(GenCodeBeads, "gen-code-beads", + "Generate machine code beads"), clEnumValN(GenRegisterInfo, "gen-register-info", "Generate registers and register classes info"), clEnumValN(GenInstrInfo, "gen-instr-info", @@ -135,6 +138,9 @@ case GenEmitter: EmitCodeEmitter(Records, OS); break; + case GenCodeBeads: + EmitCodeBeads(Records, OS); + break; case GenRegisterInfo: EmitRegisterInfo(Records, OS); break; Index: utils/TableGen/TableGenBackends.h =================================================================== --- utils/TableGen/TableGenBackends.h +++ utils/TableGen/TableGenBackends.h @@ -70,6 +70,7 @@ void EmitAsmWriter(RecordKeeper &RK, raw_ostream &OS); void EmitCallingConv(RecordKeeper &RK, raw_ostream &OS); void EmitCodeEmitter(RecordKeeper &RK, raw_ostream &OS); +void EmitCodeBeads(RecordKeeper &RK, raw_ostream &OS); void EmitDAGISel(RecordKeeper &RK, raw_ostream &OS); void EmitDFAPacketizer(RecordKeeper &RK, raw_ostream &OS); void EmitDisassembler(RecordKeeper &RK, raw_ostream &OS); Index: utils/extract-section =================================================================== --- /dev/null +++ utils/extract-section @@ -0,0 +1,38 @@ +#!/bin/bash + +#------------------------------------------------------------------------------- +# Extract Section +# +# Read object file from stdin, extract named section and print its bit-pattern +#------------------------------------------------------------------------------- + +SECTION="\s$1"; shift; +BASE=${1:-'-b'}; + +DIR=/tmp/extract-section +TEMP=$DIR/$(uuidgen).o + +[ -d $DIR ] || mkdir $DIR + +cat > $TEMP + +if [[ $BASE == '-b' ]]; then +llvm-readobj -elf-output-style=GNU --sections $TEMP \ + | grep $SECTION \ + | awk '{print "dd if='$TEMP' bs=1 count=$[0x" $7 "] skip=$[0x" $6 "]"}' \ + | bash 2> /dev/null \ + | xxd -bits -groupsize 0 \ + | cut -d " " -f 2 \ + | tr -d "\n" \ + | fold -w8 | paste -sd'.' - \ + | fold -w1 | paste -sd' ' - +elif [[ $BASE == '-h' ]]; then +llvm-readobj -elf-output-style=GNU --sections $TEMP \ + | grep $SECTION \ + | awk '{print "dd if='$TEMP' bs=1 count=$[0x" $7 "] skip=$[0x" $6 "]"}' \ + | bash 2> /dev/null \ + | xxd -p \ + | fold -w4 | paste -sd' ' - +fi + +rm $TEMP